<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://alnwlsn.com/projectrepository/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Alnwlsn</id>
	<title>Alnwlsn - Projects Repository - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://alnwlsn.com/projectrepository/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Alnwlsn"/>
	<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=Special:Contributions/Alnwlsn"/>
	<updated>2026-04-05T16:38:46Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.38.2</generator>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=Geiger_counter&amp;diff=609</id>
		<title>Geiger counter</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=Geiger_counter&amp;diff=609"/>
		<updated>2025-07-20T01:36:18Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:Geiger-counter-overview.jpg|thumb|Completed unit in 2018.]]&lt;br /&gt;
&lt;br /&gt;
[[File:Geiger-schematic.png|thumb|Schematic]]&lt;br /&gt;
&lt;br /&gt;
A Geiger counter has been on my list of projects for a while, and in Spring 2018 I made one based on the very available Russian SBM-20 Geiger-Muller tubes, one of the most common cheap tubes on Ebay.&lt;br /&gt;
&lt;br /&gt;
The heart of the Geiger counter is the GM tube, which is a tube filled with gas, and two electrodes. A high voltage is applied to the tube, usually around 400 volts. When a particle of ionizing radiation passes through the tube, it will ionize a molecule of the gas, and allow a tiny bit of current to flow through the tube. If we can detect the tiny current that flows through the tube, we can count them and find out the rate of radiation exposure in the area.&lt;br /&gt;
&lt;br /&gt;
==Hardware==&lt;br /&gt;
Since it has been a while since building, I don't have a full schematic ready. But briefly:&lt;br /&gt;
* The GM tube is a SBM-20. It can measure beta and gamma radiation. A resistor of several megaohms is placed in series to protect the tube from excessive current flow.&lt;br /&gt;
* An ATTiny84 microcontroller controls the HV power supply, counting, and user interface.&lt;br /&gt;
* The pulse detection circuit is made from a couple resistors, capacitors, and transistors, and comes from one of the common Geiger kit plans available online.&lt;br /&gt;
* The power supply is a boost converter made from an inductor being repeatedly shut off, which creates high voltage spikes. A diode rectifies the high voltage and stores it in a capacitor. A voltage divider allows the microcontroller to read the high voltage. Signal to the boost converter is supplied by the microcontroller, at about 8kHz. PWM on this 8khz signal controls the output voltage. The ATTiny provides a feedback control system, which is an I (integral) controller, which I found was quite stable. I also tried a P controller and PI controller, but I could not tune these as well.&lt;br /&gt;
* Power is supplied by an 18650 battery.&lt;br /&gt;
&lt;br /&gt;
===Case===&lt;br /&gt;
The outside of the case has all user controls, inputs and outputs.&lt;br /&gt;
* On the left side of the case, there are two switches:&lt;br /&gt;
** Top Switch - When pulled towards front face, will enable internal speaker to provide Geiger &amp;quot;clicks&amp;quot;&lt;br /&gt;
** Bottom Switch - When pulled towards face, will hold Center button down. I originally planned to use this for an extra user interface item, but ran out of IO.&lt;br /&gt;
* On the front face:&lt;br /&gt;
** 7 segment display - showing all the user interface operations&lt;br /&gt;
** Blue LED - Turns on when high counts of radiation are observed&lt;br /&gt;
** Buttons, '''C'''enter, '''L'''eft, and '''R'''ight. These are the main interface controls.&lt;br /&gt;
* On the right side:&lt;br /&gt;
** 3.5 mm jack - provides Gnd, radiation pulse output, and serial output from the microcontroller&lt;br /&gt;
** Barrel connector - Connect 3.7V - 5V here for external power. Center positive.&lt;br /&gt;
** Microusb - Connect USB to charge battery. It should not be used to run the system, probably.&lt;br /&gt;
** Power switch - Switches between battery power and external power barrel connector.&lt;br /&gt;
====2019 redesign====&lt;br /&gt;
The first case was very brittle ever since it came off the printer, and it eventually broke apart. I had made some more successful case designs for my other projects since then, and I decided to make a new case.&lt;br /&gt;
* I removed the segmented interior, so that the different components could be set into the case without the trouble of running wires through a bunch of different holes. &lt;br /&gt;
* Removed the switch that hold the center button down. Why did I need it.&lt;br /&gt;
* Added screw holes to hold the switches in place with bolts instead of hot glue.&lt;br /&gt;
* Placed all controls and ports on right side. &lt;br /&gt;
** Bottom switch is power switch&lt;br /&gt;
** Top switch is speaker on/off&lt;br /&gt;
** The USB port is now used to power the device when charging, just like on the [[Model T speedometer]]. This replaces the barrel power connector.&lt;br /&gt;
** Replaced the 1/8&amp;quot; jack with a 5 pin connector which connects to the ISP port (without VCC). This port can be used to program the ATTiny without opening the case, and the pins can be programed for other uses to get count data out.&lt;br /&gt;
&lt;br /&gt;
==Software / Operator interface==&lt;br /&gt;
The Geiger counter is controlled mainly through the three buttons on the front of the unit.&lt;br /&gt;
* The '''right''' button is the '''Mode Select''' button. Pressing this button will advance to the next mode. While pressed, it will display the number of the mode that will be active upon release. &lt;br /&gt;
* The center button is used as the main Alt/alternate button. This is used because not all information can be displayed in just 4 digits.&lt;br /&gt;
* The left button is either a second alternate button or a reset button.&lt;br /&gt;
&lt;br /&gt;
===Modes===&lt;br /&gt;
There are '''10''' modes in the current system:&lt;br /&gt;
* 0 - Counts per minute, calculated over 1 minute. &lt;br /&gt;
** '''C'''enter button - Displays seconds of the frame (0 to 59). When the number has rolled over back to where it was, one minute has passed, and a complete set of new data has been read.&lt;br /&gt;
** '''L'''eft button - Resets count.&lt;br /&gt;
* 1 - MicroSiverts per hour, calculated over 1 minute. Displays as a float, yyyy.xxxx (ie 0001.251 or 1.251 uS/h. With no buttons pressed, the display shows digits after the decimal point (xxxx)&lt;br /&gt;
** C - display high digits of number (yyyy).&lt;br /&gt;
* 2 - Counts per hour, calculated over 1 hour. Displays as integer (yyyyxxxx). Normally displays values up to 9999 (the xxxx digits)&lt;br /&gt;
** C - display high digits of number (yyyy).&lt;br /&gt;
** L - display current minute of frame (0 to 59).&lt;br /&gt;
* 3 - MicroSiverts per hour, calculated over 1 hour. Same display format as mode 1. Normal display is digits after decimal point.&lt;br /&gt;
** C - high digits of number (before decimal place).&lt;br /&gt;
* 4 - Total count. Display is an integer like mode 2.&lt;br /&gt;
** C - display high digits of integer.&lt;br /&gt;
** L - RESET this counter.&lt;br /&gt;
* 5 - Threshold to turn on blue LED. When counts/min are more than this value, blue light will turn on.&lt;br /&gt;
** C - doubles threshold value&lt;br /&gt;
** L - halves threshold value&lt;br /&gt;
* 6 - LED display brightness&lt;br /&gt;
** C - Increment value&lt;br /&gt;
** L - Decrement value&lt;br /&gt;
* 7 - Display voltage of HV power supply (in Volts)&lt;br /&gt;
** C - displays analogRead() value of HV read. (0-1023)&lt;br /&gt;
** L - displays pwm out value (0-255)&lt;br /&gt;
* 8 - Displays battery voltage (in Volts *100)&lt;br /&gt;
** C - displays analogRead() value of battery reading.&lt;br /&gt;
* 9 - Display 4 lower &amp;quot;random&amp;quot; bits  &lt;br /&gt;
** C - displays decimal representation of 8 random bits (0 to 255).&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
*[[File:Geiger-sketch.zip]] - Arduino sketches (attiny)&lt;br /&gt;
*[[File:Wilson-geiger-pcb-v1.zip]] - Eagle and gerber files for a PCB&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=File:Geiger-schematic.png&amp;diff=608</id>
		<title>File:Geiger-schematic.png</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=File:Geiger-schematic.png&amp;diff=608"/>
		<updated>2025-07-20T01:35:27Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=WC-80&amp;diff=607</id>
		<title>WC-80</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=WC-80&amp;diff=607"/>
		<updated>2023-09-28T04:33:24Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Development progress will now take place on Github as https://github.com/alnwlsn/trs-512k&lt;br /&gt;
&lt;br /&gt;
&amp;lt;insert title and stuff here&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Breadboard Z80 - 2020/07/03==&lt;br /&gt;
[[File:WC-80-v002.jpg.jpg|thumb|The breaboarded Z80 system connected to the Wilson Expansion interface and running ORCH85]]&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=File:Alnwlsn-sbg-trs80.dmk&amp;diff=606</id>
		<title>File:Alnwlsn-sbg-trs80.dmk</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=File:Alnwlsn-sbg-trs80.dmk&amp;diff=606"/>
		<updated>2023-09-20T20:29:31Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: trs-80 disk image with Soviet Bloc Game&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
trs-80 disk image with Soviet Bloc Game&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=Soviet_Bloc_Game_(C_version)&amp;diff=605</id>
		<title>Soviet Bloc Game (C version)</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=Soviet_Bloc_Game_(C_version)&amp;diff=605"/>
		<updated>2023-09-20T20:26:40Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: /* Files */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:Wilson-tetris-trs80.png|thumb|Soviet Bloc Game v1.1 running in a trs-80 emulator]]&lt;br /&gt;
While thinking about ways to make my TRS-80 model I more useable in 2019, I realized that I could not easily find a version of Tetris for it (probably because the TRS-80 came out about 10ish years before Tetris was invented), so I thought I might try to write my own after watching an episode of ''the 8-bit Guy'' where David does the same. I used to play the Gameboy version of Tetris all the time on my Ti-nSpire calculator thanks to the nspire hacking scene and an emulator port, so that's the style of Tetris I will try to recreate.&lt;br /&gt;
&lt;br /&gt;
However, I have written few games, and never done a Tetris-like clone before, so I approach this mostly as a challenge.&lt;br /&gt;
&lt;br /&gt;
Logal notice: &amp;quot;Tetris&amp;quot; is a trademark of The Tetris Company (who have been known to take down unauthorized trademark versions before: https://github.com/jdah/tetris-os), so I have decided to rename this clone &amp;quot;Soviet Bloc Game&amp;quot;&lt;br /&gt;
&lt;br /&gt;
'''Objectives:'''&lt;br /&gt;
* Playfield the same size as GB version&lt;br /&gt;
* in color?&lt;br /&gt;
* same or similar scoring system to GB version&lt;br /&gt;
* same rotation style as GB version&lt;br /&gt;
* easily portable to different platforms (that are C like)&lt;br /&gt;
* somewhat wellishly documented&lt;br /&gt;
&lt;br /&gt;
This ended up being a sort of 24 hour challenge; I started this project at about 11pm yesterday, and by about the time of this page's creation I had created an Arduino version of Soviet Bloc Game that met most of these objectives.&lt;br /&gt;
&lt;br /&gt;
==Theory of Operation==&lt;br /&gt;
===Basics===&lt;br /&gt;
The basics of the game's functioning can be summed up with just a few components: a part that generates the  shapes, a playfield that contains already played pieces and open spots where the active piece can move and collision detection that says where the active piece can and can't go.&lt;br /&gt;
&lt;br /&gt;
The playfield size is adjustable, but in the gameboy version it's 10 wide by 18 tall. In my version I added an extra 4 lines on top (which are not to be displayed) for the new pieces to appear within. Within the program, it appears as a big 2D matrix called &amp;lt;code&amp;gt;playfield[][]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
====Drawing the pieces====&lt;br /&gt;
Rather than figure out all the complex rotations of the part, I hardcoded them (using the Nintendo rotation system as described at https://tetris.fandom.com/wiki/Nintendo_Rotation_System). The parts appear in a 4x4 piece container &amp;lt;code&amp;gt;pieceC[][]&amp;lt;/code&amp;gt; matrix. This is then overlaid onto the playfield, and can be moved around. Nonzero cells of the matrix define the shape of the piece, like:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 0|| 0|| 0|| 0&lt;br /&gt;
|-&lt;br /&gt;
| 0|| 5|| 0|| 0&lt;br /&gt;
|-&lt;br /&gt;
| 5|| 5|| 0|| 0&lt;br /&gt;
|-&lt;br /&gt;
| 0|| 5|| 0|| 0&lt;br /&gt;
|}&lt;br /&gt;
The numbers used to define the piece are unique to each piece, so if you write the display section to use each number as a color, you can have a color game.&lt;br /&gt;
&lt;br /&gt;
====Collision detection====&lt;br /&gt;
In the collision detection section, the overlaid piece carrier is moved as if you were to make that move, and then we check if any of the cells overlap with non-blank sections of the playfield, or if any part of the active piece lies outside the playfield area. If so, the move is undone, and you can decide what to do from there. If no collision occurs, the move is done for real and displayed the next time the screen is updated.&lt;br /&gt;
 &lt;br /&gt;
Usually, the collision detection is used to prevent motion if a piece or wall is in the way, but if the piece is attempting to move downwards, the piece is instead copied onto the playfield, and the numbers making up the piece are increased by 8. This is how the collision detection can differentiate between the active piece and dead pieces in the playfield.&lt;br /&gt;
&lt;br /&gt;
====Input conditioning====&lt;br /&gt;
I added some input conditioning as well. For rotations of the active piece, the move is not repeating, and you must release the rotation input and press it again for a rotation to happen again. For moves left, right, and down, a system similar to repeating keys on a computer keyboard is used, holding down the direction for a brief moment will make the action repeat. In the gameboy version, dropping a piece quickly will increase the score based on the number of tiles dropped while moving fast. This is also replicated (but probably not perfectly) in my version.&lt;br /&gt;
&lt;br /&gt;
===Details===&lt;br /&gt;
====Levels and speed====&lt;br /&gt;
I only update the screen every few moments. Call the time of one of these updates a '''frame'''. While the screen is waiting to be updated, it's a good time to check the controls and see what we need to do the next time the screen needs updating. All the processing of the moves and collisions happens just before the screen updates. Every few frames, the active piece drops one row. This is connected to the levels, which in my version go from 0-20, which I think is in line with the gb version. The drop happens every 20 frames at level 0, up to a delay of 0 frames at level 20. For every 10 lines that the user clears, the level is increased by 1, so that the game gets progressively faster, just like the gb version. &lt;br /&gt;
====Game over detection====&lt;br /&gt;
This one is pretty simple. If any non-active piece sections are found on the line above the top of the visible playfield, the game ends. Check this every frame.&lt;br /&gt;
====Line completion detection====&lt;br /&gt;
This is a little more complicated than the game over, but basically I check to see if any rows are completely filled in with dead pieces. I keep track of which lines are full (and how many of them there are), and animate the corresponding lines by having the whole row(s) flash for a moment. At this point I also add onto the number of completed lines counter displayed on the scoreboard. Then, I copy all the lines above the filled line down by one tile. Since this is done for each filled line in sequence, the effect moves the playfield down for all cases of any number of filled lines.  &lt;br /&gt;
====Scoring====&lt;br /&gt;
The number of filled lines cleared in one move (calculated above) is used in the scoring formula to add to the score. The full formula (according to the Nintendo scoring system) is:&lt;br /&gt;
* 1 line: 40*(level+1)&lt;br /&gt;
* 2 lines: 100*(level+1)&lt;br /&gt;
* 3 lines: 300*(level+1)&lt;br /&gt;
* 4 lines: 1200*(level+1) &lt;br /&gt;
&lt;br /&gt;
Additionally, the number of grid tiles that are dropped by the user pressing &amp;quot;down&amp;quot; (ie, soft drop), which I kept track of earlier, are also added to the score.&lt;br /&gt;
====New game====&lt;br /&gt;
I have a new game button also. When pressed (and the controls are checked), it picks new pieces, clears the level, score, and lines counters, and empties the playfield of dead pieces. Thus, a new game begins.&lt;br /&gt;
&lt;br /&gt;
==Hardware (Arduino version)==&lt;br /&gt;
I do most of my software development on arduino-like embedded systems, and am pretty familiar with I/O options on these devices (more so than on a stndard computer), so I used an arduino nano with a 128x64 st7920 monochrome LCD and the u8g2 libray for the display. For input, I wired up 3 buttons, 2 rotate and one new game, and an analog joystick (which I am using as a digital one) for directional moves.&lt;br /&gt;
&lt;br /&gt;
===Porting===&lt;br /&gt;
With any luck, my code can be ported to different platforms just my changing the section that displays the playfield on a screen, the input sections that take in the button presses, and maybe the delay sections. On the Arduino, my code takes up about 50% of the prgram storage space, and 50% of ram, with most of that being due to the u8g2 library. I'm not an expert on the super low-level processes on the arduino, nor do I think my game is the most effceiently coded, but my guess is that if the game works well on a system with as low specifications as the atmega328, it should be able to run on nearly anything else.&lt;br /&gt;
&lt;br /&gt;
==The Code (Arduino version)==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#define pfsizeX 10&lt;br /&gt;
#define pfsizeY 22 //note that top 4 lines are not drawn&lt;br /&gt;
uint8_t playfield[pfsizeX][pfsizeY];  //the game area&lt;br /&gt;
&lt;br /&gt;
uint8_t pieceC[4][4]; //piece container&lt;br /&gt;
int8_t pieceCX;  //location of upper left corner of piece container&lt;br /&gt;
int8_t pieceCY;&lt;br /&gt;
uint8_t pieceT; //type of piece&lt;br /&gt;
uint8_t pieceR; //rotation of piece &lt;br /&gt;
uint8_t nextpieceT; &lt;br /&gt;
uint8_t nextpieceR; &lt;br /&gt;
&lt;br /&gt;
uint8_t dcontrol; //locks in control every frame&lt;br /&gt;
uint8_t rcontrol;&lt;br /&gt;
&lt;br /&gt;
uint8_t lastdcontrol=0; //for &amp;quot;debouncing&amp;quot; of inputs&lt;br /&gt;
uint8_t lastrcontrol=0; &lt;br /&gt;
uint8_t drepeatframe=0;&lt;br /&gt;
#define drepeatframes 3 //wait _ frames before repeatedly going in one direction&lt;br /&gt;
&lt;br /&gt;
uint8_t dropframe=0;    //counter for number of frames between block drops&lt;br /&gt;
uint8_t level=0; //LEVEL decreases frame drop from 20 frames to 0 frames (levels 0 to 20)&lt;br /&gt;
&lt;br /&gt;
boolean ngame=0; //when set, starts new game&lt;br /&gt;
&lt;br /&gt;
uint16_t lines=0; //NUMBER OF LINES CLEARED&lt;br /&gt;
uint32_t score=0; //TOTAL SCORE (using NES rules)&lt;br /&gt;
&lt;br /&gt;
uint8_t fdrop;   //number of blocks that piece has been fast dropped&lt;br /&gt;
uint8_t lslvi=0; //lines since level increase (when this gets to 10, increase the level)&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;U8g2lib.h&amp;gt;&lt;br /&gt;
U8G2_ST7920_128X64_1_HW_SPI u8g2(U8G2_R0, /* CS=*/ 12, /* reset=*/ 8);&lt;br /&gt;
&lt;br /&gt;
void dispscreen(){&lt;br /&gt;
  u8g2.firstPage();  &lt;br /&gt;
  do {&lt;br /&gt;
    for(uint8_t j=4; j&amp;lt;pfsizeY; j++){ //draw screen&lt;br /&gt;
    for(uint8_t i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
      if(playfield[i][j]!=0){&lt;br /&gt;
        u8g2.drawBox(5*(j-4),5*(pfsizeX-1)-5*i,6,6);&lt;br /&gt;
      }else{&lt;br /&gt;
        u8g2.drawFrame(5*(j-4),5*(pfsizeX-1)-5*i,6,6);&lt;br /&gt;
        //if(j==3){u8g2.drawLine(5*j,5*(pfsizeX-1)-5*i,5*j+5,5*(pfsizeX-1)-5*i+5);}&lt;br /&gt;
      }&lt;br /&gt;
    }}&lt;br /&gt;
&lt;br /&gt;
  uint8_t temppieceT=pieceT; &lt;br /&gt;
  uint8_t temppieceR=pieceR;&lt;br /&gt;
  pieceT=nextpieceT;&lt;br /&gt;
  pieceR=nextpieceR;&lt;br /&gt;
  loadpiece(); &lt;br /&gt;
  for(uint8_t j=0; j&amp;lt;4; j++){ //draw next piece&lt;br /&gt;
  for(uint8_t i=0; i&amp;lt;4; i++){&lt;br /&gt;
    if(pieceC[i][j]!=0){&lt;br /&gt;
      u8g2.drawBox(5*(j+pfsizeY-3),5*(pfsizeX-1)-5*i,6,6);&lt;br /&gt;
    }else{&lt;br /&gt;
      u8g2.drawFrame(5*(j+pfsizeY-3),5*(pfsizeX-1)-5*i,6,6);&lt;br /&gt;
    }&lt;br /&gt;
  }}&lt;br /&gt;
  pieceT=temppieceT;&lt;br /&gt;
  pieceR=temppieceR;&lt;br /&gt;
  loadpiece();&lt;br /&gt;
&lt;br /&gt;
  u8g2.setFont(u8g2_font_6x10_tf);&lt;br /&gt;
  char zbuffer[32];&lt;br /&gt;
  sprintf(zbuffer, &amp;quot;N%d&amp;quot;, lines);&lt;br /&gt;
  u8g2.drawStr(0,60, zbuffer);&lt;br /&gt;
  sprintf(zbuffer, &amp;quot;L%d&amp;quot;, level);&lt;br /&gt;
  u8g2.drawStr(25,60, zbuffer);&lt;br /&gt;
  sprintf(zbuffer, &amp;quot;S%d&amp;quot;, score);&lt;br /&gt;
  u8g2.drawStr(50,60, zbuffer);  &lt;br /&gt;
  } while( u8g2.nextPage() );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void controls(){&lt;br /&gt;
  if(digitalRead(A3)==0){ngame=1;}&lt;br /&gt;
  if(dcontrol==0){&lt;br /&gt;
    if(analogRead(A7)&amp;lt;10){dcontrol=4;}&lt;br /&gt;
    if(analogRead(A7)&amp;gt;1014){dcontrol=6;}&lt;br /&gt;
    if(analogRead(A6)&amp;gt;1014){dcontrol=8;}&lt;br /&gt;
    if(analogRead(A6)&amp;lt;10){dcontrol=2;}&lt;br /&gt;
    if(dcontrol==0){drepeatframe=0;}&lt;br /&gt;
    if(dcontrol==lastdcontrol){          //short delay before fast motion&lt;br /&gt;
      if(drepeatframe&amp;lt;=drepeatframes){&lt;br /&gt;
        drepeatframe++;&lt;br /&gt;
        dcontrol=3; //lockout if within lockout period&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(rcontrol==0){&lt;br /&gt;
    if(digitalRead(A4)==0){rcontrol=1;}&lt;br /&gt;
    if(digitalRead(A5)==0){rcontrol=2;}&lt;br /&gt;
    if(rcontrol==lastrcontrol){rcontrol=3;}&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void clearControls(){&lt;br /&gt;
  if(rcontrol!=3){lastrcontrol=rcontrol;}&lt;br /&gt;
  if(dcontrol!=3){lastdcontrol=dcontrol;}&lt;br /&gt;
  rcontrol=0;&lt;br /&gt;
  dcontrol=0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void draw(){&lt;br /&gt;
  loadpiece(); //load piece into the piece carrier&lt;br /&gt;
  for(uint8_t i=0; i&amp;lt;pfsizeX; i++){   //clear any cells with active piece parts (will be written again with new pieceC&lt;br /&gt;
    for(uint8_t j=0; j&amp;lt;pfsizeY; j++){&lt;br /&gt;
      if(playfield[i][j]&amp;lt;=7){playfield[i][j]=0;}&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  for(uint8_t i=0; i&amp;lt;4; i++){ //copy active piece onto the playfield&lt;br /&gt;
    for(uint8_t j=0; j&amp;lt;4; j++){&lt;br /&gt;
      if(pieceCX+i&amp;gt;=0&amp;amp;&amp;amp;pieceCX+i&amp;lt;pfsizeX&amp;amp;&amp;amp;pieceCY+j&amp;gt;=0&amp;amp;&amp;amp;pieceCY+j&amp;lt;pfsizeY){//check if piece segment can be drawn on screen&lt;br /&gt;
        if(pieceC[i][j]!=0){playfield[i+pieceCX][j+pieceCY]=pieceC[i][j];}&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
boolean checkCollide(){ //move the piece carrier first, then check if anything collides&lt;br /&gt;
  loadpiece(); //load piece into the piece carrier&lt;br /&gt;
  boolean nonvalidity=0;&lt;br /&gt;
  for(uint8_t i=0; i&amp;lt;4; i++){ //run through all piece carrier cells&lt;br /&gt;
    for(uint8_t j=0; j&amp;lt;4; j++){&lt;br /&gt;
      if(pieceCX+i&amp;gt;=0&amp;amp;&amp;amp;pieceCX+i&amp;lt;pfsizeX&amp;amp;&amp;amp;pieceCY+j&amp;gt;=0&amp;amp;&amp;amp;pieceCY+j&amp;lt;pfsizeY){ //check if piece carrier segment can be drawn on screen&lt;br /&gt;
        if(pieceC[i][j]!=0&amp;amp;&amp;amp;playfield[i+pieceCX][j+pieceCY]&amp;gt;7){ //if both background and nonzero piece carrier segment collide&lt;br /&gt;
          nonvalidity=1;&lt;br /&gt;
        }&lt;br /&gt;
      }else{ //this segment of PC can't be drawn on the screen&lt;br /&gt;
        if(pieceC[i][j]!=0){ //a filled in segment would be drawn offscreen&lt;br /&gt;
          nonvalidity=1;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  return nonvalidity;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void piece2bg(){&lt;br /&gt;
  for(uint8_t i=0; i&amp;lt;4; i++){ //copy active piece onto the screen&lt;br /&gt;
  for(uint8_t j=0; j&amp;lt;4; j++){&lt;br /&gt;
    if(pieceC[i][j]!=0){playfield[i+pieceCX][j+pieceCY]=pieceC[i][j]+8;} //copy the piece into the playfield/background &lt;br /&gt;
  }}&lt;br /&gt;
  nextpiece();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void nextpiece(){//generate the next piece and move PC back to the top&lt;br /&gt;
  pieceT=nextpieceT;&lt;br /&gt;
  pieceR=nextpieceR;&lt;br /&gt;
  nextpieceT = rand()%7 + 1;&lt;br /&gt;
  nextpieceR = rand()%4 + 1;&lt;br /&gt;
  pieceCY=0; //move piece carrier back to the top of screen&lt;br /&gt;
  pieceCX=3;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void newgamemon(){&lt;br /&gt;
  if(ngame==0){return;}&lt;br /&gt;
  Serial.println(F(&amp;quot;NEW GAME&amp;quot;));&lt;br /&gt;
  ngame=0;&lt;br /&gt;
  for(uint8_t i=0; i&amp;lt;pfsizeX; i++){   //clear any cells with active piece parts (will be written again with new pieceC&lt;br /&gt;
    for(uint8_t j=0; j&amp;lt;pfsizeY; j++){&lt;br /&gt;
      playfield[i][j]=0;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  lines=0;&lt;br /&gt;
  level=0;&lt;br /&gt;
  score=0;&lt;br /&gt;
  lslvi=0;&lt;br /&gt;
  fdrop=0;&lt;br /&gt;
  nextpiece();&lt;br /&gt;
  nextpiece();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loadpiece(){ //hardcoded all pieces&lt;br /&gt;
  switch(pieceT){&lt;br /&gt;
    case 1: //long one&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=1; pieceC[1][2]=1; pieceC[2][2]=1; pieceC[3][2]=1;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0; &lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=1; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=1; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=0; pieceC[2][2]=1; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=1; pieceC[3][3]=0; &lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 2: //backwards L&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=2; pieceC[1][2]=2; pieceC[2][2]=2; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=2; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=2; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=2; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=2; pieceC[1][3]=2; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=2; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=2; pieceC[1][2]=2; pieceC[2][2]=2; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=2; pieceC[2][1]=2; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=2; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=2; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 3: //L&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=3; pieceC[1][2]=3; pieceC[2][2]=3; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=3; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=3; pieceC[1][1]=3; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=3; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=3; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=3; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=3; pieceC[1][2]=3; pieceC[2][2]=3; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=3; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=3; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=3; pieceC[2][3]=3; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 4: //s shape&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=4; pieceC[2][2]=4; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=4; pieceC[1][3]=4; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=4; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=4; pieceC[2][2]=4; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=4; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 5: //T shape&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=5; pieceC[1][2]=5; pieceC[2][2]=5; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=5; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=5; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=5; pieceC[1][2]=5; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=5; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=5; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=5; pieceC[1][2]=5; pieceC[2][2]=5; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=5; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=5; pieceC[2][2]=5; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=5; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 6: //reverse s&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=6; pieceC[1][2]=6; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=6; pieceC[2][3]=6; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=6; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=6; pieceC[2][2]=6; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=6; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 7: //square&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      default:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=7; pieceC[2][1]=7; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=7; pieceC[2][2]=7; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setup(){&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  u8g2.begin();&lt;br /&gt;
  pinMode(A4, INPUT_PULLUP);&lt;br /&gt;
  pinMode(A5, INPUT_PULLUP);&lt;br /&gt;
  pinMode(A3, INPUT_PULLUP); //new game button&lt;br /&gt;
&lt;br /&gt;
  ngame=1;&lt;br /&gt;
  newgamemon();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
  &lt;br /&gt;
  for(uint32_t h=0;h&amp;lt;=5;h++){//frame delay  //DELAY SECTION (BETWEEN FRAMES)&lt;br /&gt;
    delay(1);&lt;br /&gt;
    controls();&lt;br /&gt;
    newgamemon();&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  boolean droppiece=0;&lt;br /&gt;
  dropframe++;&lt;br /&gt;
  if(dropframe&amp;gt;=(20-level)){&lt;br /&gt;
    dropframe=0;&lt;br /&gt;
    droppiece=1;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  if(rcontrol==1){&lt;br /&gt;
    pieceR++; if(pieceR&amp;gt;=5){pieceR=1;} //try to rotate piece&lt;br /&gt;
    if(checkCollide()){&lt;br /&gt;
      pieceR--; if(pieceR&amp;lt;=0){pieceR=4;} //undo rotation&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(rcontrol==2){&lt;br /&gt;
    pieceR--; if(pieceR&amp;lt;=0){pieceR=4;}&lt;br /&gt;
    if(checkCollide()){&lt;br /&gt;
      pieceR++; if(pieceR&amp;gt;=5){pieceR=1;}&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(dcontrol==4){&lt;br /&gt;
    pieceCX--; //try and see what happens if we move the piece left&lt;br /&gt;
    if(checkCollide()){//piece move is not valid&lt;br /&gt;
      pieceCX++; //take piece back&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(dcontrol==6){&lt;br /&gt;
    pieceCX++; //try and see what happens if we move the piece left&lt;br /&gt;
    if(checkCollide()){//piece move is not valid&lt;br /&gt;
      pieceCX--; //take piece back&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(dcontrol==8){&lt;br /&gt;
    pieceCY--; //try and see what happens if we move the piece up&lt;br /&gt;
    if(checkCollide()){//piece move is not valid&lt;br /&gt;
      pieceCY++; //take piece back&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(dcontrol==2||droppiece==1){&lt;br /&gt;
    pieceCY++; //try and see what happens if we move the piece down&lt;br /&gt;
    if(!(drepeatframe&amp;lt;=drepeatframes)){ //is in fast mode&lt;br /&gt;
      fdrop++;&lt;br /&gt;
    }else{&lt;br /&gt;
      fdrop=0;&lt;br /&gt;
    }&lt;br /&gt;
    if(checkCollide()){//piece move is not valid&lt;br /&gt;
      pieceCY--; //take piece back&lt;br /&gt;
      score+=fdrop; //add # of fast dropped blocks to score&lt;br /&gt;
      fdrop=0;&lt;br /&gt;
      piece2bg(); //copy piece to background and reset to next piece&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  draw();&lt;br /&gt;
  dispscreen();&lt;br /&gt;
  &lt;br /&gt;
  clearControls();&lt;br /&gt;
&lt;br /&gt;
  //check for line clears&lt;br /&gt;
  boolean clearline[pfsizeY];&lt;br /&gt;
  uint8_t clearedlines=0;&lt;br /&gt;
  uint8_t templines=0;&lt;br /&gt;
  for(uint8_t j=4; j&amp;lt;pfsizeY; j++){&lt;br /&gt;
  clearline[j]=1; //assume line is cleared&lt;br /&gt;
  for(uint8_t i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
    if(playfield[i][j]&amp;lt;=7){clearline[j]=0;break;} //line is not full&lt;br /&gt;
  }&lt;br /&gt;
  clearedlines+=clearline[j];&lt;br /&gt;
  templines+=clearline[j];&lt;br /&gt;
  }&lt;br /&gt;
  if(clearedlines&amp;gt;0){//breifly animate the cleared lines, then clear them&lt;br /&gt;
    for(uint8_t f=0; f&amp;lt;=6; f++){&lt;br /&gt;
      for(uint8_t j=4; j&amp;lt;pfsizeY; j++){&lt;br /&gt;
        if(clearline[j]==1){&lt;br /&gt;
          for(uint8_t i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
            if(f%2==0){playfield[i][j]=8;}else{playfield[i][j]=0;}&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      draw();&lt;br /&gt;
      dispscreen();&lt;br /&gt;
      delay(200);&lt;br /&gt;
    }&lt;br /&gt;
    for(uint8_t j=4; j&amp;lt;pfsizeY; j++){ //accutally clear the lines&lt;br /&gt;
       if(clearline[j]==1){&lt;br /&gt;
        for(uint8_t t=j; t&amp;gt;=4; t--){&lt;br /&gt;
         for(uint8_t i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
           playfield[i][t]=playfield[i][t-1];&lt;br /&gt;
         }&lt;br /&gt;
        }&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
   lines+=templines; //add number of lines to lines counter&lt;br /&gt;
   switch(templines){ //calculate score&lt;br /&gt;
    case 1:&lt;br /&gt;
      score+=40*(level+1);&lt;br /&gt;
      break;&lt;br /&gt;
    case 2:&lt;br /&gt;
      score+=100*(level+1);&lt;br /&gt;
      break;&lt;br /&gt;
    case 3:&lt;br /&gt;
      score+=300*(level+1);&lt;br /&gt;
      break;&lt;br /&gt;
    default:&lt;br /&gt;
      score+=1200*(level+1);&lt;br /&gt;
      break;&lt;br /&gt;
   }&lt;br /&gt;
   for(uint8_t i=1; i&amp;lt;=templines; i++){ //see if the level needs increasing&lt;br /&gt;
    lslvi++;&lt;br /&gt;
    if(lslvi&amp;gt;=10){lslvi=0;level++;}&lt;br /&gt;
    if(level&amp;gt;=20){level=20;}&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
  //check gameover (scan through line 3 and see if there are any non-active pieces in it)&lt;br /&gt;
  for(uint8_t i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
    if(playfield[i][3]&amp;gt;7){&lt;br /&gt;
      //*********************GAME OVER********************&lt;br /&gt;
      Serial.println(F(&amp;quot;GAME OVER&amp;quot;));&lt;br /&gt;
      while(ngame==0){&lt;br /&gt;
        controls();&lt;br /&gt;
      }&lt;br /&gt;
      newgamemon();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  //check for completed lines&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
==The Code (TRS-80 version)==&lt;br /&gt;
I used the z88dk C compiler for the port. To compile, use &amp;lt;code&amp;gt;zcc  +trs80 -lndos -lm -create-app soviet-bloc-game.c -o soviet-bloc-game&amp;lt;/code&amp;gt; which will create a &amp;lt;code&amp;gt;.CAS&amp;lt;/code&amp;gt; file. Use &amp;lt;code&amp;gt;zcc  +trs80 -lndos -lm -create-app -subtype=disk soviet-bloc-game.c -o soviet-bloc-game&amp;lt;/code&amp;gt; to create a &amp;lt;code&amp;gt;.CMD&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Porting this to the TRS-80 turned out to be a much harder challange, mostly because the TRS-80 is a very slow, low power machine. Some of the improvements were obvious, like only checking the lines and game over only when an active piece becomes inactive. However, this isn't enough to make a playable version. The TRS-80 is slow enough that every comparison, operation, and graphics plot add noticable delay. Therefore, I only update parts of the display when needed (ie, the area around the active piece, 1 block around the piece carrier); the background is filled back in to erase piece moves, and the screen is only redrawn at the end when the active piece becomes inactive. This is possible because unlike the arduino version, the whole screen doesn't need to be rewitten every time it changes. I also had to take out any frame delay and reduce the levels to 10 because the machine is not fast enough to keep up. The version below is therefore clunky, but playable. The obvious next steps are to make an assembler version of Soviet Bloc Game.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//soviet-bloc-game.c - Soviet Bloc Game for TRS-80 - 05/28/2019&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt; &lt;br /&gt;
#include &amp;lt;graphics.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define pfsizeX 10 //playfield size (in cells)&lt;br /&gt;
#define pfsizeY 22 //note that top 4 lines are not drawn&lt;br /&gt;
#define pfX 8 //upper left corner of playfield location (pixel)&lt;br /&gt;
#define pfY 4&lt;br /&gt;
#define npX 60 //upper left corner of next piece indicator&lt;br /&gt;
#define npY 2&lt;br /&gt;
#define clspX 4 //spacing between cell elements (pixels)&lt;br /&gt;
#define clspY 2&lt;br /&gt;
&lt;br /&gt;
void controls(void);&lt;br /&gt;
&lt;br /&gt;
unsigned char playfield[pfsizeX][pfsizeY];  //the game area&lt;br /&gt;
unsigned char cscreen[pfsizeX][pfsizeY-4];  //a copy of what gets drawn on the screen (game area)&lt;br /&gt;
unsigned char pieceC[4][4]; //piece container&lt;br /&gt;
signed char pieceCX;  //location of upper left corner of piece container&lt;br /&gt;
signed char pieceCY;&lt;br /&gt;
unsigned char pieceT; //type of piece&lt;br /&gt;
unsigned char pieceR; //rotation of piece &lt;br /&gt;
unsigned char nextpieceT; &lt;br /&gt;
unsigned char nextpieceR; &lt;br /&gt;
&lt;br /&gt;
unsigned char dcontrol; //locks in control every frame&lt;br /&gt;
unsigned char rcontrol;&lt;br /&gt;
&lt;br /&gt;
unsigned char lastdcontrol=0; //for &amp;quot;debouncing&amp;quot; of inputs&lt;br /&gt;
unsigned char lastrcontrol=0; &lt;br /&gt;
unsigned char drepeatframe=0;&lt;br /&gt;
#define drepeatframes 3 //wait _ frames before repeatedly going in one direction&lt;br /&gt;
&lt;br /&gt;
unsigned char dropframe=0;    //counter for number of frames between block drops&lt;br /&gt;
unsigned char level=0; //LEVEL decreases frame drop from maxlevel frames to 0 frames (levels 0 to MAXLEVEL)&lt;br /&gt;
#define maxlevel 10&lt;br /&gt;
&lt;br /&gt;
unsigned char ngame=1; //when set, starts new game&lt;br /&gt;
unsigned char newpc=0; //when set, get a new piece&lt;br /&gt;
&lt;br /&gt;
unsigned int lines=0; //NUMBER OF LINES CLEARED&lt;br /&gt;
long score=0; //TOTAL SCORE (using NES rules)&lt;br /&gt;
&lt;br /&gt;
unsigned char fdrop;   //number of blocks that piece has been fast dropped&lt;br /&gt;
unsigned char lslvi=0; //lines since level increase (when this gets to 10, increase the level)&lt;br /&gt;
&lt;br /&gt;
char lvladd=0;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void setblock(char x, char y){&lt;br /&gt;
	for(char i=0; i&amp;lt;8; i++){&lt;br /&gt;
		controls();&lt;br /&gt;
     	plot(x+i%4,y+i/4);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
void clearblock(char x, char y){&lt;br /&gt;
	for(char i=0; i&amp;lt;8; i++){&lt;br /&gt;
		controls();&lt;br /&gt;
    	unplot(x+i%4,y+i/4);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
void cdraw(char x, char y, char color){//only draws on screen if it needs to (uses cell coordinates)&lt;br /&gt;
	if(cscreen[x][y]!=color){&lt;br /&gt;
		cscreen[x][y]=color;&lt;br /&gt;
		if(color!=0){&lt;br /&gt;
			setblock( (x)*clspX+pfX, (y)*clspY+pfY );&lt;br /&gt;
		}else{&lt;br /&gt;
			clearblock( (x)*clspX+pfX, (y)*clspY+pfY);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
void recopyscreen(){ //copies playfield to screen &lt;br /&gt;
  for(char i=0; i&amp;lt;pfsizeX; i++){ //manually redraw the entire screen&lt;br /&gt;
  for(char j=4; j&amp;lt;pfsizeY; j++){&lt;br /&gt;
	  cdraw(i,j-4,playfield[i][j]);&lt;br /&gt;
  }}&lt;br /&gt;
  printf(&amp;quot;%cY%c%c%ld&amp;quot;, 27, 32+3, 32+47, score);&lt;br /&gt;
  printf(&amp;quot;%cY%c%c%d&amp;quot;, 27, 32+5, 32+47, level);&lt;br /&gt;
  printf(&amp;quot;%cY%c%c%d&amp;quot;, 27, 32+7, 32+47, lines);&lt;br /&gt;
}&lt;br /&gt;
void controls(){&lt;br /&gt;
	/*&lt;br /&gt;
   if(bpeek(0x3802)&amp;amp;0b00000010){plot(126,46);}else{unplot(126,46);} //up button (I)&lt;br /&gt;
   if(bpeek(0x3802)&amp;amp;0b00000100){plot(125,47);}else{unplot(125,47);} //left button (J)&lt;br /&gt;
   if(bpeek(0x3802)&amp;amp;0b00001000){plot(126,47);}else{unplot(126,47);} //down button (K)&lt;br /&gt;
   if(bpeek(0x3802)&amp;amp;0b00010000){plot(127,47);}else{unplot(127,47);} //right button (L)&lt;br /&gt;
   if(bpeek(0x3801)&amp;amp;0b00000010){plot(121,47);}else{unplot(121,47);} //a button (A)&lt;br /&gt;
   if(bpeek(0x3804)&amp;amp;0b00001000){plot(122,47);}else{unplot(122,47);} //b button (S)&lt;br /&gt;
   if(bpeek(0x3801)&amp;amp;0b00000001){plot(119,47);}else{unplot(119,47);} //newgame (@)&lt;br /&gt;
	   */&lt;br /&gt;
  if(bpeek(0x3840)&amp;amp;0b00000100){exit(0);}&lt;br /&gt;
  if(bpeek(0x3810)&amp;amp;0b00000010){lvladd=1;}&lt;br /&gt;
  if(bpeek(0x3810)&amp;amp;0b00000001){lvladd=-1;}&lt;br /&gt;
  &lt;br /&gt;
  if(bpeek(0x3801)&amp;amp;0b00000001){ngame=1;}&lt;br /&gt;
  if(bpeek(0x3808)&amp;amp;0b00000010){newpc=1;}&lt;br /&gt;
  if(dcontrol==0){&lt;br /&gt;
    if(bpeek(0x3802)&amp;amp;0b00000100){dcontrol=4;}&lt;br /&gt;
    if(bpeek(0x3802)&amp;amp;0b00010000){dcontrol=6;}&lt;br /&gt;
    if(bpeek(0x3802)&amp;amp;0b00000010){dcontrol=8;}&lt;br /&gt;
    if(bpeek(0x3802)&amp;amp;0b00001000){dcontrol=2;}&lt;br /&gt;
    if(dcontrol==0){drepeatframe=0;}&lt;br /&gt;
    if(dcontrol==lastdcontrol){          //short delay before fast motion&lt;br /&gt;
      if(drepeatframe&amp;lt;=drepeatframes){&lt;br /&gt;
        drepeatframe++;&lt;br /&gt;
        dcontrol=3; //lockout if within lockout period&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(rcontrol==0){&lt;br /&gt;
    if(bpeek(0x3801)&amp;amp;0b00000010){rcontrol=1;}&lt;br /&gt;
    if(bpeek(0x3804)&amp;amp;0b00001000){rcontrol=2;}&lt;br /&gt;
    if(rcontrol==lastrcontrol){rcontrol=3;}&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
void clearControls(){&lt;br /&gt;
  if(rcontrol!=3){lastrcontrol=rcontrol;}&lt;br /&gt;
  if(dcontrol!=3){lastdcontrol=dcontrol;}&lt;br /&gt;
  rcontrol=0;&lt;br /&gt;
  dcontrol=0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void loadpiece(){ //hardcoded all pieces&lt;br /&gt;
  switch(pieceT){&lt;br /&gt;
    case 1: //long one&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=1; pieceC[1][2]=1; pieceC[2][2]=1; pieceC[3][2]=1;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0; &lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=1; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=1; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=0; pieceC[2][2]=1; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=1; pieceC[3][3]=0; &lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 2: //backwards L&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=2; pieceC[1][2]=2; pieceC[2][2]=2; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=2; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=2; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=2; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=2; pieceC[1][3]=2; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=2; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=2; pieceC[1][2]=2; pieceC[2][2]=2; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=2; pieceC[2][1]=2; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=2; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=2; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 3: //L&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=3; pieceC[1][2]=3; pieceC[2][2]=3; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=3; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=3; pieceC[1][1]=3; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=3; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=3; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=3; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=3; pieceC[1][2]=3; pieceC[2][2]=3; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=3; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=3; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=3; pieceC[2][3]=3; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 4: //s shape&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=4; pieceC[2][2]=4; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=4; pieceC[1][3]=4; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=4; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=4; pieceC[2][2]=4; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=4; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 5: //T shape&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=5; pieceC[1][2]=5; pieceC[2][2]=5; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=5; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=5; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=5; pieceC[1][2]=5; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=5; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=5; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=5; pieceC[1][2]=5; pieceC[2][2]=5; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=5; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=5; pieceC[2][2]=5; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=5; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 6: //reverse s&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=6; pieceC[1][2]=6; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=6; pieceC[2][3]=6; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=6; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=6; pieceC[2][2]=6; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=6; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 7: //square&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      default:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=7; pieceC[2][1]=7; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=7; pieceC[2][2]=7; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void nextpiece(){//generate the next piece and move PC back to the top&lt;br /&gt;
  unsigned char tempT=nextpieceT;&lt;br /&gt;
  unsigned char tempR=nextpieceR;&lt;br /&gt;
  nextpieceT = rand()%7 + 1;&lt;br /&gt;
  nextpieceR = rand()%4 + 1;&lt;br /&gt;
  pieceT=nextpieceT;&lt;br /&gt;
  pieceR=nextpieceR;&lt;br /&gt;
  loadpiece();&lt;br /&gt;
  for(char i=0; i&amp;lt;4; i++){&lt;br /&gt;
  for(char j=0; j&amp;lt;4; j++){&lt;br /&gt;
	  if(pieceC[i][j]!=0){&lt;br /&gt;
		  setblock((i)*clspX+npX, (j)*clspY+npY);&lt;br /&gt;
	  }else{&lt;br /&gt;
		  clearblock((i)*clspX+npX, (j)*clspY+npY);&lt;br /&gt;
	  }&lt;br /&gt;
  }}&lt;br /&gt;
  &lt;br /&gt;
  pieceT=tempT;&lt;br /&gt;
  pieceR=tempR;&lt;br /&gt;
  &lt;br /&gt;
  pieceCY=0; //move piece carrier back to the top of screen&lt;br /&gt;
  pieceCX=3;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void mdraw(){&lt;br /&gt;
  //printf(&amp;quot;%cY%c%c%d&amp;quot;, 27, 32+1, 32+55, fff); fff++;&lt;br /&gt;
  loadpiece(); //load piece into the piece carrier&lt;br /&gt;
  for(char i=0; i&amp;lt;6; i++){ //check piece carrier and one block offset outside it&lt;br /&gt;
  for(char j=0; j&amp;lt;6; j++){&lt;br /&gt;
	  controls();&lt;br /&gt;
	  if((pieceCX-1+i)&amp;gt;=0&amp;amp;&amp;amp;(pieceCX-1+i)&amp;lt;pfsizeX&amp;amp;&amp;amp;(pieceCY-1+j)&amp;gt;=4&amp;amp;&amp;amp;(pieceCY-1+j)&amp;lt;(pfsizeY)){//check if this segment be drawn on screen&lt;br /&gt;
		if(i&amp;gt;=1&amp;amp;&amp;amp;i&amp;lt;=4&amp;amp;&amp;amp;j&amp;gt;=1&amp;amp;&amp;amp;j&amp;lt;=4){//accessing piece carrier section here}&lt;br /&gt;
			if(pieceC[i-1][j-1]&amp;gt;0){//if piece carrier block is confirmed should be white&lt;br /&gt;
				cdraw(i-1+pieceCX,j-5+pieceCY,pieceC[i-1][j-1]);&lt;br /&gt;
			}else{&lt;br /&gt;
				cdraw(i-1+pieceCX,j-5+pieceCY,playfield[i-1+pieceCX][j-1+pieceCY]); //color it in with the existing playfield background&lt;br /&gt;
			}&lt;br /&gt;
		}else{&lt;br /&gt;
			cdraw(i-1+pieceCX,j-5+pieceCY,playfield[i-1+pieceCX][j-1+pieceCY]);&lt;br /&gt;
		}&lt;br /&gt;
	  }&lt;br /&gt;
  }}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
unsigned char checkCollide(){ //move the piece carrier first, then check if anything collides&lt;br /&gt;
  loadpiece(); //load piece into the piece carrier&lt;br /&gt;
  unsigned char nonvalidity=0;&lt;br /&gt;
  for(char i=0; i&amp;lt;4; i++){ //run through all piece carrier cells&lt;br /&gt;
    for(char j=0; j&amp;lt;4; j++){&lt;br /&gt;
      if(pieceCX+i&amp;gt;=0&amp;amp;&amp;amp;pieceCX+i&amp;lt;pfsizeX&amp;amp;&amp;amp;pieceCY+j&amp;gt;=0&amp;amp;&amp;amp;pieceCY+j&amp;lt;pfsizeY){ //check if piece carrier segment can be drawn on screen&lt;br /&gt;
        if(pieceC[i][j]!=0&amp;amp;&amp;amp;playfield[i+pieceCX][j+pieceCY]&amp;gt;7){ //if both background and nonzero piece carrier segment collide&lt;br /&gt;
          nonvalidity=1;&lt;br /&gt;
        }&lt;br /&gt;
      }else{ //this segment of PC can't be drawn on the screen&lt;br /&gt;
        if(pieceC[i][j]!=0){ //a filled in segment would be drawn offscreen&lt;br /&gt;
          nonvalidity=1;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  return nonvalidity;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void newgamemon(){&lt;br /&gt;
  if(ngame==0){return;}&lt;br /&gt;
  //Serial.println(F(&amp;quot;NEW GAME&amp;quot;));&lt;br /&gt;
  printf(&amp;quot;%cY%c%c         &amp;quot;, 27, 32+14, 32+40);&lt;br /&gt;
  printf(&amp;quot;%cY%c%c        &amp;quot;, 27, 32+3, 32+47);&lt;br /&gt;
  printf(&amp;quot;%cY%c%c        &amp;quot;, 27, 32+5, 32+47);&lt;br /&gt;
  printf(&amp;quot;%cY%c%c        &amp;quot;, 27, 32+7, 32+47);&lt;br /&gt;
  ngame=0;&lt;br /&gt;
  for(unsigned char i=0; i&amp;lt;pfsizeX; i++){   //clear any cells with active piece parts (will be written again with new pieceC&lt;br /&gt;
    for(unsigned char j=0; j&amp;lt;pfsizeY; j++){&lt;br /&gt;
      playfield[i][j]=0;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  lines=0;&lt;br /&gt;
  level=0;&lt;br /&gt;
  score=0;&lt;br /&gt;
  lslvi=0;&lt;br /&gt;
  fdrop=0;&lt;br /&gt;
  nextpiece();&lt;br /&gt;
  nextpiece();&lt;br /&gt;
  recopyscreen();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void piece2bg(){ //will copy active piece into background and check for lines and game over&lt;br /&gt;
  for(unsigned char i=0; i&amp;lt;4; i++){ //copy piece onto background&lt;br /&gt;
  for(unsigned char j=0; j&amp;lt;4; j++){&lt;br /&gt;
    if(pieceC[i][j]!=0){playfield[i+pieceCX][j+pieceCY]=pieceC[i][j]+8;} //copy the piece into the playfield/background &lt;br /&gt;
  }}&lt;br /&gt;
  nextpiece();&lt;br /&gt;
  &lt;br /&gt;
  //check for line clears&lt;br /&gt;
  unsigned char clearline[pfsizeY];&lt;br /&gt;
  unsigned char clearedlines=0;&lt;br /&gt;
  unsigned char templines=0;&lt;br /&gt;
  for(unsigned char j=4; j&amp;lt;pfsizeY; j++){&lt;br /&gt;
  clearline[j]=1; //assume line is cleared&lt;br /&gt;
  for(unsigned char i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
    if(playfield[i][j]&amp;lt;=7){clearline[j]=0;break;} //line is not full&lt;br /&gt;
  }&lt;br /&gt;
  clearedlines+=clearline[j];&lt;br /&gt;
  templines+=clearline[j];&lt;br /&gt;
  }&lt;br /&gt;
  if(clearedlines&amp;gt;0){//breifly animate the cleared lines, then clear them&lt;br /&gt;
  &lt;br /&gt;
    for(unsigned char f=0; f&amp;lt;=4; f++){&lt;br /&gt;
    for(unsigned char j=4; j&amp;lt;pfsizeY; j++){&lt;br /&gt;
        if(clearline[j]==1){&lt;br /&gt;
          for(unsigned char i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
			  cdraw(i,j-4,f%2);&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      t_delay(2000);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    for(unsigned char j=4; j&amp;lt;pfsizeY; j++){ //accutally clear the lines&lt;br /&gt;
       if(clearline[j]==1){&lt;br /&gt;
        for(unsigned char t=j; t&amp;gt;=4; t--){&lt;br /&gt;
         for(unsigned char i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
           playfield[i][t]=playfield[i][t-1];&lt;br /&gt;
         }&lt;br /&gt;
        }&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
   lines+=templines; //add number of lines to lines counter&lt;br /&gt;
   switch(templines){ //calculate score&lt;br /&gt;
    case 1:&lt;br /&gt;
      score+=40*(level+1);&lt;br /&gt;
      break;&lt;br /&gt;
    case 2:&lt;br /&gt;
      score+=100*(level+1);&lt;br /&gt;
      break;&lt;br /&gt;
    case 3:&lt;br /&gt;
      score+=300*(level+1);&lt;br /&gt;
      break;&lt;br /&gt;
    default:&lt;br /&gt;
      score+=1200*(level+1);&lt;br /&gt;
      break;&lt;br /&gt;
   }&lt;br /&gt;
   for(unsigned char i=1; i&amp;lt;=templines; i++){ //see if the level needs increasing&lt;br /&gt;
    lslvi++;&lt;br /&gt;
    if(lslvi&amp;gt;=10){lslvi=0;level++;}&lt;br /&gt;
    if(level&amp;gt;=maxlevel){level=maxlevel;}&lt;br /&gt;
   }&lt;br /&gt;
  }&lt;br /&gt;
  //check gameover (scan through line 3 and see if there are any non-active pieces in it)&lt;br /&gt;
  for(unsigned char i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
    if(playfield[i][3]&amp;gt;7){&lt;br /&gt;
      //*********************GAME OVER********************&lt;br /&gt;
      //Serial.println(F(&amp;quot;GAME OVER&amp;quot;));&lt;br /&gt;
	  printf(&amp;quot;%cY%c%cGAME OVER&amp;quot;, 27, 32+14, 32+40);&lt;br /&gt;
      while(ngame==0){&lt;br /&gt;
        controls();&lt;br /&gt;
      }&lt;br /&gt;
      newgamemon();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  recopyscreen();&lt;br /&gt;
	  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void main(){&lt;br /&gt;
	printf(&amp;quot;%cE&amp;quot;, 27);&lt;br /&gt;
	printf(&amp;quot;%cY%c%cSoviet Bloc Game 2019&amp;quot;, 27, 32+1, 32+40);&lt;br /&gt;
	printf(&amp;quot;%cY%c%cScore:&amp;quot;, 27, 32+3, 32+40);&lt;br /&gt;
	printf(&amp;quot;%cY%c%cLevel:&amp;quot;, 27, 32+5, 32+40);&lt;br /&gt;
	printf(&amp;quot;%cY%c%cLines:&amp;quot;, 27, 32+7, 32+40);&lt;br /&gt;
	printf(&amp;quot;%cY%c%cNext&amp;quot;, 27, 32+4, 32+30);&lt;br /&gt;
	&lt;br /&gt;
	printf(&amp;quot;%cY%c%cJ K L to move&amp;quot;, 27, 32+10, 32+32);&lt;br /&gt;
	printf(&amp;quot;%cY%c%c A S  to rotate&amp;quot;, 27, 32+11, 32+32);&lt;br /&gt;
	printf(&amp;quot;%cY%c%c  @   to start new game&amp;quot;, 27, 32+12, 32+32);&lt;br /&gt;
	&lt;br /&gt;
	drawb(pfX-2, pfY-1, (pfsizeX+1)*clspX, ((pfsizeY-3)*clspY));&lt;br /&gt;
	&lt;br /&gt;
&lt;br /&gt;
	ngame=1;&lt;br /&gt;
	newgamemon();&lt;br /&gt;
	while(1){ //*********************MAIN*PROGRAM*LOOP**************************&lt;br /&gt;
  &lt;br /&gt;
  //normally a frame delay would go here, but the trs-80 is slow and we need all the speed we can get.&lt;br /&gt;
  &lt;br /&gt;
  unsigned char droppiece=0;&lt;br /&gt;
  dropframe++;&lt;br /&gt;
  if(dropframe&amp;gt;=(maxlevel-level)){&lt;br /&gt;
    dropframe=0;&lt;br /&gt;
    droppiece=1;&lt;br /&gt;
  }&lt;br /&gt;
  newgamemon(); &lt;br /&gt;
  &lt;br /&gt;
  if(lvladd!=0){&lt;br /&gt;
	  level+=lvladd;&lt;br /&gt;
	  lvladd=0;&lt;br /&gt;
	  recopyscreen();&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  if(newpc!=0){&lt;br /&gt;
	  newpc=0;&lt;br /&gt;
	  nextpiece();&lt;br /&gt;
	  recopyscreen();&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  if(rcontrol==1){&lt;br /&gt;
    pieceR++; if(pieceR&amp;gt;=5){pieceR=1;} //try to rotate piece&lt;br /&gt;
    if(checkCollide()){&lt;br /&gt;
      pieceR--; if(pieceR&amp;lt;=0){pieceR=4;} //undo rotation&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(rcontrol==2){&lt;br /&gt;
    pieceR--; if(pieceR&amp;lt;=0){pieceR=4;}&lt;br /&gt;
    if(checkCollide()){&lt;br /&gt;
      pieceR++; if(pieceR&amp;gt;=5){pieceR=1;}&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(dcontrol==4){&lt;br /&gt;
    pieceCX--; //try and see what happens if we move the piece left&lt;br /&gt;
    if(checkCollide()){//piece move is not valid&lt;br /&gt;
      pieceCX++; //take piece back&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(dcontrol==6){&lt;br /&gt;
    pieceCX++; //try and see what happens if we move the piece left&lt;br /&gt;
    if(checkCollide()){//piece move is not valid&lt;br /&gt;
      pieceCX--; //take piece back&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(dcontrol==8){&lt;br /&gt;
    pieceCY--; //try and see what happens if we move the piece up&lt;br /&gt;
    if(checkCollide()){//piece move is not valid&lt;br /&gt;
      pieceCY++; //take piece back&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(dcontrol==2||droppiece==1){&lt;br /&gt;
    pieceCY++; //try and see what happens if we move the piece down&lt;br /&gt;
    if(!(drepeatframe&amp;lt;=drepeatframes)){ //is in fast mode&lt;br /&gt;
      fdrop++;&lt;br /&gt;
    }else{&lt;br /&gt;
      fdrop=0;&lt;br /&gt;
    }&lt;br /&gt;
    if(checkCollide()){//piece move is not valid&lt;br /&gt;
      pieceCY--; //take piece back&lt;br /&gt;
      score+=fdrop; //add # of fast dropped blocks to score&lt;br /&gt;
      fdrop=0;&lt;br /&gt;
      piece2bg(); //copy piece to background and reset to next piece&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  clearControls();&lt;br /&gt;
  mdraw();&lt;br /&gt;
}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
==Nintendo DS version==&lt;br /&gt;
'''This is ridiculous. We took the TRS-80 version and developed it for a platform made 27 years later. And it works.'''&lt;br /&gt;
[[File:Wilson-tetris-NDS.png|thumb|Nintendo DS Version running. Note the split screens, one text, one graphics. The DS gives you almost nothing built in, it expects all game data to be provided in the game (including fonts and images), and there are only a handful included with ndslib. For me, that means one screen with graphics, and one with monochrome text.]]&lt;br /&gt;
Sure, there are already plenty of games available for the Nintendo DS, but if all you have is a Soviet Bloc Game clone, everything starts to look like a nail. The NDS defines handheld gaming for the children of my generation, so when I found one at my local rummage sale and paired it up with a generic China Special flash card so it could run programs off a micro sd card, I knew it had to be done.&lt;br /&gt;
&lt;br /&gt;
This is honestly the first time I have worked/played with an actual games machine since I was first learning to hack my Wii around 2010, and will be the first time I write a homebrew app of any kind. &lt;br /&gt;
&lt;br /&gt;
My port uses libnds (or is that ndslib?), compiled with the devkitPro enviroment. There isn't much development on the NDS nowadays, but even in 2019 I found updated instructions for installing an up to date development enviroment.&lt;br /&gt;
&lt;br /&gt;
I learned that the nds has a ton of fancy game featrues (as you might expect), like sprites and special memory hardware that can make really fast copies of some things. It also has two seperate processors, which do completely diferent things (update the screen and check the inputs, for example) yet still make a complete system work. This is, needless to say, much more complicated than the things I usually work on.&lt;br /&gt;
&lt;br /&gt;
However, I decided to forgo all the low level work and keep things simple. There is one video mode which, instead of making up sprites or loading in complex background images, is just that you write to a certain spot in memory with a certain value, and it turns a certain pixel of the screen a certain color (aka framebuffer mode). This is really easy to work with, although there are no included commands to draw lines or crcles or anything like that, so I added a few functions for this (although all I need to draw is boxes, which does make it somewhat easier.) This mode is also a lot slower than what the Ds is truely caplable of doing apparently, but I found it was more than fast enough to make Soviet Bloc Game work correctly. &lt;br /&gt;
&lt;br /&gt;
The DS is also equipped with color screens, so I got to try coloring the blocks for the first time; this too was very easy, since I had kept track of which block belongs to which piece type, all I had to do was assign a color to each piece when going through and printing the playfield matrix. In doing this, I left out a bit of the code from the TRS-80 version that only plots things on the screen where it needs to be changed (this was absolutely nessacary in the TRS-80 version to get a playable game), but I found that the DS was so fast that it didn't matter at all. So now the entire piece carrier gets redrawn instead of just those tiles which have changed, but I could probably redraw the entire playfield too without any issue at all because speed is very high.&lt;br /&gt;
&lt;br /&gt;
Speaking of speed, this ended up being a problem. In my two older versions, gameplay was quite slow. On the DS, I needed to introduce a lockout so that the next piece would not immediatly drop down while still holding the DOWN key. Then, it was a matter of adding frame delays to make a game that was not too fast (though I did increase the number of levels (speedup as lines are cleared) for faster gameplay as you keep playing).&lt;br /&gt;
&lt;br /&gt;
Other than that, the port went smoothly, and as usual the hardest parts were trying to figure out how to get inputs from the buttons and how to draw blocks on the screen. Once I got that done I had a working version of Soviet Bloc Game about 30 seconds later, and the rest was fine tuning it.&lt;br /&gt;
&lt;br /&gt;
The last step is, hey, the DS has speakers. The example code includes a way to play MOD-like tracker files (XM, S3M, and others). So, why not finish it out with a poorly rendered Midi version of All Star by Smash Mouth? This was literally a matter of adding about 5 lines to my existing code, and disabling a couple things in the setup that it turned out I wasn't using anyways. The player takes everything in the background, while playing Soviet Bloc Game still. It uses some hardware (interrupts, timers and the like) so you can't drop it into just any source code you've got, but for one like Soviet Bloc Game that barely uses any special hardware it works fine.&lt;br /&gt;
&lt;br /&gt;
==IBM-PC/DOS Version==&lt;br /&gt;
I got a bunch of 5-1/4 floppy drives recently and used them to fix the first computer I learned to program - the Sanyo MBC-550. This 1983 computer comes with MS-DOS and BASIC, but is not IBM-PC compatible (no CGA card, for instance, instead having it's own internal color graphics). As such, it has very little games availible for it.&lt;br /&gt;
&lt;br /&gt;
Since I had never written any code for a 8088 based system, and I have five computers that use a pre-286 CPU, I decided to port my Soviet Bloc Game to it. GCC does not work for anything below a 386, but Open WATCOM does, so that's what I used. &lt;br /&gt;
&lt;br /&gt;
This C comiler comes with modes for choosing text modes, but like most IBM PC software, it directly accesses the hardware to make it work. This makes things incompatible with the Sanyo. The Sanyo does support standard BIOS Interrupt calls, which are slower than the direct hardware calls but are fast enough for Soviet Bloc Game. However, I discovered that C functions to do text manipulation are too slow for Soviet Bloc Game, at least on a slow system like the Sanyo. So, the sections to write graphics and also read keys on the keyboard are done in Assembly. For proper timing between system of different clock speeds, I used the system tick - Int 1aH, which increments a counter about 18.2 times per second. &lt;br /&gt;
&lt;br /&gt;
So now, there is a version of my Soviet Bloc Game that works on pretty much any computer that runs MS-DOS.&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
Here is a link to the source files and a compiled version: [[File:Wilson-tetris-NDS-t2.zip]]. You can build this just by running &amp;lt;code&amp;gt;make&amp;lt;/code&amp;gt; from within the devkitPro console.&lt;br /&gt;
&lt;br /&gt;
[[File:Wilson-tetris-PC.zip]] - the DOS/PC version&lt;br /&gt;
&lt;br /&gt;
[[File:alnwlsn-sbg-trs80.dmk]] - (by popular demand) TRS-80 disk image with /CMD binary&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=Soviet_Bloc_Game_(C_version)&amp;diff=604</id>
		<title>Soviet Bloc Game (C version)</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=Soviet_Bloc_Game_(C_version)&amp;diff=604"/>
		<updated>2023-09-20T20:25:59Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:Wilson-tetris-trs80.png|thumb|Soviet Bloc Game v1.1 running in a trs-80 emulator]]&lt;br /&gt;
While thinking about ways to make my TRS-80 model I more useable in 2019, I realized that I could not easily find a version of Tetris for it (probably because the TRS-80 came out about 10ish years before Tetris was invented), so I thought I might try to write my own after watching an episode of ''the 8-bit Guy'' where David does the same. I used to play the Gameboy version of Tetris all the time on my Ti-nSpire calculator thanks to the nspire hacking scene and an emulator port, so that's the style of Tetris I will try to recreate.&lt;br /&gt;
&lt;br /&gt;
However, I have written few games, and never done a Tetris-like clone before, so I approach this mostly as a challenge.&lt;br /&gt;
&lt;br /&gt;
Logal notice: &amp;quot;Tetris&amp;quot; is a trademark of The Tetris Company (who have been known to take down unauthorized trademark versions before: https://github.com/jdah/tetris-os), so I have decided to rename this clone &amp;quot;Soviet Bloc Game&amp;quot;&lt;br /&gt;
&lt;br /&gt;
'''Objectives:'''&lt;br /&gt;
* Playfield the same size as GB version&lt;br /&gt;
* in color?&lt;br /&gt;
* same or similar scoring system to GB version&lt;br /&gt;
* same rotation style as GB version&lt;br /&gt;
* easily portable to different platforms (that are C like)&lt;br /&gt;
* somewhat wellishly documented&lt;br /&gt;
&lt;br /&gt;
This ended up being a sort of 24 hour challenge; I started this project at about 11pm yesterday, and by about the time of this page's creation I had created an Arduino version of Soviet Bloc Game that met most of these objectives.&lt;br /&gt;
&lt;br /&gt;
==Theory of Operation==&lt;br /&gt;
===Basics===&lt;br /&gt;
The basics of the game's functioning can be summed up with just a few components: a part that generates the  shapes, a playfield that contains already played pieces and open spots where the active piece can move and collision detection that says where the active piece can and can't go.&lt;br /&gt;
&lt;br /&gt;
The playfield size is adjustable, but in the gameboy version it's 10 wide by 18 tall. In my version I added an extra 4 lines on top (which are not to be displayed) for the new pieces to appear within. Within the program, it appears as a big 2D matrix called &amp;lt;code&amp;gt;playfield[][]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
====Drawing the pieces====&lt;br /&gt;
Rather than figure out all the complex rotations of the part, I hardcoded them (using the Nintendo rotation system as described at https://tetris.fandom.com/wiki/Nintendo_Rotation_System). The parts appear in a 4x4 piece container &amp;lt;code&amp;gt;pieceC[][]&amp;lt;/code&amp;gt; matrix. This is then overlaid onto the playfield, and can be moved around. Nonzero cells of the matrix define the shape of the piece, like:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 0|| 0|| 0|| 0&lt;br /&gt;
|-&lt;br /&gt;
| 0|| 5|| 0|| 0&lt;br /&gt;
|-&lt;br /&gt;
| 5|| 5|| 0|| 0&lt;br /&gt;
|-&lt;br /&gt;
| 0|| 5|| 0|| 0&lt;br /&gt;
|}&lt;br /&gt;
The numbers used to define the piece are unique to each piece, so if you write the display section to use each number as a color, you can have a color game.&lt;br /&gt;
&lt;br /&gt;
====Collision detection====&lt;br /&gt;
In the collision detection section, the overlaid piece carrier is moved as if you were to make that move, and then we check if any of the cells overlap with non-blank sections of the playfield, or if any part of the active piece lies outside the playfield area. If so, the move is undone, and you can decide what to do from there. If no collision occurs, the move is done for real and displayed the next time the screen is updated.&lt;br /&gt;
 &lt;br /&gt;
Usually, the collision detection is used to prevent motion if a piece or wall is in the way, but if the piece is attempting to move downwards, the piece is instead copied onto the playfield, and the numbers making up the piece are increased by 8. This is how the collision detection can differentiate between the active piece and dead pieces in the playfield.&lt;br /&gt;
&lt;br /&gt;
====Input conditioning====&lt;br /&gt;
I added some input conditioning as well. For rotations of the active piece, the move is not repeating, and you must release the rotation input and press it again for a rotation to happen again. For moves left, right, and down, a system similar to repeating keys on a computer keyboard is used, holding down the direction for a brief moment will make the action repeat. In the gameboy version, dropping a piece quickly will increase the score based on the number of tiles dropped while moving fast. This is also replicated (but probably not perfectly) in my version.&lt;br /&gt;
&lt;br /&gt;
===Details===&lt;br /&gt;
====Levels and speed====&lt;br /&gt;
I only update the screen every few moments. Call the time of one of these updates a '''frame'''. While the screen is waiting to be updated, it's a good time to check the controls and see what we need to do the next time the screen needs updating. All the processing of the moves and collisions happens just before the screen updates. Every few frames, the active piece drops one row. This is connected to the levels, which in my version go from 0-20, which I think is in line with the gb version. The drop happens every 20 frames at level 0, up to a delay of 0 frames at level 20. For every 10 lines that the user clears, the level is increased by 1, so that the game gets progressively faster, just like the gb version. &lt;br /&gt;
====Game over detection====&lt;br /&gt;
This one is pretty simple. If any non-active piece sections are found on the line above the top of the visible playfield, the game ends. Check this every frame.&lt;br /&gt;
====Line completion detection====&lt;br /&gt;
This is a little more complicated than the game over, but basically I check to see if any rows are completely filled in with dead pieces. I keep track of which lines are full (and how many of them there are), and animate the corresponding lines by having the whole row(s) flash for a moment. At this point I also add onto the number of completed lines counter displayed on the scoreboard. Then, I copy all the lines above the filled line down by one tile. Since this is done for each filled line in sequence, the effect moves the playfield down for all cases of any number of filled lines.  &lt;br /&gt;
====Scoring====&lt;br /&gt;
The number of filled lines cleared in one move (calculated above) is used in the scoring formula to add to the score. The full formula (according to the Nintendo scoring system) is:&lt;br /&gt;
* 1 line: 40*(level+1)&lt;br /&gt;
* 2 lines: 100*(level+1)&lt;br /&gt;
* 3 lines: 300*(level+1)&lt;br /&gt;
* 4 lines: 1200*(level+1) &lt;br /&gt;
&lt;br /&gt;
Additionally, the number of grid tiles that are dropped by the user pressing &amp;quot;down&amp;quot; (ie, soft drop), which I kept track of earlier, are also added to the score.&lt;br /&gt;
====New game====&lt;br /&gt;
I have a new game button also. When pressed (and the controls are checked), it picks new pieces, clears the level, score, and lines counters, and empties the playfield of dead pieces. Thus, a new game begins.&lt;br /&gt;
&lt;br /&gt;
==Hardware (Arduino version)==&lt;br /&gt;
I do most of my software development on arduino-like embedded systems, and am pretty familiar with I/O options on these devices (more so than on a stndard computer), so I used an arduino nano with a 128x64 st7920 monochrome LCD and the u8g2 libray for the display. For input, I wired up 3 buttons, 2 rotate and one new game, and an analog joystick (which I am using as a digital one) for directional moves.&lt;br /&gt;
&lt;br /&gt;
===Porting===&lt;br /&gt;
With any luck, my code can be ported to different platforms just my changing the section that displays the playfield on a screen, the input sections that take in the button presses, and maybe the delay sections. On the Arduino, my code takes up about 50% of the prgram storage space, and 50% of ram, with most of that being due to the u8g2 library. I'm not an expert on the super low-level processes on the arduino, nor do I think my game is the most effceiently coded, but my guess is that if the game works well on a system with as low specifications as the atmega328, it should be able to run on nearly anything else.&lt;br /&gt;
&lt;br /&gt;
==The Code (Arduino version)==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#define pfsizeX 10&lt;br /&gt;
#define pfsizeY 22 //note that top 4 lines are not drawn&lt;br /&gt;
uint8_t playfield[pfsizeX][pfsizeY];  //the game area&lt;br /&gt;
&lt;br /&gt;
uint8_t pieceC[4][4]; //piece container&lt;br /&gt;
int8_t pieceCX;  //location of upper left corner of piece container&lt;br /&gt;
int8_t pieceCY;&lt;br /&gt;
uint8_t pieceT; //type of piece&lt;br /&gt;
uint8_t pieceR; //rotation of piece &lt;br /&gt;
uint8_t nextpieceT; &lt;br /&gt;
uint8_t nextpieceR; &lt;br /&gt;
&lt;br /&gt;
uint8_t dcontrol; //locks in control every frame&lt;br /&gt;
uint8_t rcontrol;&lt;br /&gt;
&lt;br /&gt;
uint8_t lastdcontrol=0; //for &amp;quot;debouncing&amp;quot; of inputs&lt;br /&gt;
uint8_t lastrcontrol=0; &lt;br /&gt;
uint8_t drepeatframe=0;&lt;br /&gt;
#define drepeatframes 3 //wait _ frames before repeatedly going in one direction&lt;br /&gt;
&lt;br /&gt;
uint8_t dropframe=0;    //counter for number of frames between block drops&lt;br /&gt;
uint8_t level=0; //LEVEL decreases frame drop from 20 frames to 0 frames (levels 0 to 20)&lt;br /&gt;
&lt;br /&gt;
boolean ngame=0; //when set, starts new game&lt;br /&gt;
&lt;br /&gt;
uint16_t lines=0; //NUMBER OF LINES CLEARED&lt;br /&gt;
uint32_t score=0; //TOTAL SCORE (using NES rules)&lt;br /&gt;
&lt;br /&gt;
uint8_t fdrop;   //number of blocks that piece has been fast dropped&lt;br /&gt;
uint8_t lslvi=0; //lines since level increase (when this gets to 10, increase the level)&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;U8g2lib.h&amp;gt;&lt;br /&gt;
U8G2_ST7920_128X64_1_HW_SPI u8g2(U8G2_R0, /* CS=*/ 12, /* reset=*/ 8);&lt;br /&gt;
&lt;br /&gt;
void dispscreen(){&lt;br /&gt;
  u8g2.firstPage();  &lt;br /&gt;
  do {&lt;br /&gt;
    for(uint8_t j=4; j&amp;lt;pfsizeY; j++){ //draw screen&lt;br /&gt;
    for(uint8_t i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
      if(playfield[i][j]!=0){&lt;br /&gt;
        u8g2.drawBox(5*(j-4),5*(pfsizeX-1)-5*i,6,6);&lt;br /&gt;
      }else{&lt;br /&gt;
        u8g2.drawFrame(5*(j-4),5*(pfsizeX-1)-5*i,6,6);&lt;br /&gt;
        //if(j==3){u8g2.drawLine(5*j,5*(pfsizeX-1)-5*i,5*j+5,5*(pfsizeX-1)-5*i+5);}&lt;br /&gt;
      }&lt;br /&gt;
    }}&lt;br /&gt;
&lt;br /&gt;
  uint8_t temppieceT=pieceT; &lt;br /&gt;
  uint8_t temppieceR=pieceR;&lt;br /&gt;
  pieceT=nextpieceT;&lt;br /&gt;
  pieceR=nextpieceR;&lt;br /&gt;
  loadpiece(); &lt;br /&gt;
  for(uint8_t j=0; j&amp;lt;4; j++){ //draw next piece&lt;br /&gt;
  for(uint8_t i=0; i&amp;lt;4; i++){&lt;br /&gt;
    if(pieceC[i][j]!=0){&lt;br /&gt;
      u8g2.drawBox(5*(j+pfsizeY-3),5*(pfsizeX-1)-5*i,6,6);&lt;br /&gt;
    }else{&lt;br /&gt;
      u8g2.drawFrame(5*(j+pfsizeY-3),5*(pfsizeX-1)-5*i,6,6);&lt;br /&gt;
    }&lt;br /&gt;
  }}&lt;br /&gt;
  pieceT=temppieceT;&lt;br /&gt;
  pieceR=temppieceR;&lt;br /&gt;
  loadpiece();&lt;br /&gt;
&lt;br /&gt;
  u8g2.setFont(u8g2_font_6x10_tf);&lt;br /&gt;
  char zbuffer[32];&lt;br /&gt;
  sprintf(zbuffer, &amp;quot;N%d&amp;quot;, lines);&lt;br /&gt;
  u8g2.drawStr(0,60, zbuffer);&lt;br /&gt;
  sprintf(zbuffer, &amp;quot;L%d&amp;quot;, level);&lt;br /&gt;
  u8g2.drawStr(25,60, zbuffer);&lt;br /&gt;
  sprintf(zbuffer, &amp;quot;S%d&amp;quot;, score);&lt;br /&gt;
  u8g2.drawStr(50,60, zbuffer);  &lt;br /&gt;
  } while( u8g2.nextPage() );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void controls(){&lt;br /&gt;
  if(digitalRead(A3)==0){ngame=1;}&lt;br /&gt;
  if(dcontrol==0){&lt;br /&gt;
    if(analogRead(A7)&amp;lt;10){dcontrol=4;}&lt;br /&gt;
    if(analogRead(A7)&amp;gt;1014){dcontrol=6;}&lt;br /&gt;
    if(analogRead(A6)&amp;gt;1014){dcontrol=8;}&lt;br /&gt;
    if(analogRead(A6)&amp;lt;10){dcontrol=2;}&lt;br /&gt;
    if(dcontrol==0){drepeatframe=0;}&lt;br /&gt;
    if(dcontrol==lastdcontrol){          //short delay before fast motion&lt;br /&gt;
      if(drepeatframe&amp;lt;=drepeatframes){&lt;br /&gt;
        drepeatframe++;&lt;br /&gt;
        dcontrol=3; //lockout if within lockout period&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(rcontrol==0){&lt;br /&gt;
    if(digitalRead(A4)==0){rcontrol=1;}&lt;br /&gt;
    if(digitalRead(A5)==0){rcontrol=2;}&lt;br /&gt;
    if(rcontrol==lastrcontrol){rcontrol=3;}&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void clearControls(){&lt;br /&gt;
  if(rcontrol!=3){lastrcontrol=rcontrol;}&lt;br /&gt;
  if(dcontrol!=3){lastdcontrol=dcontrol;}&lt;br /&gt;
  rcontrol=0;&lt;br /&gt;
  dcontrol=0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void draw(){&lt;br /&gt;
  loadpiece(); //load piece into the piece carrier&lt;br /&gt;
  for(uint8_t i=0; i&amp;lt;pfsizeX; i++){   //clear any cells with active piece parts (will be written again with new pieceC&lt;br /&gt;
    for(uint8_t j=0; j&amp;lt;pfsizeY; j++){&lt;br /&gt;
      if(playfield[i][j]&amp;lt;=7){playfield[i][j]=0;}&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  for(uint8_t i=0; i&amp;lt;4; i++){ //copy active piece onto the playfield&lt;br /&gt;
    for(uint8_t j=0; j&amp;lt;4; j++){&lt;br /&gt;
      if(pieceCX+i&amp;gt;=0&amp;amp;&amp;amp;pieceCX+i&amp;lt;pfsizeX&amp;amp;&amp;amp;pieceCY+j&amp;gt;=0&amp;amp;&amp;amp;pieceCY+j&amp;lt;pfsizeY){//check if piece segment can be drawn on screen&lt;br /&gt;
        if(pieceC[i][j]!=0){playfield[i+pieceCX][j+pieceCY]=pieceC[i][j];}&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
boolean checkCollide(){ //move the piece carrier first, then check if anything collides&lt;br /&gt;
  loadpiece(); //load piece into the piece carrier&lt;br /&gt;
  boolean nonvalidity=0;&lt;br /&gt;
  for(uint8_t i=0; i&amp;lt;4; i++){ //run through all piece carrier cells&lt;br /&gt;
    for(uint8_t j=0; j&amp;lt;4; j++){&lt;br /&gt;
      if(pieceCX+i&amp;gt;=0&amp;amp;&amp;amp;pieceCX+i&amp;lt;pfsizeX&amp;amp;&amp;amp;pieceCY+j&amp;gt;=0&amp;amp;&amp;amp;pieceCY+j&amp;lt;pfsizeY){ //check if piece carrier segment can be drawn on screen&lt;br /&gt;
        if(pieceC[i][j]!=0&amp;amp;&amp;amp;playfield[i+pieceCX][j+pieceCY]&amp;gt;7){ //if both background and nonzero piece carrier segment collide&lt;br /&gt;
          nonvalidity=1;&lt;br /&gt;
        }&lt;br /&gt;
      }else{ //this segment of PC can't be drawn on the screen&lt;br /&gt;
        if(pieceC[i][j]!=0){ //a filled in segment would be drawn offscreen&lt;br /&gt;
          nonvalidity=1;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  return nonvalidity;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void piece2bg(){&lt;br /&gt;
  for(uint8_t i=0; i&amp;lt;4; i++){ //copy active piece onto the screen&lt;br /&gt;
  for(uint8_t j=0; j&amp;lt;4; j++){&lt;br /&gt;
    if(pieceC[i][j]!=0){playfield[i+pieceCX][j+pieceCY]=pieceC[i][j]+8;} //copy the piece into the playfield/background &lt;br /&gt;
  }}&lt;br /&gt;
  nextpiece();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void nextpiece(){//generate the next piece and move PC back to the top&lt;br /&gt;
  pieceT=nextpieceT;&lt;br /&gt;
  pieceR=nextpieceR;&lt;br /&gt;
  nextpieceT = rand()%7 + 1;&lt;br /&gt;
  nextpieceR = rand()%4 + 1;&lt;br /&gt;
  pieceCY=0; //move piece carrier back to the top of screen&lt;br /&gt;
  pieceCX=3;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void newgamemon(){&lt;br /&gt;
  if(ngame==0){return;}&lt;br /&gt;
  Serial.println(F(&amp;quot;NEW GAME&amp;quot;));&lt;br /&gt;
  ngame=0;&lt;br /&gt;
  for(uint8_t i=0; i&amp;lt;pfsizeX; i++){   //clear any cells with active piece parts (will be written again with new pieceC&lt;br /&gt;
    for(uint8_t j=0; j&amp;lt;pfsizeY; j++){&lt;br /&gt;
      playfield[i][j]=0;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  lines=0;&lt;br /&gt;
  level=0;&lt;br /&gt;
  score=0;&lt;br /&gt;
  lslvi=0;&lt;br /&gt;
  fdrop=0;&lt;br /&gt;
  nextpiece();&lt;br /&gt;
  nextpiece();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loadpiece(){ //hardcoded all pieces&lt;br /&gt;
  switch(pieceT){&lt;br /&gt;
    case 1: //long one&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=1; pieceC[1][2]=1; pieceC[2][2]=1; pieceC[3][2]=1;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0; &lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=1; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=1; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=0; pieceC[2][2]=1; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=1; pieceC[3][3]=0; &lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 2: //backwards L&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=2; pieceC[1][2]=2; pieceC[2][2]=2; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=2; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=2; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=2; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=2; pieceC[1][3]=2; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=2; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=2; pieceC[1][2]=2; pieceC[2][2]=2; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=2; pieceC[2][1]=2; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=2; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=2; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 3: //L&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=3; pieceC[1][2]=3; pieceC[2][2]=3; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=3; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=3; pieceC[1][1]=3; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=3; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=3; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=3; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=3; pieceC[1][2]=3; pieceC[2][2]=3; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=3; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=3; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=3; pieceC[2][3]=3; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 4: //s shape&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=4; pieceC[2][2]=4; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=4; pieceC[1][3]=4; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=4; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=4; pieceC[2][2]=4; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=4; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 5: //T shape&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=5; pieceC[1][2]=5; pieceC[2][2]=5; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=5; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=5; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=5; pieceC[1][2]=5; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=5; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=5; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=5; pieceC[1][2]=5; pieceC[2][2]=5; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=5; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=5; pieceC[2][2]=5; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=5; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 6: //reverse s&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=6; pieceC[1][2]=6; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=6; pieceC[2][3]=6; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=6; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=6; pieceC[2][2]=6; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=6; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 7: //square&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      default:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=7; pieceC[2][1]=7; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=7; pieceC[2][2]=7; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setup(){&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  u8g2.begin();&lt;br /&gt;
  pinMode(A4, INPUT_PULLUP);&lt;br /&gt;
  pinMode(A5, INPUT_PULLUP);&lt;br /&gt;
  pinMode(A3, INPUT_PULLUP); //new game button&lt;br /&gt;
&lt;br /&gt;
  ngame=1;&lt;br /&gt;
  newgamemon();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
  &lt;br /&gt;
  for(uint32_t h=0;h&amp;lt;=5;h++){//frame delay  //DELAY SECTION (BETWEEN FRAMES)&lt;br /&gt;
    delay(1);&lt;br /&gt;
    controls();&lt;br /&gt;
    newgamemon();&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  boolean droppiece=0;&lt;br /&gt;
  dropframe++;&lt;br /&gt;
  if(dropframe&amp;gt;=(20-level)){&lt;br /&gt;
    dropframe=0;&lt;br /&gt;
    droppiece=1;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  if(rcontrol==1){&lt;br /&gt;
    pieceR++; if(pieceR&amp;gt;=5){pieceR=1;} //try to rotate piece&lt;br /&gt;
    if(checkCollide()){&lt;br /&gt;
      pieceR--; if(pieceR&amp;lt;=0){pieceR=4;} //undo rotation&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(rcontrol==2){&lt;br /&gt;
    pieceR--; if(pieceR&amp;lt;=0){pieceR=4;}&lt;br /&gt;
    if(checkCollide()){&lt;br /&gt;
      pieceR++; if(pieceR&amp;gt;=5){pieceR=1;}&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(dcontrol==4){&lt;br /&gt;
    pieceCX--; //try and see what happens if we move the piece left&lt;br /&gt;
    if(checkCollide()){//piece move is not valid&lt;br /&gt;
      pieceCX++; //take piece back&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(dcontrol==6){&lt;br /&gt;
    pieceCX++; //try and see what happens if we move the piece left&lt;br /&gt;
    if(checkCollide()){//piece move is not valid&lt;br /&gt;
      pieceCX--; //take piece back&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(dcontrol==8){&lt;br /&gt;
    pieceCY--; //try and see what happens if we move the piece up&lt;br /&gt;
    if(checkCollide()){//piece move is not valid&lt;br /&gt;
      pieceCY++; //take piece back&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(dcontrol==2||droppiece==1){&lt;br /&gt;
    pieceCY++; //try and see what happens if we move the piece down&lt;br /&gt;
    if(!(drepeatframe&amp;lt;=drepeatframes)){ //is in fast mode&lt;br /&gt;
      fdrop++;&lt;br /&gt;
    }else{&lt;br /&gt;
      fdrop=0;&lt;br /&gt;
    }&lt;br /&gt;
    if(checkCollide()){//piece move is not valid&lt;br /&gt;
      pieceCY--; //take piece back&lt;br /&gt;
      score+=fdrop; //add # of fast dropped blocks to score&lt;br /&gt;
      fdrop=0;&lt;br /&gt;
      piece2bg(); //copy piece to background and reset to next piece&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  draw();&lt;br /&gt;
  dispscreen();&lt;br /&gt;
  &lt;br /&gt;
  clearControls();&lt;br /&gt;
&lt;br /&gt;
  //check for line clears&lt;br /&gt;
  boolean clearline[pfsizeY];&lt;br /&gt;
  uint8_t clearedlines=0;&lt;br /&gt;
  uint8_t templines=0;&lt;br /&gt;
  for(uint8_t j=4; j&amp;lt;pfsizeY; j++){&lt;br /&gt;
  clearline[j]=1; //assume line is cleared&lt;br /&gt;
  for(uint8_t i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
    if(playfield[i][j]&amp;lt;=7){clearline[j]=0;break;} //line is not full&lt;br /&gt;
  }&lt;br /&gt;
  clearedlines+=clearline[j];&lt;br /&gt;
  templines+=clearline[j];&lt;br /&gt;
  }&lt;br /&gt;
  if(clearedlines&amp;gt;0){//breifly animate the cleared lines, then clear them&lt;br /&gt;
    for(uint8_t f=0; f&amp;lt;=6; f++){&lt;br /&gt;
      for(uint8_t j=4; j&amp;lt;pfsizeY; j++){&lt;br /&gt;
        if(clearline[j]==1){&lt;br /&gt;
          for(uint8_t i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
            if(f%2==0){playfield[i][j]=8;}else{playfield[i][j]=0;}&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      draw();&lt;br /&gt;
      dispscreen();&lt;br /&gt;
      delay(200);&lt;br /&gt;
    }&lt;br /&gt;
    for(uint8_t j=4; j&amp;lt;pfsizeY; j++){ //accutally clear the lines&lt;br /&gt;
       if(clearline[j]==1){&lt;br /&gt;
        for(uint8_t t=j; t&amp;gt;=4; t--){&lt;br /&gt;
         for(uint8_t i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
           playfield[i][t]=playfield[i][t-1];&lt;br /&gt;
         }&lt;br /&gt;
        }&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
   lines+=templines; //add number of lines to lines counter&lt;br /&gt;
   switch(templines){ //calculate score&lt;br /&gt;
    case 1:&lt;br /&gt;
      score+=40*(level+1);&lt;br /&gt;
      break;&lt;br /&gt;
    case 2:&lt;br /&gt;
      score+=100*(level+1);&lt;br /&gt;
      break;&lt;br /&gt;
    case 3:&lt;br /&gt;
      score+=300*(level+1);&lt;br /&gt;
      break;&lt;br /&gt;
    default:&lt;br /&gt;
      score+=1200*(level+1);&lt;br /&gt;
      break;&lt;br /&gt;
   }&lt;br /&gt;
   for(uint8_t i=1; i&amp;lt;=templines; i++){ //see if the level needs increasing&lt;br /&gt;
    lslvi++;&lt;br /&gt;
    if(lslvi&amp;gt;=10){lslvi=0;level++;}&lt;br /&gt;
    if(level&amp;gt;=20){level=20;}&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
  //check gameover (scan through line 3 and see if there are any non-active pieces in it)&lt;br /&gt;
  for(uint8_t i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
    if(playfield[i][3]&amp;gt;7){&lt;br /&gt;
      //*********************GAME OVER********************&lt;br /&gt;
      Serial.println(F(&amp;quot;GAME OVER&amp;quot;));&lt;br /&gt;
      while(ngame==0){&lt;br /&gt;
        controls();&lt;br /&gt;
      }&lt;br /&gt;
      newgamemon();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  //check for completed lines&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
==The Code (TRS-80 version)==&lt;br /&gt;
I used the z88dk C compiler for the port. To compile, use &amp;lt;code&amp;gt;zcc  +trs80 -lndos -lm -create-app soviet-bloc-game.c -o soviet-bloc-game&amp;lt;/code&amp;gt; which will create a &amp;lt;code&amp;gt;.CAS&amp;lt;/code&amp;gt; file. Use &amp;lt;code&amp;gt;zcc  +trs80 -lndos -lm -create-app -subtype=disk soviet-bloc-game.c -o soviet-bloc-game&amp;lt;/code&amp;gt; to create a &amp;lt;code&amp;gt;.CMD&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Porting this to the TRS-80 turned out to be a much harder challange, mostly because the TRS-80 is a very slow, low power machine. Some of the improvements were obvious, like only checking the lines and game over only when an active piece becomes inactive. However, this isn't enough to make a playable version. The TRS-80 is slow enough that every comparison, operation, and graphics plot add noticable delay. Therefore, I only update parts of the display when needed (ie, the area around the active piece, 1 block around the piece carrier); the background is filled back in to erase piece moves, and the screen is only redrawn at the end when the active piece becomes inactive. This is possible because unlike the arduino version, the whole screen doesn't need to be rewitten every time it changes. I also had to take out any frame delay and reduce the levels to 10 because the machine is not fast enough to keep up. The version below is therefore clunky, but playable. The obvious next steps are to make an assembler version of Soviet Bloc Game.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//soviet-bloc-game.c - Soviet Bloc Game for TRS-80 - 05/28/2019&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt; &lt;br /&gt;
#include &amp;lt;graphics.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define pfsizeX 10 //playfield size (in cells)&lt;br /&gt;
#define pfsizeY 22 //note that top 4 lines are not drawn&lt;br /&gt;
#define pfX 8 //upper left corner of playfield location (pixel)&lt;br /&gt;
#define pfY 4&lt;br /&gt;
#define npX 60 //upper left corner of next piece indicator&lt;br /&gt;
#define npY 2&lt;br /&gt;
#define clspX 4 //spacing between cell elements (pixels)&lt;br /&gt;
#define clspY 2&lt;br /&gt;
&lt;br /&gt;
void controls(void);&lt;br /&gt;
&lt;br /&gt;
unsigned char playfield[pfsizeX][pfsizeY];  //the game area&lt;br /&gt;
unsigned char cscreen[pfsizeX][pfsizeY-4];  //a copy of what gets drawn on the screen (game area)&lt;br /&gt;
unsigned char pieceC[4][4]; //piece container&lt;br /&gt;
signed char pieceCX;  //location of upper left corner of piece container&lt;br /&gt;
signed char pieceCY;&lt;br /&gt;
unsigned char pieceT; //type of piece&lt;br /&gt;
unsigned char pieceR; //rotation of piece &lt;br /&gt;
unsigned char nextpieceT; &lt;br /&gt;
unsigned char nextpieceR; &lt;br /&gt;
&lt;br /&gt;
unsigned char dcontrol; //locks in control every frame&lt;br /&gt;
unsigned char rcontrol;&lt;br /&gt;
&lt;br /&gt;
unsigned char lastdcontrol=0; //for &amp;quot;debouncing&amp;quot; of inputs&lt;br /&gt;
unsigned char lastrcontrol=0; &lt;br /&gt;
unsigned char drepeatframe=0;&lt;br /&gt;
#define drepeatframes 3 //wait _ frames before repeatedly going in one direction&lt;br /&gt;
&lt;br /&gt;
unsigned char dropframe=0;    //counter for number of frames between block drops&lt;br /&gt;
unsigned char level=0; //LEVEL decreases frame drop from maxlevel frames to 0 frames (levels 0 to MAXLEVEL)&lt;br /&gt;
#define maxlevel 10&lt;br /&gt;
&lt;br /&gt;
unsigned char ngame=1; //when set, starts new game&lt;br /&gt;
unsigned char newpc=0; //when set, get a new piece&lt;br /&gt;
&lt;br /&gt;
unsigned int lines=0; //NUMBER OF LINES CLEARED&lt;br /&gt;
long score=0; //TOTAL SCORE (using NES rules)&lt;br /&gt;
&lt;br /&gt;
unsigned char fdrop;   //number of blocks that piece has been fast dropped&lt;br /&gt;
unsigned char lslvi=0; //lines since level increase (when this gets to 10, increase the level)&lt;br /&gt;
&lt;br /&gt;
char lvladd=0;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void setblock(char x, char y){&lt;br /&gt;
	for(char i=0; i&amp;lt;8; i++){&lt;br /&gt;
		controls();&lt;br /&gt;
     	plot(x+i%4,y+i/4);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
void clearblock(char x, char y){&lt;br /&gt;
	for(char i=0; i&amp;lt;8; i++){&lt;br /&gt;
		controls();&lt;br /&gt;
    	unplot(x+i%4,y+i/4);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
void cdraw(char x, char y, char color){//only draws on screen if it needs to (uses cell coordinates)&lt;br /&gt;
	if(cscreen[x][y]!=color){&lt;br /&gt;
		cscreen[x][y]=color;&lt;br /&gt;
		if(color!=0){&lt;br /&gt;
			setblock( (x)*clspX+pfX, (y)*clspY+pfY );&lt;br /&gt;
		}else{&lt;br /&gt;
			clearblock( (x)*clspX+pfX, (y)*clspY+pfY);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
void recopyscreen(){ //copies playfield to screen &lt;br /&gt;
  for(char i=0; i&amp;lt;pfsizeX; i++){ //manually redraw the entire screen&lt;br /&gt;
  for(char j=4; j&amp;lt;pfsizeY; j++){&lt;br /&gt;
	  cdraw(i,j-4,playfield[i][j]);&lt;br /&gt;
  }}&lt;br /&gt;
  printf(&amp;quot;%cY%c%c%ld&amp;quot;, 27, 32+3, 32+47, score);&lt;br /&gt;
  printf(&amp;quot;%cY%c%c%d&amp;quot;, 27, 32+5, 32+47, level);&lt;br /&gt;
  printf(&amp;quot;%cY%c%c%d&amp;quot;, 27, 32+7, 32+47, lines);&lt;br /&gt;
}&lt;br /&gt;
void controls(){&lt;br /&gt;
	/*&lt;br /&gt;
   if(bpeek(0x3802)&amp;amp;0b00000010){plot(126,46);}else{unplot(126,46);} //up button (I)&lt;br /&gt;
   if(bpeek(0x3802)&amp;amp;0b00000100){plot(125,47);}else{unplot(125,47);} //left button (J)&lt;br /&gt;
   if(bpeek(0x3802)&amp;amp;0b00001000){plot(126,47);}else{unplot(126,47);} //down button (K)&lt;br /&gt;
   if(bpeek(0x3802)&amp;amp;0b00010000){plot(127,47);}else{unplot(127,47);} //right button (L)&lt;br /&gt;
   if(bpeek(0x3801)&amp;amp;0b00000010){plot(121,47);}else{unplot(121,47);} //a button (A)&lt;br /&gt;
   if(bpeek(0x3804)&amp;amp;0b00001000){plot(122,47);}else{unplot(122,47);} //b button (S)&lt;br /&gt;
   if(bpeek(0x3801)&amp;amp;0b00000001){plot(119,47);}else{unplot(119,47);} //newgame (@)&lt;br /&gt;
	   */&lt;br /&gt;
  if(bpeek(0x3840)&amp;amp;0b00000100){exit(0);}&lt;br /&gt;
  if(bpeek(0x3810)&amp;amp;0b00000010){lvladd=1;}&lt;br /&gt;
  if(bpeek(0x3810)&amp;amp;0b00000001){lvladd=-1;}&lt;br /&gt;
  &lt;br /&gt;
  if(bpeek(0x3801)&amp;amp;0b00000001){ngame=1;}&lt;br /&gt;
  if(bpeek(0x3808)&amp;amp;0b00000010){newpc=1;}&lt;br /&gt;
  if(dcontrol==0){&lt;br /&gt;
    if(bpeek(0x3802)&amp;amp;0b00000100){dcontrol=4;}&lt;br /&gt;
    if(bpeek(0x3802)&amp;amp;0b00010000){dcontrol=6;}&lt;br /&gt;
    if(bpeek(0x3802)&amp;amp;0b00000010){dcontrol=8;}&lt;br /&gt;
    if(bpeek(0x3802)&amp;amp;0b00001000){dcontrol=2;}&lt;br /&gt;
    if(dcontrol==0){drepeatframe=0;}&lt;br /&gt;
    if(dcontrol==lastdcontrol){          //short delay before fast motion&lt;br /&gt;
      if(drepeatframe&amp;lt;=drepeatframes){&lt;br /&gt;
        drepeatframe++;&lt;br /&gt;
        dcontrol=3; //lockout if within lockout period&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(rcontrol==0){&lt;br /&gt;
    if(bpeek(0x3801)&amp;amp;0b00000010){rcontrol=1;}&lt;br /&gt;
    if(bpeek(0x3804)&amp;amp;0b00001000){rcontrol=2;}&lt;br /&gt;
    if(rcontrol==lastrcontrol){rcontrol=3;}&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
void clearControls(){&lt;br /&gt;
  if(rcontrol!=3){lastrcontrol=rcontrol;}&lt;br /&gt;
  if(dcontrol!=3){lastdcontrol=dcontrol;}&lt;br /&gt;
  rcontrol=0;&lt;br /&gt;
  dcontrol=0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void loadpiece(){ //hardcoded all pieces&lt;br /&gt;
  switch(pieceT){&lt;br /&gt;
    case 1: //long one&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=1; pieceC[1][2]=1; pieceC[2][2]=1; pieceC[3][2]=1;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0; &lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=1; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=1; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=0; pieceC[2][2]=1; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=1; pieceC[3][3]=0; &lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 2: //backwards L&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=2; pieceC[1][2]=2; pieceC[2][2]=2; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=2; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=2; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=2; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=2; pieceC[1][3]=2; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=2; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=2; pieceC[1][2]=2; pieceC[2][2]=2; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=2; pieceC[2][1]=2; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=2; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=2; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 3: //L&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=3; pieceC[1][2]=3; pieceC[2][2]=3; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=3; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=3; pieceC[1][1]=3; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=3; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=3; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=3; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=3; pieceC[1][2]=3; pieceC[2][2]=3; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=3; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=3; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=3; pieceC[2][3]=3; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 4: //s shape&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=4; pieceC[2][2]=4; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=4; pieceC[1][3]=4; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=4; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=4; pieceC[2][2]=4; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=4; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 5: //T shape&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=5; pieceC[1][2]=5; pieceC[2][2]=5; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=5; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=5; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=5; pieceC[1][2]=5; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=5; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=5; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=5; pieceC[1][2]=5; pieceC[2][2]=5; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=5; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=5; pieceC[2][2]=5; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=5; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 6: //reverse s&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=6; pieceC[1][2]=6; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=6; pieceC[2][3]=6; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=6; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=6; pieceC[2][2]=6; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=6; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 7: //square&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      default:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=7; pieceC[2][1]=7; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=7; pieceC[2][2]=7; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void nextpiece(){//generate the next piece and move PC back to the top&lt;br /&gt;
  unsigned char tempT=nextpieceT;&lt;br /&gt;
  unsigned char tempR=nextpieceR;&lt;br /&gt;
  nextpieceT = rand()%7 + 1;&lt;br /&gt;
  nextpieceR = rand()%4 + 1;&lt;br /&gt;
  pieceT=nextpieceT;&lt;br /&gt;
  pieceR=nextpieceR;&lt;br /&gt;
  loadpiece();&lt;br /&gt;
  for(char i=0; i&amp;lt;4; i++){&lt;br /&gt;
  for(char j=0; j&amp;lt;4; j++){&lt;br /&gt;
	  if(pieceC[i][j]!=0){&lt;br /&gt;
		  setblock((i)*clspX+npX, (j)*clspY+npY);&lt;br /&gt;
	  }else{&lt;br /&gt;
		  clearblock((i)*clspX+npX, (j)*clspY+npY);&lt;br /&gt;
	  }&lt;br /&gt;
  }}&lt;br /&gt;
  &lt;br /&gt;
  pieceT=tempT;&lt;br /&gt;
  pieceR=tempR;&lt;br /&gt;
  &lt;br /&gt;
  pieceCY=0; //move piece carrier back to the top of screen&lt;br /&gt;
  pieceCX=3;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void mdraw(){&lt;br /&gt;
  //printf(&amp;quot;%cY%c%c%d&amp;quot;, 27, 32+1, 32+55, fff); fff++;&lt;br /&gt;
  loadpiece(); //load piece into the piece carrier&lt;br /&gt;
  for(char i=0; i&amp;lt;6; i++){ //check piece carrier and one block offset outside it&lt;br /&gt;
  for(char j=0; j&amp;lt;6; j++){&lt;br /&gt;
	  controls();&lt;br /&gt;
	  if((pieceCX-1+i)&amp;gt;=0&amp;amp;&amp;amp;(pieceCX-1+i)&amp;lt;pfsizeX&amp;amp;&amp;amp;(pieceCY-1+j)&amp;gt;=4&amp;amp;&amp;amp;(pieceCY-1+j)&amp;lt;(pfsizeY)){//check if this segment be drawn on screen&lt;br /&gt;
		if(i&amp;gt;=1&amp;amp;&amp;amp;i&amp;lt;=4&amp;amp;&amp;amp;j&amp;gt;=1&amp;amp;&amp;amp;j&amp;lt;=4){//accessing piece carrier section here}&lt;br /&gt;
			if(pieceC[i-1][j-1]&amp;gt;0){//if piece carrier block is confirmed should be white&lt;br /&gt;
				cdraw(i-1+pieceCX,j-5+pieceCY,pieceC[i-1][j-1]);&lt;br /&gt;
			}else{&lt;br /&gt;
				cdraw(i-1+pieceCX,j-5+pieceCY,playfield[i-1+pieceCX][j-1+pieceCY]); //color it in with the existing playfield background&lt;br /&gt;
			}&lt;br /&gt;
		}else{&lt;br /&gt;
			cdraw(i-1+pieceCX,j-5+pieceCY,playfield[i-1+pieceCX][j-1+pieceCY]);&lt;br /&gt;
		}&lt;br /&gt;
	  }&lt;br /&gt;
  }}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
unsigned char checkCollide(){ //move the piece carrier first, then check if anything collides&lt;br /&gt;
  loadpiece(); //load piece into the piece carrier&lt;br /&gt;
  unsigned char nonvalidity=0;&lt;br /&gt;
  for(char i=0; i&amp;lt;4; i++){ //run through all piece carrier cells&lt;br /&gt;
    for(char j=0; j&amp;lt;4; j++){&lt;br /&gt;
      if(pieceCX+i&amp;gt;=0&amp;amp;&amp;amp;pieceCX+i&amp;lt;pfsizeX&amp;amp;&amp;amp;pieceCY+j&amp;gt;=0&amp;amp;&amp;amp;pieceCY+j&amp;lt;pfsizeY){ //check if piece carrier segment can be drawn on screen&lt;br /&gt;
        if(pieceC[i][j]!=0&amp;amp;&amp;amp;playfield[i+pieceCX][j+pieceCY]&amp;gt;7){ //if both background and nonzero piece carrier segment collide&lt;br /&gt;
          nonvalidity=1;&lt;br /&gt;
        }&lt;br /&gt;
      }else{ //this segment of PC can't be drawn on the screen&lt;br /&gt;
        if(pieceC[i][j]!=0){ //a filled in segment would be drawn offscreen&lt;br /&gt;
          nonvalidity=1;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  return nonvalidity;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void newgamemon(){&lt;br /&gt;
  if(ngame==0){return;}&lt;br /&gt;
  //Serial.println(F(&amp;quot;NEW GAME&amp;quot;));&lt;br /&gt;
  printf(&amp;quot;%cY%c%c         &amp;quot;, 27, 32+14, 32+40);&lt;br /&gt;
  printf(&amp;quot;%cY%c%c        &amp;quot;, 27, 32+3, 32+47);&lt;br /&gt;
  printf(&amp;quot;%cY%c%c        &amp;quot;, 27, 32+5, 32+47);&lt;br /&gt;
  printf(&amp;quot;%cY%c%c        &amp;quot;, 27, 32+7, 32+47);&lt;br /&gt;
  ngame=0;&lt;br /&gt;
  for(unsigned char i=0; i&amp;lt;pfsizeX; i++){   //clear any cells with active piece parts (will be written again with new pieceC&lt;br /&gt;
    for(unsigned char j=0; j&amp;lt;pfsizeY; j++){&lt;br /&gt;
      playfield[i][j]=0;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  lines=0;&lt;br /&gt;
  level=0;&lt;br /&gt;
  score=0;&lt;br /&gt;
  lslvi=0;&lt;br /&gt;
  fdrop=0;&lt;br /&gt;
  nextpiece();&lt;br /&gt;
  nextpiece();&lt;br /&gt;
  recopyscreen();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void piece2bg(){ //will copy active piece into background and check for lines and game over&lt;br /&gt;
  for(unsigned char i=0; i&amp;lt;4; i++){ //copy piece onto background&lt;br /&gt;
  for(unsigned char j=0; j&amp;lt;4; j++){&lt;br /&gt;
    if(pieceC[i][j]!=0){playfield[i+pieceCX][j+pieceCY]=pieceC[i][j]+8;} //copy the piece into the playfield/background &lt;br /&gt;
  }}&lt;br /&gt;
  nextpiece();&lt;br /&gt;
  &lt;br /&gt;
  //check for line clears&lt;br /&gt;
  unsigned char clearline[pfsizeY];&lt;br /&gt;
  unsigned char clearedlines=0;&lt;br /&gt;
  unsigned char templines=0;&lt;br /&gt;
  for(unsigned char j=4; j&amp;lt;pfsizeY; j++){&lt;br /&gt;
  clearline[j]=1; //assume line is cleared&lt;br /&gt;
  for(unsigned char i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
    if(playfield[i][j]&amp;lt;=7){clearline[j]=0;break;} //line is not full&lt;br /&gt;
  }&lt;br /&gt;
  clearedlines+=clearline[j];&lt;br /&gt;
  templines+=clearline[j];&lt;br /&gt;
  }&lt;br /&gt;
  if(clearedlines&amp;gt;0){//breifly animate the cleared lines, then clear them&lt;br /&gt;
  &lt;br /&gt;
    for(unsigned char f=0; f&amp;lt;=4; f++){&lt;br /&gt;
    for(unsigned char j=4; j&amp;lt;pfsizeY; j++){&lt;br /&gt;
        if(clearline[j]==1){&lt;br /&gt;
          for(unsigned char i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
			  cdraw(i,j-4,f%2);&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      t_delay(2000);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    for(unsigned char j=4; j&amp;lt;pfsizeY; j++){ //accutally clear the lines&lt;br /&gt;
       if(clearline[j]==1){&lt;br /&gt;
        for(unsigned char t=j; t&amp;gt;=4; t--){&lt;br /&gt;
         for(unsigned char i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
           playfield[i][t]=playfield[i][t-1];&lt;br /&gt;
         }&lt;br /&gt;
        }&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
   lines+=templines; //add number of lines to lines counter&lt;br /&gt;
   switch(templines){ //calculate score&lt;br /&gt;
    case 1:&lt;br /&gt;
      score+=40*(level+1);&lt;br /&gt;
      break;&lt;br /&gt;
    case 2:&lt;br /&gt;
      score+=100*(level+1);&lt;br /&gt;
      break;&lt;br /&gt;
    case 3:&lt;br /&gt;
      score+=300*(level+1);&lt;br /&gt;
      break;&lt;br /&gt;
    default:&lt;br /&gt;
      score+=1200*(level+1);&lt;br /&gt;
      break;&lt;br /&gt;
   }&lt;br /&gt;
   for(unsigned char i=1; i&amp;lt;=templines; i++){ //see if the level needs increasing&lt;br /&gt;
    lslvi++;&lt;br /&gt;
    if(lslvi&amp;gt;=10){lslvi=0;level++;}&lt;br /&gt;
    if(level&amp;gt;=maxlevel){level=maxlevel;}&lt;br /&gt;
   }&lt;br /&gt;
  }&lt;br /&gt;
  //check gameover (scan through line 3 and see if there are any non-active pieces in it)&lt;br /&gt;
  for(unsigned char i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
    if(playfield[i][3]&amp;gt;7){&lt;br /&gt;
      //*********************GAME OVER********************&lt;br /&gt;
      //Serial.println(F(&amp;quot;GAME OVER&amp;quot;));&lt;br /&gt;
	  printf(&amp;quot;%cY%c%cGAME OVER&amp;quot;, 27, 32+14, 32+40);&lt;br /&gt;
      while(ngame==0){&lt;br /&gt;
        controls();&lt;br /&gt;
      }&lt;br /&gt;
      newgamemon();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  recopyscreen();&lt;br /&gt;
	  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void main(){&lt;br /&gt;
	printf(&amp;quot;%cE&amp;quot;, 27);&lt;br /&gt;
	printf(&amp;quot;%cY%c%cSoviet Bloc Game 2019&amp;quot;, 27, 32+1, 32+40);&lt;br /&gt;
	printf(&amp;quot;%cY%c%cScore:&amp;quot;, 27, 32+3, 32+40);&lt;br /&gt;
	printf(&amp;quot;%cY%c%cLevel:&amp;quot;, 27, 32+5, 32+40);&lt;br /&gt;
	printf(&amp;quot;%cY%c%cLines:&amp;quot;, 27, 32+7, 32+40);&lt;br /&gt;
	printf(&amp;quot;%cY%c%cNext&amp;quot;, 27, 32+4, 32+30);&lt;br /&gt;
	&lt;br /&gt;
	printf(&amp;quot;%cY%c%cJ K L to move&amp;quot;, 27, 32+10, 32+32);&lt;br /&gt;
	printf(&amp;quot;%cY%c%c A S  to rotate&amp;quot;, 27, 32+11, 32+32);&lt;br /&gt;
	printf(&amp;quot;%cY%c%c  @   to start new game&amp;quot;, 27, 32+12, 32+32);&lt;br /&gt;
	&lt;br /&gt;
	drawb(pfX-2, pfY-1, (pfsizeX+1)*clspX, ((pfsizeY-3)*clspY));&lt;br /&gt;
	&lt;br /&gt;
&lt;br /&gt;
	ngame=1;&lt;br /&gt;
	newgamemon();&lt;br /&gt;
	while(1){ //*********************MAIN*PROGRAM*LOOP**************************&lt;br /&gt;
  &lt;br /&gt;
  //normally a frame delay would go here, but the trs-80 is slow and we need all the speed we can get.&lt;br /&gt;
  &lt;br /&gt;
  unsigned char droppiece=0;&lt;br /&gt;
  dropframe++;&lt;br /&gt;
  if(dropframe&amp;gt;=(maxlevel-level)){&lt;br /&gt;
    dropframe=0;&lt;br /&gt;
    droppiece=1;&lt;br /&gt;
  }&lt;br /&gt;
  newgamemon(); &lt;br /&gt;
  &lt;br /&gt;
  if(lvladd!=0){&lt;br /&gt;
	  level+=lvladd;&lt;br /&gt;
	  lvladd=0;&lt;br /&gt;
	  recopyscreen();&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  if(newpc!=0){&lt;br /&gt;
	  newpc=0;&lt;br /&gt;
	  nextpiece();&lt;br /&gt;
	  recopyscreen();&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  if(rcontrol==1){&lt;br /&gt;
    pieceR++; if(pieceR&amp;gt;=5){pieceR=1;} //try to rotate piece&lt;br /&gt;
    if(checkCollide()){&lt;br /&gt;
      pieceR--; if(pieceR&amp;lt;=0){pieceR=4;} //undo rotation&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(rcontrol==2){&lt;br /&gt;
    pieceR--; if(pieceR&amp;lt;=0){pieceR=4;}&lt;br /&gt;
    if(checkCollide()){&lt;br /&gt;
      pieceR++; if(pieceR&amp;gt;=5){pieceR=1;}&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(dcontrol==4){&lt;br /&gt;
    pieceCX--; //try and see what happens if we move the piece left&lt;br /&gt;
    if(checkCollide()){//piece move is not valid&lt;br /&gt;
      pieceCX++; //take piece back&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(dcontrol==6){&lt;br /&gt;
    pieceCX++; //try and see what happens if we move the piece left&lt;br /&gt;
    if(checkCollide()){//piece move is not valid&lt;br /&gt;
      pieceCX--; //take piece back&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(dcontrol==8){&lt;br /&gt;
    pieceCY--; //try and see what happens if we move the piece up&lt;br /&gt;
    if(checkCollide()){//piece move is not valid&lt;br /&gt;
      pieceCY++; //take piece back&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(dcontrol==2||droppiece==1){&lt;br /&gt;
    pieceCY++; //try and see what happens if we move the piece down&lt;br /&gt;
    if(!(drepeatframe&amp;lt;=drepeatframes)){ //is in fast mode&lt;br /&gt;
      fdrop++;&lt;br /&gt;
    }else{&lt;br /&gt;
      fdrop=0;&lt;br /&gt;
    }&lt;br /&gt;
    if(checkCollide()){//piece move is not valid&lt;br /&gt;
      pieceCY--; //take piece back&lt;br /&gt;
      score+=fdrop; //add # of fast dropped blocks to score&lt;br /&gt;
      fdrop=0;&lt;br /&gt;
      piece2bg(); //copy piece to background and reset to next piece&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  clearControls();&lt;br /&gt;
  mdraw();&lt;br /&gt;
}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
==Nintendo DS version==&lt;br /&gt;
'''This is ridiculous. We took the TRS-80 version and developed it for a platform made 27 years later. And it works.'''&lt;br /&gt;
[[File:Wilson-tetris-NDS.png|thumb|Nintendo DS Version running. Note the split screens, one text, one graphics. The DS gives you almost nothing built in, it expects all game data to be provided in the game (including fonts and images), and there are only a handful included with ndslib. For me, that means one screen with graphics, and one with monochrome text.]]&lt;br /&gt;
Sure, there are already plenty of games available for the Nintendo DS, but if all you have is a Soviet Bloc Game clone, everything starts to look like a nail. The NDS defines handheld gaming for the children of my generation, so when I found one at my local rummage sale and paired it up with a generic China Special flash card so it could run programs off a micro sd card, I knew it had to be done.&lt;br /&gt;
&lt;br /&gt;
This is honestly the first time I have worked/played with an actual games machine since I was first learning to hack my Wii around 2010, and will be the first time I write a homebrew app of any kind. &lt;br /&gt;
&lt;br /&gt;
My port uses libnds (or is that ndslib?), compiled with the devkitPro enviroment. There isn't much development on the NDS nowadays, but even in 2019 I found updated instructions for installing an up to date development enviroment.&lt;br /&gt;
&lt;br /&gt;
I learned that the nds has a ton of fancy game featrues (as you might expect), like sprites and special memory hardware that can make really fast copies of some things. It also has two seperate processors, which do completely diferent things (update the screen and check the inputs, for example) yet still make a complete system work. This is, needless to say, much more complicated than the things I usually work on.&lt;br /&gt;
&lt;br /&gt;
However, I decided to forgo all the low level work and keep things simple. There is one video mode which, instead of making up sprites or loading in complex background images, is just that you write to a certain spot in memory with a certain value, and it turns a certain pixel of the screen a certain color (aka framebuffer mode). This is really easy to work with, although there are no included commands to draw lines or crcles or anything like that, so I added a few functions for this (although all I need to draw is boxes, which does make it somewhat easier.) This mode is also a lot slower than what the Ds is truely caplable of doing apparently, but I found it was more than fast enough to make Soviet Bloc Game work correctly. &lt;br /&gt;
&lt;br /&gt;
The DS is also equipped with color screens, so I got to try coloring the blocks for the first time; this too was very easy, since I had kept track of which block belongs to which piece type, all I had to do was assign a color to each piece when going through and printing the playfield matrix. In doing this, I left out a bit of the code from the TRS-80 version that only plots things on the screen where it needs to be changed (this was absolutely nessacary in the TRS-80 version to get a playable game), but I found that the DS was so fast that it didn't matter at all. So now the entire piece carrier gets redrawn instead of just those tiles which have changed, but I could probably redraw the entire playfield too without any issue at all because speed is very high.&lt;br /&gt;
&lt;br /&gt;
Speaking of speed, this ended up being a problem. In my two older versions, gameplay was quite slow. On the DS, I needed to introduce a lockout so that the next piece would not immediatly drop down while still holding the DOWN key. Then, it was a matter of adding frame delays to make a game that was not too fast (though I did increase the number of levels (speedup as lines are cleared) for faster gameplay as you keep playing).&lt;br /&gt;
&lt;br /&gt;
Other than that, the port went smoothly, and as usual the hardest parts were trying to figure out how to get inputs from the buttons and how to draw blocks on the screen. Once I got that done I had a working version of Soviet Bloc Game about 30 seconds later, and the rest was fine tuning it.&lt;br /&gt;
&lt;br /&gt;
The last step is, hey, the DS has speakers. The example code includes a way to play MOD-like tracker files (XM, S3M, and others). So, why not finish it out with a poorly rendered Midi version of All Star by Smash Mouth? This was literally a matter of adding about 5 lines to my existing code, and disabling a couple things in the setup that it turned out I wasn't using anyways. The player takes everything in the background, while playing Soviet Bloc Game still. It uses some hardware (interrupts, timers and the like) so you can't drop it into just any source code you've got, but for one like Soviet Bloc Game that barely uses any special hardware it works fine.&lt;br /&gt;
&lt;br /&gt;
==IBM-PC/DOS Version==&lt;br /&gt;
I got a bunch of 5-1/4 floppy drives recently and used them to fix the first computer I learned to program - the Sanyo MBC-550. This 1983 computer comes with MS-DOS and BASIC, but is not IBM-PC compatible (no CGA card, for instance, instead having it's own internal color graphics). As such, it has very little games availible for it.&lt;br /&gt;
&lt;br /&gt;
Since I had never written any code for a 8088 based system, and I have five computers that use a pre-286 CPU, I decided to port my Soviet Bloc Game to it. GCC does not work for anything below a 386, but Open WATCOM does, so that's what I used. &lt;br /&gt;
&lt;br /&gt;
This C comiler comes with modes for choosing text modes, but like most IBM PC software, it directly accesses the hardware to make it work. This makes things incompatible with the Sanyo. The Sanyo does support standard BIOS Interrupt calls, which are slower than the direct hardware calls but are fast enough for Soviet Bloc Game. However, I discovered that C functions to do text manipulation are too slow for Soviet Bloc Game, at least on a slow system like the Sanyo. So, the sections to write graphics and also read keys on the keyboard are done in Assembly. For proper timing between system of different clock speeds, I used the system tick - Int 1aH, which increments a counter about 18.2 times per second. &lt;br /&gt;
&lt;br /&gt;
So now, there is a version of my Soviet Bloc Game that works on pretty much any computer that runs MS-DOS.&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
Here is a link to the source files and a compiled version: [[File:Wilson-tetris-NDS-t2.zip]]. You can build this just by running &amp;lt;code&amp;gt;make&amp;lt;/code&amp;gt; from within the devkitPro console.&lt;br /&gt;
&lt;br /&gt;
[[File:Wilson-tetris-PC.zip]] - the DOS/PC version&lt;br /&gt;
&lt;br /&gt;
[[File:alnwlen-sbg-trs80.dmk]] - (by popular demand) TRS-80 disk image with /CMD binary&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=Main_Page&amp;diff=603</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=Main_Page&amp;diff=603"/>
		<updated>2023-09-20T20:21:38Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''This site''' serves documentation for my various projects, mostly for my own reference, but also as a way to share them. Using a wiki for this purpose is certainly overkill, but it is easy to edit and format, and I am familiar with it. Whenever I do a fairly involved project, there's a decent chance I will write up the things I learned on this site. As it stands, this site is still relatively sparse, because I like working on projects more than writing about them, like most people.&lt;br /&gt;
&lt;br /&gt;
This web server and the content it provides is also a project in and of itself. This is running on server hardware and software that I set up myself, so as such even the content you are reading now is experimental. I guarantee no stability or reliable access, although I will do my best to keep this available online for as long as possible (though as of 2020 I have had a server like this running continuously for over 5 years).&lt;br /&gt;
&lt;br /&gt;
==Project List==&lt;br /&gt;
More major projects are listed below, in no particular order. See the list of [[Special:AllPages|all pages here.]]&lt;br /&gt;
&lt;br /&gt;
* [[AWOL Box]] - My take on the infamous Mikal Hart Reverse Geocache™ puzzle&lt;br /&gt;
* [[Nixie Clock 1]] - First completed Nixie clock/display unit&lt;br /&gt;
* [[Nixie Clock 2]] - Second Nixie display&lt;br /&gt;
* [[RC Wheelchair 1]] - Hacking the joystick of an electric wheelchair for remote control&lt;br /&gt;
* [[RC Wheelchair 2]] - A complete high-power h-bridge controller, controls, and radio system.&lt;br /&gt;
* [[RC Wheelchair 3]] - Latest version, using an rs485 bus between different modules &lt;br /&gt;
* [[Wilson X carriage]] - Improved x-carriage for the Prusa i3 style 3d printer.&lt;br /&gt;
* [[COW arm]] - a robot arm for the wheelchair robot base&lt;br /&gt;
* [[Lora module]] - module that makes using LoRa radio communications easier to work with.&lt;br /&gt;
* [[Environmental logger]] - A time capsule experiment&lt;br /&gt;
* [[Model T speedometer]] - General purpose GPS based speedometer for cars that don't have one. &lt;br /&gt;
* [[Geiger counter]] - my homemade Geiger counter using a SBM20 tube&lt;br /&gt;
* [[Raspberry Pi DVR]] - my main way of recording TV off the air&lt;br /&gt;
* [[Soviet Bloc Game (C version)]] - my version of a Tetris-like clone (in C)&lt;br /&gt;
* [[Door monitor]] - putting an unused security system to &amp;quot;good&amp;quot; use.&lt;br /&gt;
* [[uScreen]] - general purpose module with esp8266 and Nokia LCD&lt;br /&gt;
* [[6888 Transmitter]] - 6888 tube-based AM transmitter&lt;br /&gt;
* [[Sky Pointer]] - a thingy for pointing out objects in the sky.&lt;br /&gt;
* [[Atlas Gears]] - Replacement lathe/mill changegears&lt;br /&gt;
* [[Smart Response XE]] - Hacking of a school response system&lt;br /&gt;
* [[Floppy disk drive hacking]] - Poking around floppy drives with a logic analyzer&lt;br /&gt;
* [[Coronameter]] - Dedicated screen for pandemic panicking.&lt;br /&gt;
* [[midiCNC]] - python program for playing MIDI files on a CNC machine / stepper motors with gcode&lt;br /&gt;
* [[Miscellaneous]] - short things that aren't long enough for their own page&lt;br /&gt;
* [[Unpublished Projects]]&lt;br /&gt;
&lt;br /&gt;
==TRS-80 Stuff==&lt;br /&gt;
* [[TRS-80 Model 1 - Character ROM]]&lt;br /&gt;
* [[TRS-80 Model 1 - Wilson Expansion Interface]] - an Expansion Interface (needed to connect floppy drives and other peripherals to the 1977 TRS-80 microcomputer) &lt;br /&gt;
* [[TRS-80 Plug 'n Power Controller]] - reverse engineering an extinct trs-80 accessory&lt;br /&gt;
* [[WC-80]] - A work in progress to build TRS-80 compatible Z80 computer&lt;br /&gt;
&lt;br /&gt;
==Other Services==&lt;br /&gt;
This server has hosted a number of different things since it's original incarnation in high school. Some of the old services are still up (but others are unlisted or taken down due to spam or security risks)&lt;br /&gt;
&lt;br /&gt;
* [http://alnwlsn.com/lists/ Public Lists] - experimental spam collection&lt;br /&gt;
* [http://alnwlsn.com/wintercamp/notifier/ Winter Camp Notifier Script] - service that emails you when new Winter Camp discussion takes place&lt;br /&gt;
* [http://alnwlsn.com/wicm/index.php/Main_Page WICM] - A copy of the Winter Camp encyclopedia translated into MediaWiki&lt;br /&gt;
&lt;br /&gt;
===What is the fish?===&lt;br /&gt;
The green fish is from a game I made when I first learned how to program. Over time, it turned into the logo that I use for my projects.&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=Main_Page&amp;diff=602</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=Main_Page&amp;diff=602"/>
		<updated>2023-09-20T20:21:13Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''This site''' serves documentation for my various projects, mostly for my own reference, but also as a way to share them. Using a wiki for this purpose is certainly overkill, but it is easy to edit and format, and I am familiar with it. Whenever I do a fairly involved project, there's a decent chance I will write up the things I learned on this site. As it stands, this site is still relatively sparse, because I like working on projects more than writing about them, like most people.&lt;br /&gt;
&lt;br /&gt;
This web server and the content it provides is also a project in and of itself. This is running on server hardware and software that I set up myself, so as such even the content you are reading now is experimental. I guarantee no stability or reliable access, although I will do my best to keep this available online for as long as possible (though as of 2020 I have had a server like this running continuously for over 5 years).&lt;br /&gt;
&lt;br /&gt;
==Project List==&lt;br /&gt;
More major projects are listed below, in no particular order. See the list of [[Special:AllPages|all pages here.]]&lt;br /&gt;
&lt;br /&gt;
* [[AWOL Box]] - My take on the infamous Mikal Hart Reverse Geocache™ puzzle&lt;br /&gt;
* [[Nixie Clock 1]] - First completed Nixie clock/display unit&lt;br /&gt;
* [[Nixie Clock 2]] - Second Nixie display&lt;br /&gt;
* [[RC Wheelchair 1]] - Hacking the joystick of an electric wheelchair for remote control&lt;br /&gt;
* [[RC Wheelchair 2]] - A complete high-power h-bridge controller, controls, and radio system.&lt;br /&gt;
* [[RC Wheelchair 3]] - Latest version, using an rs485 bus between different modules &lt;br /&gt;
* [[Wilson X carriage]] - Improved x-carriage for the Prusa i3 style 3d printer.&lt;br /&gt;
* [[COW arm]] - a robot arm for the wheelchair robot base&lt;br /&gt;
* [[Lora module]] - module that makes using LoRa radio communications easier to work with.&lt;br /&gt;
* [[Environmental logger]] - A time capsule experiment&lt;br /&gt;
* [[Model T speedometer]] - General purpose GPS based speedometer for cars that don't have one. &lt;br /&gt;
* [[Geiger counter]] - my homemade Geiger counter using a SBM20 tube&lt;br /&gt;
* [[Raspberry Pi DVR]] - my main way of recording TV off the air&lt;br /&gt;
* [[Soviet Bloc Game (C)]] - my version of a Tetris-like clone (in C)&lt;br /&gt;
* [[Door monitor]] - putting an unused security system to &amp;quot;good&amp;quot; use.&lt;br /&gt;
* [[uScreen]] - general purpose module with esp8266 and Nokia LCD&lt;br /&gt;
* [[6888 Transmitter]] - 6888 tube-based AM transmitter&lt;br /&gt;
* [[Sky Pointer]] - a thingy for pointing out objects in the sky.&lt;br /&gt;
* [[Atlas Gears]] - Replacement lathe/mill changegears&lt;br /&gt;
* [[Smart Response XE]] - Hacking of a school response system&lt;br /&gt;
* [[Floppy disk drive hacking]] - Poking around floppy drives with a logic analyzer&lt;br /&gt;
* [[Coronameter]] - Dedicated screen for pandemic panicking.&lt;br /&gt;
* [[midiCNC]] - python program for playing MIDI files on a CNC machine / stepper motors with gcode&lt;br /&gt;
* [[Miscellaneous]] - short things that aren't long enough for their own page&lt;br /&gt;
* [[Unpublished Projects]]&lt;br /&gt;
&lt;br /&gt;
==TRS-80 Stuff==&lt;br /&gt;
* [[TRS-80 Model 1 - Character ROM]]&lt;br /&gt;
* [[TRS-80 Model 1 - Wilson Expansion Interface]] - an Expansion Interface (needed to connect floppy drives and other peripherals to the 1977 TRS-80 microcomputer) &lt;br /&gt;
* [[TRS-80 Plug 'n Power Controller]] - reverse engineering an extinct trs-80 accessory&lt;br /&gt;
* [[WC-80]] - A work in progress to build TRS-80 compatible Z80 computer&lt;br /&gt;
&lt;br /&gt;
==Other Services==&lt;br /&gt;
This server has hosted a number of different things since it's original incarnation in high school. Some of the old services are still up (but others are unlisted or taken down due to spam or security risks)&lt;br /&gt;
&lt;br /&gt;
* [http://alnwlsn.com/lists/ Public Lists] - experimental spam collection&lt;br /&gt;
* [http://alnwlsn.com/wintercamp/notifier/ Winter Camp Notifier Script] - service that emails you when new Winter Camp discussion takes place&lt;br /&gt;
* [http://alnwlsn.com/wicm/index.php/Main_Page WICM] - A copy of the Winter Camp encyclopedia translated into MediaWiki&lt;br /&gt;
&lt;br /&gt;
===What is the fish?===&lt;br /&gt;
The green fish is from a game I made when I first learned how to program. Over time, it turned into the logo that I use for my projects.&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=Soviet_Bloc_Game_(C_version)&amp;diff=601</id>
		<title>Soviet Bloc Game (C version)</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=Soviet_Bloc_Game_(C_version)&amp;diff=601"/>
		<updated>2023-09-20T20:20:05Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:Wilson-tetris-trs80.png|thumb|Soviet Bloc Game v1.1 running in a trs-80 emulator]]&lt;br /&gt;
While thinking about ways to make my TRS-80 model I more useable in 2019, I realized that I could not easily find a version of Tetris for it (probably because the TRS-80 came out about 10ish years before Tetris was invented), so I thought I might try to write my own after watching an episode of ''the 8-bit Guy'' where David does the same. I used to play the Gameboy version of Tetris all the time on my Ti-nSpire calculator thanks to the nspire hacking scene and an emulator port, so that's the style of Tetris I will try to recreate.&lt;br /&gt;
&lt;br /&gt;
However, I have written few games, and never done a Tetris-like clone before, so I approach this mostly as a challenge.&lt;br /&gt;
&lt;br /&gt;
Logal notice: &amp;quot;Tetris&amp;quot; is a trademark of The Tetris Company (who have been known to take down unauthorized trademark versions before: https://github.com/jdah/tetris-os), so I have decided to rename this clone &amp;quot;Soviet Bloc Game&amp;quot;&lt;br /&gt;
&lt;br /&gt;
'''Objectives:'''&lt;br /&gt;
* Playfield the same size as GB version&lt;br /&gt;
* in color?&lt;br /&gt;
* same or similar scoring system to GB version&lt;br /&gt;
* same rotation style as GB version&lt;br /&gt;
* easily portable to different platforms (that are C like)&lt;br /&gt;
* somewhat wellishly documented&lt;br /&gt;
&lt;br /&gt;
This ended up being a sort of 24 hour challenge; I started this project at about 11pm yesterday, and by about the time of this page's creation I had created an Arduino version of Soviet Bloc Game that met most of these objectives.&lt;br /&gt;
&lt;br /&gt;
==Theory of Operation==&lt;br /&gt;
===Basics===&lt;br /&gt;
The basics of the game's functioning can be summed up with just a few components: a part that generates the  shapes, a playfield that contains already played pieces and open spots where the active piece can move and collision detection that says where the active piece can and can't go.&lt;br /&gt;
&lt;br /&gt;
The playfield size is adjustable, but in the gameboy version it's 10 wide by 18 tall. In my version I added an extra 4 lines on top (which are not to be displayed) for the new pieces to appear within. Within the program, it appears as a big 2D matrix called &amp;lt;code&amp;gt;playfield[][]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
====Drawing the pieces====&lt;br /&gt;
Rather than figure out all the complex rotations of the part, I hardcoded them (using the Nintendo rotation system as described at https://tetris.fandom.com/wiki/Nintendo_Rotation_System). The parts appear in a 4x4 piece container &amp;lt;code&amp;gt;pieceC[][]&amp;lt;/code&amp;gt; matrix. This is then overlaid onto the playfield, and can be moved around. Nonzero cells of the matrix define the shape of the piece, like:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 0|| 0|| 0|| 0&lt;br /&gt;
|-&lt;br /&gt;
| 0|| 5|| 0|| 0&lt;br /&gt;
|-&lt;br /&gt;
| 5|| 5|| 0|| 0&lt;br /&gt;
|-&lt;br /&gt;
| 0|| 5|| 0|| 0&lt;br /&gt;
|}&lt;br /&gt;
The numbers used to define the piece are unique to each piece, so if you write the display section to use each number as a color, you can have a color game.&lt;br /&gt;
&lt;br /&gt;
====Collision detection====&lt;br /&gt;
In the collision detection section, the overlaid piece carrier is moved as if you were to make that move, and then we check if any of the cells overlap with non-blank sections of the playfield, or if any part of the active piece lies outside the playfield area. If so, the move is undone, and you can decide what to do from there. If no collision occurs, the move is done for real and displayed the next time the screen is updated.&lt;br /&gt;
 &lt;br /&gt;
Usually, the collision detection is used to prevent motion if a piece or wall is in the way, but if the piece is attempting to move downwards, the piece is instead copied onto the playfield, and the numbers making up the piece are increased by 8. This is how the collision detection can differentiate between the active piece and dead pieces in the playfield.&lt;br /&gt;
&lt;br /&gt;
====Input conditioning====&lt;br /&gt;
I added some input conditioning as well. For rotations of the active piece, the move is not repeating, and you must release the rotation input and press it again for a rotation to happen again. For moves left, right, and down, a system similar to repeating keys on a computer keyboard is used, holding down the direction for a brief moment will make the action repeat. In the gameboy version, dropping a piece quickly will increase the score based on the number of tiles dropped while moving fast. This is also replicated (but probably not perfectly) in my version.&lt;br /&gt;
&lt;br /&gt;
===Details===&lt;br /&gt;
====Levels and speed====&lt;br /&gt;
I only update the screen every few moments. Call the time of one of these updates a '''frame'''. While the screen is waiting to be updated, it's a good time to check the controls and see what we need to do the next time the screen needs updating. All the processing of the moves and collisions happens just before the screen updates. Every few frames, the active piece drops one row. This is connected to the levels, which in my version go from 0-20, which I think is in line with the gb version. The drop happens every 20 frames at level 0, up to a delay of 0 frames at level 20. For every 10 lines that the user clears, the level is increased by 1, so that the game gets progressively faster, just like the gb version. &lt;br /&gt;
====Game over detection====&lt;br /&gt;
This one is pretty simple. If any non-active piece sections are found on the line above the top of the visible playfield, the game ends. Check this every frame.&lt;br /&gt;
====Line completion detection====&lt;br /&gt;
This is a little more complicated than the game over, but basically I check to see if any rows are completely filled in with dead pieces. I keep track of which lines are full (and how many of them there are), and animate the corresponding lines by having the whole row(s) flash for a moment. At this point I also add onto the number of completed lines counter displayed on the scoreboard. Then, I copy all the lines above the filled line down by one tile. Since this is done for each filled line in sequence, the effect moves the playfield down for all cases of any number of filled lines.  &lt;br /&gt;
====Scoring====&lt;br /&gt;
The number of filled lines cleared in one move (calculated above) is used in the scoring formula to add to the score. The full formula (according to the Nintendo scoring system) is:&lt;br /&gt;
* 1 line: 40*(level+1)&lt;br /&gt;
* 2 lines: 100*(level+1)&lt;br /&gt;
* 3 lines: 300*(level+1)&lt;br /&gt;
* 4 lines: 1200*(level+1) &lt;br /&gt;
&lt;br /&gt;
Additionally, the number of grid tiles that are dropped by the user pressing &amp;quot;down&amp;quot; (ie, soft drop), which I kept track of earlier, are also added to the score.&lt;br /&gt;
====New game====&lt;br /&gt;
I have a new game button also. When pressed (and the controls are checked), it picks new pieces, clears the level, score, and lines counters, and empties the playfield of dead pieces. Thus, a new game begins.&lt;br /&gt;
&lt;br /&gt;
==Hardware (Arduino version)==&lt;br /&gt;
I do most of my software development on arduino-like embedded systems, and am pretty familiar with I/O options on these devices (more so than on a stndard computer), so I used an arduino nano with a 128x64 st7920 monochrome LCD and the u8g2 libray for the display. For input, I wired up 3 buttons, 2 rotate and one new game, and an analog joystick (which I am using as a digital one) for directional moves.&lt;br /&gt;
&lt;br /&gt;
===Porting===&lt;br /&gt;
With any luck, my code can be ported to different platforms just my changing the section that displays the playfield on a screen, the input sections that take in the button presses, and maybe the delay sections. On the Arduino, my code takes up about 50% of the prgram storage space, and 50% of ram, with most of that being due to the u8g2 library. I'm not an expert on the super low-level processes on the arduino, nor do I think my game is the most effceiently coded, but my guess is that if the game works well on a system with as low specifications as the atmega328, it should be able to run on nearly anything else.&lt;br /&gt;
&lt;br /&gt;
==The Code (Arduino version)==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#define pfsizeX 10&lt;br /&gt;
#define pfsizeY 22 //note that top 4 lines are not drawn&lt;br /&gt;
uint8_t playfield[pfsizeX][pfsizeY];  //the game area&lt;br /&gt;
&lt;br /&gt;
uint8_t pieceC[4][4]; //piece container&lt;br /&gt;
int8_t pieceCX;  //location of upper left corner of piece container&lt;br /&gt;
int8_t pieceCY;&lt;br /&gt;
uint8_t pieceT; //type of piece&lt;br /&gt;
uint8_t pieceR; //rotation of piece &lt;br /&gt;
uint8_t nextpieceT; &lt;br /&gt;
uint8_t nextpieceR; &lt;br /&gt;
&lt;br /&gt;
uint8_t dcontrol; //locks in control every frame&lt;br /&gt;
uint8_t rcontrol;&lt;br /&gt;
&lt;br /&gt;
uint8_t lastdcontrol=0; //for &amp;quot;debouncing&amp;quot; of inputs&lt;br /&gt;
uint8_t lastrcontrol=0; &lt;br /&gt;
uint8_t drepeatframe=0;&lt;br /&gt;
#define drepeatframes 3 //wait _ frames before repeatedly going in one direction&lt;br /&gt;
&lt;br /&gt;
uint8_t dropframe=0;    //counter for number of frames between block drops&lt;br /&gt;
uint8_t level=0; //LEVEL decreases frame drop from 20 frames to 0 frames (levels 0 to 20)&lt;br /&gt;
&lt;br /&gt;
boolean ngame=0; //when set, starts new game&lt;br /&gt;
&lt;br /&gt;
uint16_t lines=0; //NUMBER OF LINES CLEARED&lt;br /&gt;
uint32_t score=0; //TOTAL SCORE (using NES rules)&lt;br /&gt;
&lt;br /&gt;
uint8_t fdrop;   //number of blocks that piece has been fast dropped&lt;br /&gt;
uint8_t lslvi=0; //lines since level increase (when this gets to 10, increase the level)&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;U8g2lib.h&amp;gt;&lt;br /&gt;
U8G2_ST7920_128X64_1_HW_SPI u8g2(U8G2_R0, /* CS=*/ 12, /* reset=*/ 8);&lt;br /&gt;
&lt;br /&gt;
void dispscreen(){&lt;br /&gt;
  u8g2.firstPage();  &lt;br /&gt;
  do {&lt;br /&gt;
    for(uint8_t j=4; j&amp;lt;pfsizeY; j++){ //draw screen&lt;br /&gt;
    for(uint8_t i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
      if(playfield[i][j]!=0){&lt;br /&gt;
        u8g2.drawBox(5*(j-4),5*(pfsizeX-1)-5*i,6,6);&lt;br /&gt;
      }else{&lt;br /&gt;
        u8g2.drawFrame(5*(j-4),5*(pfsizeX-1)-5*i,6,6);&lt;br /&gt;
        //if(j==3){u8g2.drawLine(5*j,5*(pfsizeX-1)-5*i,5*j+5,5*(pfsizeX-1)-5*i+5);}&lt;br /&gt;
      }&lt;br /&gt;
    }}&lt;br /&gt;
&lt;br /&gt;
  uint8_t temppieceT=pieceT; &lt;br /&gt;
  uint8_t temppieceR=pieceR;&lt;br /&gt;
  pieceT=nextpieceT;&lt;br /&gt;
  pieceR=nextpieceR;&lt;br /&gt;
  loadpiece(); &lt;br /&gt;
  for(uint8_t j=0; j&amp;lt;4; j++){ //draw next piece&lt;br /&gt;
  for(uint8_t i=0; i&amp;lt;4; i++){&lt;br /&gt;
    if(pieceC[i][j]!=0){&lt;br /&gt;
      u8g2.drawBox(5*(j+pfsizeY-3),5*(pfsizeX-1)-5*i,6,6);&lt;br /&gt;
    }else{&lt;br /&gt;
      u8g2.drawFrame(5*(j+pfsizeY-3),5*(pfsizeX-1)-5*i,6,6);&lt;br /&gt;
    }&lt;br /&gt;
  }}&lt;br /&gt;
  pieceT=temppieceT;&lt;br /&gt;
  pieceR=temppieceR;&lt;br /&gt;
  loadpiece();&lt;br /&gt;
&lt;br /&gt;
  u8g2.setFont(u8g2_font_6x10_tf);&lt;br /&gt;
  char zbuffer[32];&lt;br /&gt;
  sprintf(zbuffer, &amp;quot;N%d&amp;quot;, lines);&lt;br /&gt;
  u8g2.drawStr(0,60, zbuffer);&lt;br /&gt;
  sprintf(zbuffer, &amp;quot;L%d&amp;quot;, level);&lt;br /&gt;
  u8g2.drawStr(25,60, zbuffer);&lt;br /&gt;
  sprintf(zbuffer, &amp;quot;S%d&amp;quot;, score);&lt;br /&gt;
  u8g2.drawStr(50,60, zbuffer);  &lt;br /&gt;
  } while( u8g2.nextPage() );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void controls(){&lt;br /&gt;
  if(digitalRead(A3)==0){ngame=1;}&lt;br /&gt;
  if(dcontrol==0){&lt;br /&gt;
    if(analogRead(A7)&amp;lt;10){dcontrol=4;}&lt;br /&gt;
    if(analogRead(A7)&amp;gt;1014){dcontrol=6;}&lt;br /&gt;
    if(analogRead(A6)&amp;gt;1014){dcontrol=8;}&lt;br /&gt;
    if(analogRead(A6)&amp;lt;10){dcontrol=2;}&lt;br /&gt;
    if(dcontrol==0){drepeatframe=0;}&lt;br /&gt;
    if(dcontrol==lastdcontrol){          //short delay before fast motion&lt;br /&gt;
      if(drepeatframe&amp;lt;=drepeatframes){&lt;br /&gt;
        drepeatframe++;&lt;br /&gt;
        dcontrol=3; //lockout if within lockout period&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(rcontrol==0){&lt;br /&gt;
    if(digitalRead(A4)==0){rcontrol=1;}&lt;br /&gt;
    if(digitalRead(A5)==0){rcontrol=2;}&lt;br /&gt;
    if(rcontrol==lastrcontrol){rcontrol=3;}&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void clearControls(){&lt;br /&gt;
  if(rcontrol!=3){lastrcontrol=rcontrol;}&lt;br /&gt;
  if(dcontrol!=3){lastdcontrol=dcontrol;}&lt;br /&gt;
  rcontrol=0;&lt;br /&gt;
  dcontrol=0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void draw(){&lt;br /&gt;
  loadpiece(); //load piece into the piece carrier&lt;br /&gt;
  for(uint8_t i=0; i&amp;lt;pfsizeX; i++){   //clear any cells with active piece parts (will be written again with new pieceC&lt;br /&gt;
    for(uint8_t j=0; j&amp;lt;pfsizeY; j++){&lt;br /&gt;
      if(playfield[i][j]&amp;lt;=7){playfield[i][j]=0;}&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  for(uint8_t i=0; i&amp;lt;4; i++){ //copy active piece onto the playfield&lt;br /&gt;
    for(uint8_t j=0; j&amp;lt;4; j++){&lt;br /&gt;
      if(pieceCX+i&amp;gt;=0&amp;amp;&amp;amp;pieceCX+i&amp;lt;pfsizeX&amp;amp;&amp;amp;pieceCY+j&amp;gt;=0&amp;amp;&amp;amp;pieceCY+j&amp;lt;pfsizeY){//check if piece segment can be drawn on screen&lt;br /&gt;
        if(pieceC[i][j]!=0){playfield[i+pieceCX][j+pieceCY]=pieceC[i][j];}&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
boolean checkCollide(){ //move the piece carrier first, then check if anything collides&lt;br /&gt;
  loadpiece(); //load piece into the piece carrier&lt;br /&gt;
  boolean nonvalidity=0;&lt;br /&gt;
  for(uint8_t i=0; i&amp;lt;4; i++){ //run through all piece carrier cells&lt;br /&gt;
    for(uint8_t j=0; j&amp;lt;4; j++){&lt;br /&gt;
      if(pieceCX+i&amp;gt;=0&amp;amp;&amp;amp;pieceCX+i&amp;lt;pfsizeX&amp;amp;&amp;amp;pieceCY+j&amp;gt;=0&amp;amp;&amp;amp;pieceCY+j&amp;lt;pfsizeY){ //check if piece carrier segment can be drawn on screen&lt;br /&gt;
        if(pieceC[i][j]!=0&amp;amp;&amp;amp;playfield[i+pieceCX][j+pieceCY]&amp;gt;7){ //if both background and nonzero piece carrier segment collide&lt;br /&gt;
          nonvalidity=1;&lt;br /&gt;
        }&lt;br /&gt;
      }else{ //this segment of PC can't be drawn on the screen&lt;br /&gt;
        if(pieceC[i][j]!=0){ //a filled in segment would be drawn offscreen&lt;br /&gt;
          nonvalidity=1;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  return nonvalidity;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void piece2bg(){&lt;br /&gt;
  for(uint8_t i=0; i&amp;lt;4; i++){ //copy active piece onto the screen&lt;br /&gt;
  for(uint8_t j=0; j&amp;lt;4; j++){&lt;br /&gt;
    if(pieceC[i][j]!=0){playfield[i+pieceCX][j+pieceCY]=pieceC[i][j]+8;} //copy the piece into the playfield/background &lt;br /&gt;
  }}&lt;br /&gt;
  nextpiece();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void nextpiece(){//generate the next piece and move PC back to the top&lt;br /&gt;
  pieceT=nextpieceT;&lt;br /&gt;
  pieceR=nextpieceR;&lt;br /&gt;
  nextpieceT = rand()%7 + 1;&lt;br /&gt;
  nextpieceR = rand()%4 + 1;&lt;br /&gt;
  pieceCY=0; //move piece carrier back to the top of screen&lt;br /&gt;
  pieceCX=3;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void newgamemon(){&lt;br /&gt;
  if(ngame==0){return;}&lt;br /&gt;
  Serial.println(F(&amp;quot;NEW GAME&amp;quot;));&lt;br /&gt;
  ngame=0;&lt;br /&gt;
  for(uint8_t i=0; i&amp;lt;pfsizeX; i++){   //clear any cells with active piece parts (will be written again with new pieceC&lt;br /&gt;
    for(uint8_t j=0; j&amp;lt;pfsizeY; j++){&lt;br /&gt;
      playfield[i][j]=0;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  lines=0;&lt;br /&gt;
  level=0;&lt;br /&gt;
  score=0;&lt;br /&gt;
  lslvi=0;&lt;br /&gt;
  fdrop=0;&lt;br /&gt;
  nextpiece();&lt;br /&gt;
  nextpiece();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loadpiece(){ //hardcoded all pieces&lt;br /&gt;
  switch(pieceT){&lt;br /&gt;
    case 1: //long one&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=1; pieceC[1][2]=1; pieceC[2][2]=1; pieceC[3][2]=1;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0; &lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=1; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=1; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=0; pieceC[2][2]=1; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=1; pieceC[3][3]=0; &lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 2: //backwards L&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=2; pieceC[1][2]=2; pieceC[2][2]=2; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=2; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=2; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=2; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=2; pieceC[1][3]=2; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=2; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=2; pieceC[1][2]=2; pieceC[2][2]=2; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=2; pieceC[2][1]=2; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=2; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=2; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 3: //L&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=3; pieceC[1][2]=3; pieceC[2][2]=3; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=3; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=3; pieceC[1][1]=3; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=3; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=3; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=3; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=3; pieceC[1][2]=3; pieceC[2][2]=3; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=3; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=3; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=3; pieceC[2][3]=3; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 4: //s shape&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=4; pieceC[2][2]=4; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=4; pieceC[1][3]=4; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=4; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=4; pieceC[2][2]=4; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=4; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 5: //T shape&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=5; pieceC[1][2]=5; pieceC[2][2]=5; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=5; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=5; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=5; pieceC[1][2]=5; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=5; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=5; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=5; pieceC[1][2]=5; pieceC[2][2]=5; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=5; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=5; pieceC[2][2]=5; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=5; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 6: //reverse s&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=6; pieceC[1][2]=6; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=6; pieceC[2][3]=6; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=6; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=6; pieceC[2][2]=6; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=6; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 7: //square&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      default:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=7; pieceC[2][1]=7; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=7; pieceC[2][2]=7; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setup(){&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  u8g2.begin();&lt;br /&gt;
  pinMode(A4, INPUT_PULLUP);&lt;br /&gt;
  pinMode(A5, INPUT_PULLUP);&lt;br /&gt;
  pinMode(A3, INPUT_PULLUP); //new game button&lt;br /&gt;
&lt;br /&gt;
  ngame=1;&lt;br /&gt;
  newgamemon();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
  &lt;br /&gt;
  for(uint32_t h=0;h&amp;lt;=5;h++){//frame delay  //DELAY SECTION (BETWEEN FRAMES)&lt;br /&gt;
    delay(1);&lt;br /&gt;
    controls();&lt;br /&gt;
    newgamemon();&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  boolean droppiece=0;&lt;br /&gt;
  dropframe++;&lt;br /&gt;
  if(dropframe&amp;gt;=(20-level)){&lt;br /&gt;
    dropframe=0;&lt;br /&gt;
    droppiece=1;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  if(rcontrol==1){&lt;br /&gt;
    pieceR++; if(pieceR&amp;gt;=5){pieceR=1;} //try to rotate piece&lt;br /&gt;
    if(checkCollide()){&lt;br /&gt;
      pieceR--; if(pieceR&amp;lt;=0){pieceR=4;} //undo rotation&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(rcontrol==2){&lt;br /&gt;
    pieceR--; if(pieceR&amp;lt;=0){pieceR=4;}&lt;br /&gt;
    if(checkCollide()){&lt;br /&gt;
      pieceR++; if(pieceR&amp;gt;=5){pieceR=1;}&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(dcontrol==4){&lt;br /&gt;
    pieceCX--; //try and see what happens if we move the piece left&lt;br /&gt;
    if(checkCollide()){//piece move is not valid&lt;br /&gt;
      pieceCX++; //take piece back&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(dcontrol==6){&lt;br /&gt;
    pieceCX++; //try and see what happens if we move the piece left&lt;br /&gt;
    if(checkCollide()){//piece move is not valid&lt;br /&gt;
      pieceCX--; //take piece back&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(dcontrol==8){&lt;br /&gt;
    pieceCY--; //try and see what happens if we move the piece up&lt;br /&gt;
    if(checkCollide()){//piece move is not valid&lt;br /&gt;
      pieceCY++; //take piece back&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(dcontrol==2||droppiece==1){&lt;br /&gt;
    pieceCY++; //try and see what happens if we move the piece down&lt;br /&gt;
    if(!(drepeatframe&amp;lt;=drepeatframes)){ //is in fast mode&lt;br /&gt;
      fdrop++;&lt;br /&gt;
    }else{&lt;br /&gt;
      fdrop=0;&lt;br /&gt;
    }&lt;br /&gt;
    if(checkCollide()){//piece move is not valid&lt;br /&gt;
      pieceCY--; //take piece back&lt;br /&gt;
      score+=fdrop; //add # of fast dropped blocks to score&lt;br /&gt;
      fdrop=0;&lt;br /&gt;
      piece2bg(); //copy piece to background and reset to next piece&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  draw();&lt;br /&gt;
  dispscreen();&lt;br /&gt;
  &lt;br /&gt;
  clearControls();&lt;br /&gt;
&lt;br /&gt;
  //check for line clears&lt;br /&gt;
  boolean clearline[pfsizeY];&lt;br /&gt;
  uint8_t clearedlines=0;&lt;br /&gt;
  uint8_t templines=0;&lt;br /&gt;
  for(uint8_t j=4; j&amp;lt;pfsizeY; j++){&lt;br /&gt;
  clearline[j]=1; //assume line is cleared&lt;br /&gt;
  for(uint8_t i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
    if(playfield[i][j]&amp;lt;=7){clearline[j]=0;break;} //line is not full&lt;br /&gt;
  }&lt;br /&gt;
  clearedlines+=clearline[j];&lt;br /&gt;
  templines+=clearline[j];&lt;br /&gt;
  }&lt;br /&gt;
  if(clearedlines&amp;gt;0){//breifly animate the cleared lines, then clear them&lt;br /&gt;
    for(uint8_t f=0; f&amp;lt;=6; f++){&lt;br /&gt;
      for(uint8_t j=4; j&amp;lt;pfsizeY; j++){&lt;br /&gt;
        if(clearline[j]==1){&lt;br /&gt;
          for(uint8_t i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
            if(f%2==0){playfield[i][j]=8;}else{playfield[i][j]=0;}&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      draw();&lt;br /&gt;
      dispscreen();&lt;br /&gt;
      delay(200);&lt;br /&gt;
    }&lt;br /&gt;
    for(uint8_t j=4; j&amp;lt;pfsizeY; j++){ //accutally clear the lines&lt;br /&gt;
       if(clearline[j]==1){&lt;br /&gt;
        for(uint8_t t=j; t&amp;gt;=4; t--){&lt;br /&gt;
         for(uint8_t i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
           playfield[i][t]=playfield[i][t-1];&lt;br /&gt;
         }&lt;br /&gt;
        }&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
   lines+=templines; //add number of lines to lines counter&lt;br /&gt;
   switch(templines){ //calculate score&lt;br /&gt;
    case 1:&lt;br /&gt;
      score+=40*(level+1);&lt;br /&gt;
      break;&lt;br /&gt;
    case 2:&lt;br /&gt;
      score+=100*(level+1);&lt;br /&gt;
      break;&lt;br /&gt;
    case 3:&lt;br /&gt;
      score+=300*(level+1);&lt;br /&gt;
      break;&lt;br /&gt;
    default:&lt;br /&gt;
      score+=1200*(level+1);&lt;br /&gt;
      break;&lt;br /&gt;
   }&lt;br /&gt;
   for(uint8_t i=1; i&amp;lt;=templines; i++){ //see if the level needs increasing&lt;br /&gt;
    lslvi++;&lt;br /&gt;
    if(lslvi&amp;gt;=10){lslvi=0;level++;}&lt;br /&gt;
    if(level&amp;gt;=20){level=20;}&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
  //check gameover (scan through line 3 and see if there are any non-active pieces in it)&lt;br /&gt;
  for(uint8_t i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
    if(playfield[i][3]&amp;gt;7){&lt;br /&gt;
      //*********************GAME OVER********************&lt;br /&gt;
      Serial.println(F(&amp;quot;GAME OVER&amp;quot;));&lt;br /&gt;
      while(ngame==0){&lt;br /&gt;
        controls();&lt;br /&gt;
      }&lt;br /&gt;
      newgamemon();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  //check for completed lines&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
==The Code (TRS-80 version)==&lt;br /&gt;
I used the z88dk C compiler for the port. To compile, use &amp;lt;code&amp;gt;zcc  +trs80 -lndos -lm -create-app soviet-bloc-game.c -o soviet-bloc-game&amp;lt;/code&amp;gt; which will create a &amp;lt;code&amp;gt;.CAS&amp;lt;/code&amp;gt; file. Use &amp;lt;code&amp;gt;zcc  +trs80 -lndos -lm -create-app -subtype=disk soviet-bloc-game.c -o soviet-bloc-game&amp;lt;/code&amp;gt; to create a &amp;lt;code&amp;gt;.CMD&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Porting this to the TRS-80 turned out to be a much harder challange, mostly because the TRS-80 is a very slow, low power machine. Some of the improvements were obvious, like only checking the lines and game over only when an active piece becomes inactive. However, this isn't enough to make a playable version. The TRS-80 is slow enough that every comparison, operation, and graphics plot add noticable delay. Therefore, I only update parts of the display when needed (ie, the area around the active piece, 1 block around the piece carrier); the background is filled back in to erase piece moves, and the screen is only redrawn at the end when the active piece becomes inactive. This is possible because unlike the arduino version, the whole screen doesn't need to be rewitten every time it changes. I also had to take out any frame delay and reduce the levels to 10 because the machine is not fast enough to keep up. The version below is therefore clunky, but playable. The obvious next steps are to make an assembler version of Soviet Bloc Game.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//soviet-bloc-game.c - Soviet Bloc Game for TRS-80 - 05/28/2019&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt; &lt;br /&gt;
#include &amp;lt;graphics.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define pfsizeX 10 //playfield size (in cells)&lt;br /&gt;
#define pfsizeY 22 //note that top 4 lines are not drawn&lt;br /&gt;
#define pfX 8 //upper left corner of playfield location (pixel)&lt;br /&gt;
#define pfY 4&lt;br /&gt;
#define npX 60 //upper left corner of next piece indicator&lt;br /&gt;
#define npY 2&lt;br /&gt;
#define clspX 4 //spacing between cell elements (pixels)&lt;br /&gt;
#define clspY 2&lt;br /&gt;
&lt;br /&gt;
void controls(void);&lt;br /&gt;
&lt;br /&gt;
unsigned char playfield[pfsizeX][pfsizeY];  //the game area&lt;br /&gt;
unsigned char cscreen[pfsizeX][pfsizeY-4];  //a copy of what gets drawn on the screen (game area)&lt;br /&gt;
unsigned char pieceC[4][4]; //piece container&lt;br /&gt;
signed char pieceCX;  //location of upper left corner of piece container&lt;br /&gt;
signed char pieceCY;&lt;br /&gt;
unsigned char pieceT; //type of piece&lt;br /&gt;
unsigned char pieceR; //rotation of piece &lt;br /&gt;
unsigned char nextpieceT; &lt;br /&gt;
unsigned char nextpieceR; &lt;br /&gt;
&lt;br /&gt;
unsigned char dcontrol; //locks in control every frame&lt;br /&gt;
unsigned char rcontrol;&lt;br /&gt;
&lt;br /&gt;
unsigned char lastdcontrol=0; //for &amp;quot;debouncing&amp;quot; of inputs&lt;br /&gt;
unsigned char lastrcontrol=0; &lt;br /&gt;
unsigned char drepeatframe=0;&lt;br /&gt;
#define drepeatframes 3 //wait _ frames before repeatedly going in one direction&lt;br /&gt;
&lt;br /&gt;
unsigned char dropframe=0;    //counter for number of frames between block drops&lt;br /&gt;
unsigned char level=0; //LEVEL decreases frame drop from maxlevel frames to 0 frames (levels 0 to MAXLEVEL)&lt;br /&gt;
#define maxlevel 10&lt;br /&gt;
&lt;br /&gt;
unsigned char ngame=1; //when set, starts new game&lt;br /&gt;
unsigned char newpc=0; //when set, get a new piece&lt;br /&gt;
&lt;br /&gt;
unsigned int lines=0; //NUMBER OF LINES CLEARED&lt;br /&gt;
long score=0; //TOTAL SCORE (using NES rules)&lt;br /&gt;
&lt;br /&gt;
unsigned char fdrop;   //number of blocks that piece has been fast dropped&lt;br /&gt;
unsigned char lslvi=0; //lines since level increase (when this gets to 10, increase the level)&lt;br /&gt;
&lt;br /&gt;
char lvladd=0;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void setblock(char x, char y){&lt;br /&gt;
	for(char i=0; i&amp;lt;8; i++){&lt;br /&gt;
		controls();&lt;br /&gt;
     	plot(x+i%4,y+i/4);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
void clearblock(char x, char y){&lt;br /&gt;
	for(char i=0; i&amp;lt;8; i++){&lt;br /&gt;
		controls();&lt;br /&gt;
    	unplot(x+i%4,y+i/4);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
void cdraw(char x, char y, char color){//only draws on screen if it needs to (uses cell coordinates)&lt;br /&gt;
	if(cscreen[x][y]!=color){&lt;br /&gt;
		cscreen[x][y]=color;&lt;br /&gt;
		if(color!=0){&lt;br /&gt;
			setblock( (x)*clspX+pfX, (y)*clspY+pfY );&lt;br /&gt;
		}else{&lt;br /&gt;
			clearblock( (x)*clspX+pfX, (y)*clspY+pfY);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
void recopyscreen(){ //copies playfield to screen &lt;br /&gt;
  for(char i=0; i&amp;lt;pfsizeX; i++){ //manually redraw the entire screen&lt;br /&gt;
  for(char j=4; j&amp;lt;pfsizeY; j++){&lt;br /&gt;
	  cdraw(i,j-4,playfield[i][j]);&lt;br /&gt;
  }}&lt;br /&gt;
  printf(&amp;quot;%cY%c%c%ld&amp;quot;, 27, 32+3, 32+47, score);&lt;br /&gt;
  printf(&amp;quot;%cY%c%c%d&amp;quot;, 27, 32+5, 32+47, level);&lt;br /&gt;
  printf(&amp;quot;%cY%c%c%d&amp;quot;, 27, 32+7, 32+47, lines);&lt;br /&gt;
}&lt;br /&gt;
void controls(){&lt;br /&gt;
	/*&lt;br /&gt;
   if(bpeek(0x3802)&amp;amp;0b00000010){plot(126,46);}else{unplot(126,46);} //up button (I)&lt;br /&gt;
   if(bpeek(0x3802)&amp;amp;0b00000100){plot(125,47);}else{unplot(125,47);} //left button (J)&lt;br /&gt;
   if(bpeek(0x3802)&amp;amp;0b00001000){plot(126,47);}else{unplot(126,47);} //down button (K)&lt;br /&gt;
   if(bpeek(0x3802)&amp;amp;0b00010000){plot(127,47);}else{unplot(127,47);} //right button (L)&lt;br /&gt;
   if(bpeek(0x3801)&amp;amp;0b00000010){plot(121,47);}else{unplot(121,47);} //a button (A)&lt;br /&gt;
   if(bpeek(0x3804)&amp;amp;0b00001000){plot(122,47);}else{unplot(122,47);} //b button (S)&lt;br /&gt;
   if(bpeek(0x3801)&amp;amp;0b00000001){plot(119,47);}else{unplot(119,47);} //newgame (@)&lt;br /&gt;
	   */&lt;br /&gt;
  if(bpeek(0x3840)&amp;amp;0b00000100){exit(0);}&lt;br /&gt;
  if(bpeek(0x3810)&amp;amp;0b00000010){lvladd=1;}&lt;br /&gt;
  if(bpeek(0x3810)&amp;amp;0b00000001){lvladd=-1;}&lt;br /&gt;
  &lt;br /&gt;
  if(bpeek(0x3801)&amp;amp;0b00000001){ngame=1;}&lt;br /&gt;
  if(bpeek(0x3808)&amp;amp;0b00000010){newpc=1;}&lt;br /&gt;
  if(dcontrol==0){&lt;br /&gt;
    if(bpeek(0x3802)&amp;amp;0b00000100){dcontrol=4;}&lt;br /&gt;
    if(bpeek(0x3802)&amp;amp;0b00010000){dcontrol=6;}&lt;br /&gt;
    if(bpeek(0x3802)&amp;amp;0b00000010){dcontrol=8;}&lt;br /&gt;
    if(bpeek(0x3802)&amp;amp;0b00001000){dcontrol=2;}&lt;br /&gt;
    if(dcontrol==0){drepeatframe=0;}&lt;br /&gt;
    if(dcontrol==lastdcontrol){          //short delay before fast motion&lt;br /&gt;
      if(drepeatframe&amp;lt;=drepeatframes){&lt;br /&gt;
        drepeatframe++;&lt;br /&gt;
        dcontrol=3; //lockout if within lockout period&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(rcontrol==0){&lt;br /&gt;
    if(bpeek(0x3801)&amp;amp;0b00000010){rcontrol=1;}&lt;br /&gt;
    if(bpeek(0x3804)&amp;amp;0b00001000){rcontrol=2;}&lt;br /&gt;
    if(rcontrol==lastrcontrol){rcontrol=3;}&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
void clearControls(){&lt;br /&gt;
  if(rcontrol!=3){lastrcontrol=rcontrol;}&lt;br /&gt;
  if(dcontrol!=3){lastdcontrol=dcontrol;}&lt;br /&gt;
  rcontrol=0;&lt;br /&gt;
  dcontrol=0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void loadpiece(){ //hardcoded all pieces&lt;br /&gt;
  switch(pieceT){&lt;br /&gt;
    case 1: //long one&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=1; pieceC[1][2]=1; pieceC[2][2]=1; pieceC[3][2]=1;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0; &lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=1; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=1; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=0; pieceC[2][2]=1; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=1; pieceC[3][3]=0; &lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 2: //backwards L&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=2; pieceC[1][2]=2; pieceC[2][2]=2; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=2; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=2; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=2; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=2; pieceC[1][3]=2; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=2; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=2; pieceC[1][2]=2; pieceC[2][2]=2; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=2; pieceC[2][1]=2; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=2; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=2; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 3: //L&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=3; pieceC[1][2]=3; pieceC[2][2]=3; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=3; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=3; pieceC[1][1]=3; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=3; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=3; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=3; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=3; pieceC[1][2]=3; pieceC[2][2]=3; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=3; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=3; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=3; pieceC[2][3]=3; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 4: //s shape&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=4; pieceC[2][2]=4; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=4; pieceC[1][3]=4; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=4; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=4; pieceC[2][2]=4; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=4; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 5: //T shape&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=5; pieceC[1][2]=5; pieceC[2][2]=5; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=5; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=5; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=5; pieceC[1][2]=5; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=5; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=5; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=5; pieceC[1][2]=5; pieceC[2][2]=5; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=5; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=5; pieceC[2][2]=5; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=5; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 6: //reverse s&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=6; pieceC[1][2]=6; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=6; pieceC[2][3]=6; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=6; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=6; pieceC[2][2]=6; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=6; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 7: //square&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      default:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=7; pieceC[2][1]=7; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=7; pieceC[2][2]=7; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void nextpiece(){//generate the next piece and move PC back to the top&lt;br /&gt;
  unsigned char tempT=nextpieceT;&lt;br /&gt;
  unsigned char tempR=nextpieceR;&lt;br /&gt;
  nextpieceT = rand()%7 + 1;&lt;br /&gt;
  nextpieceR = rand()%4 + 1;&lt;br /&gt;
  pieceT=nextpieceT;&lt;br /&gt;
  pieceR=nextpieceR;&lt;br /&gt;
  loadpiece();&lt;br /&gt;
  for(char i=0; i&amp;lt;4; i++){&lt;br /&gt;
  for(char j=0; j&amp;lt;4; j++){&lt;br /&gt;
	  if(pieceC[i][j]!=0){&lt;br /&gt;
		  setblock((i)*clspX+npX, (j)*clspY+npY);&lt;br /&gt;
	  }else{&lt;br /&gt;
		  clearblock((i)*clspX+npX, (j)*clspY+npY);&lt;br /&gt;
	  }&lt;br /&gt;
  }}&lt;br /&gt;
  &lt;br /&gt;
  pieceT=tempT;&lt;br /&gt;
  pieceR=tempR;&lt;br /&gt;
  &lt;br /&gt;
  pieceCY=0; //move piece carrier back to the top of screen&lt;br /&gt;
  pieceCX=3;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void mdraw(){&lt;br /&gt;
  //printf(&amp;quot;%cY%c%c%d&amp;quot;, 27, 32+1, 32+55, fff); fff++;&lt;br /&gt;
  loadpiece(); //load piece into the piece carrier&lt;br /&gt;
  for(char i=0; i&amp;lt;6; i++){ //check piece carrier and one block offset outside it&lt;br /&gt;
  for(char j=0; j&amp;lt;6; j++){&lt;br /&gt;
	  controls();&lt;br /&gt;
	  if((pieceCX-1+i)&amp;gt;=0&amp;amp;&amp;amp;(pieceCX-1+i)&amp;lt;pfsizeX&amp;amp;&amp;amp;(pieceCY-1+j)&amp;gt;=4&amp;amp;&amp;amp;(pieceCY-1+j)&amp;lt;(pfsizeY)){//check if this segment be drawn on screen&lt;br /&gt;
		if(i&amp;gt;=1&amp;amp;&amp;amp;i&amp;lt;=4&amp;amp;&amp;amp;j&amp;gt;=1&amp;amp;&amp;amp;j&amp;lt;=4){//accessing piece carrier section here}&lt;br /&gt;
			if(pieceC[i-1][j-1]&amp;gt;0){//if piece carrier block is confirmed should be white&lt;br /&gt;
				cdraw(i-1+pieceCX,j-5+pieceCY,pieceC[i-1][j-1]);&lt;br /&gt;
			}else{&lt;br /&gt;
				cdraw(i-1+pieceCX,j-5+pieceCY,playfield[i-1+pieceCX][j-1+pieceCY]); //color it in with the existing playfield background&lt;br /&gt;
			}&lt;br /&gt;
		}else{&lt;br /&gt;
			cdraw(i-1+pieceCX,j-5+pieceCY,playfield[i-1+pieceCX][j-1+pieceCY]);&lt;br /&gt;
		}&lt;br /&gt;
	  }&lt;br /&gt;
  }}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
unsigned char checkCollide(){ //move the piece carrier first, then check if anything collides&lt;br /&gt;
  loadpiece(); //load piece into the piece carrier&lt;br /&gt;
  unsigned char nonvalidity=0;&lt;br /&gt;
  for(char i=0; i&amp;lt;4; i++){ //run through all piece carrier cells&lt;br /&gt;
    for(char j=0; j&amp;lt;4; j++){&lt;br /&gt;
      if(pieceCX+i&amp;gt;=0&amp;amp;&amp;amp;pieceCX+i&amp;lt;pfsizeX&amp;amp;&amp;amp;pieceCY+j&amp;gt;=0&amp;amp;&amp;amp;pieceCY+j&amp;lt;pfsizeY){ //check if piece carrier segment can be drawn on screen&lt;br /&gt;
        if(pieceC[i][j]!=0&amp;amp;&amp;amp;playfield[i+pieceCX][j+pieceCY]&amp;gt;7){ //if both background and nonzero piece carrier segment collide&lt;br /&gt;
          nonvalidity=1;&lt;br /&gt;
        }&lt;br /&gt;
      }else{ //this segment of PC can't be drawn on the screen&lt;br /&gt;
        if(pieceC[i][j]!=0){ //a filled in segment would be drawn offscreen&lt;br /&gt;
          nonvalidity=1;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  return nonvalidity;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void newgamemon(){&lt;br /&gt;
  if(ngame==0){return;}&lt;br /&gt;
  //Serial.println(F(&amp;quot;NEW GAME&amp;quot;));&lt;br /&gt;
  printf(&amp;quot;%cY%c%c         &amp;quot;, 27, 32+14, 32+40);&lt;br /&gt;
  printf(&amp;quot;%cY%c%c        &amp;quot;, 27, 32+3, 32+47);&lt;br /&gt;
  printf(&amp;quot;%cY%c%c        &amp;quot;, 27, 32+5, 32+47);&lt;br /&gt;
  printf(&amp;quot;%cY%c%c        &amp;quot;, 27, 32+7, 32+47);&lt;br /&gt;
  ngame=0;&lt;br /&gt;
  for(unsigned char i=0; i&amp;lt;pfsizeX; i++){   //clear any cells with active piece parts (will be written again with new pieceC&lt;br /&gt;
    for(unsigned char j=0; j&amp;lt;pfsizeY; j++){&lt;br /&gt;
      playfield[i][j]=0;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  lines=0;&lt;br /&gt;
  level=0;&lt;br /&gt;
  score=0;&lt;br /&gt;
  lslvi=0;&lt;br /&gt;
  fdrop=0;&lt;br /&gt;
  nextpiece();&lt;br /&gt;
  nextpiece();&lt;br /&gt;
  recopyscreen();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void piece2bg(){ //will copy active piece into background and check for lines and game over&lt;br /&gt;
  for(unsigned char i=0; i&amp;lt;4; i++){ //copy piece onto background&lt;br /&gt;
  for(unsigned char j=0; j&amp;lt;4; j++){&lt;br /&gt;
    if(pieceC[i][j]!=0){playfield[i+pieceCX][j+pieceCY]=pieceC[i][j]+8;} //copy the piece into the playfield/background &lt;br /&gt;
  }}&lt;br /&gt;
  nextpiece();&lt;br /&gt;
  &lt;br /&gt;
  //check for line clears&lt;br /&gt;
  unsigned char clearline[pfsizeY];&lt;br /&gt;
  unsigned char clearedlines=0;&lt;br /&gt;
  unsigned char templines=0;&lt;br /&gt;
  for(unsigned char j=4; j&amp;lt;pfsizeY; j++){&lt;br /&gt;
  clearline[j]=1; //assume line is cleared&lt;br /&gt;
  for(unsigned char i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
    if(playfield[i][j]&amp;lt;=7){clearline[j]=0;break;} //line is not full&lt;br /&gt;
  }&lt;br /&gt;
  clearedlines+=clearline[j];&lt;br /&gt;
  templines+=clearline[j];&lt;br /&gt;
  }&lt;br /&gt;
  if(clearedlines&amp;gt;0){//breifly animate the cleared lines, then clear them&lt;br /&gt;
  &lt;br /&gt;
    for(unsigned char f=0; f&amp;lt;=4; f++){&lt;br /&gt;
    for(unsigned char j=4; j&amp;lt;pfsizeY; j++){&lt;br /&gt;
        if(clearline[j]==1){&lt;br /&gt;
          for(unsigned char i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
			  cdraw(i,j-4,f%2);&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      t_delay(2000);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    for(unsigned char j=4; j&amp;lt;pfsizeY; j++){ //accutally clear the lines&lt;br /&gt;
       if(clearline[j]==1){&lt;br /&gt;
        for(unsigned char t=j; t&amp;gt;=4; t--){&lt;br /&gt;
         for(unsigned char i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
           playfield[i][t]=playfield[i][t-1];&lt;br /&gt;
         }&lt;br /&gt;
        }&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
   lines+=templines; //add number of lines to lines counter&lt;br /&gt;
   switch(templines){ //calculate score&lt;br /&gt;
    case 1:&lt;br /&gt;
      score+=40*(level+1);&lt;br /&gt;
      break;&lt;br /&gt;
    case 2:&lt;br /&gt;
      score+=100*(level+1);&lt;br /&gt;
      break;&lt;br /&gt;
    case 3:&lt;br /&gt;
      score+=300*(level+1);&lt;br /&gt;
      break;&lt;br /&gt;
    default:&lt;br /&gt;
      score+=1200*(level+1);&lt;br /&gt;
      break;&lt;br /&gt;
   }&lt;br /&gt;
   for(unsigned char i=1; i&amp;lt;=templines; i++){ //see if the level needs increasing&lt;br /&gt;
    lslvi++;&lt;br /&gt;
    if(lslvi&amp;gt;=10){lslvi=0;level++;}&lt;br /&gt;
    if(level&amp;gt;=maxlevel){level=maxlevel;}&lt;br /&gt;
   }&lt;br /&gt;
  }&lt;br /&gt;
  //check gameover (scan through line 3 and see if there are any non-active pieces in it)&lt;br /&gt;
  for(unsigned char i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
    if(playfield[i][3]&amp;gt;7){&lt;br /&gt;
      //*********************GAME OVER********************&lt;br /&gt;
      //Serial.println(F(&amp;quot;GAME OVER&amp;quot;));&lt;br /&gt;
	  printf(&amp;quot;%cY%c%cGAME OVER&amp;quot;, 27, 32+14, 32+40);&lt;br /&gt;
      while(ngame==0){&lt;br /&gt;
        controls();&lt;br /&gt;
      }&lt;br /&gt;
      newgamemon();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  recopyscreen();&lt;br /&gt;
	  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void main(){&lt;br /&gt;
	printf(&amp;quot;%cE&amp;quot;, 27);&lt;br /&gt;
	printf(&amp;quot;%cY%c%cSoviet Bloc Game 2019&amp;quot;, 27, 32+1, 32+40);&lt;br /&gt;
	printf(&amp;quot;%cY%c%cScore:&amp;quot;, 27, 32+3, 32+40);&lt;br /&gt;
	printf(&amp;quot;%cY%c%cLevel:&amp;quot;, 27, 32+5, 32+40);&lt;br /&gt;
	printf(&amp;quot;%cY%c%cLines:&amp;quot;, 27, 32+7, 32+40);&lt;br /&gt;
	printf(&amp;quot;%cY%c%cNext&amp;quot;, 27, 32+4, 32+30);&lt;br /&gt;
	&lt;br /&gt;
	printf(&amp;quot;%cY%c%cJ K L to move&amp;quot;, 27, 32+10, 32+32);&lt;br /&gt;
	printf(&amp;quot;%cY%c%c A S  to rotate&amp;quot;, 27, 32+11, 32+32);&lt;br /&gt;
	printf(&amp;quot;%cY%c%c  @   to start new game&amp;quot;, 27, 32+12, 32+32);&lt;br /&gt;
	&lt;br /&gt;
	drawb(pfX-2, pfY-1, (pfsizeX+1)*clspX, ((pfsizeY-3)*clspY));&lt;br /&gt;
	&lt;br /&gt;
&lt;br /&gt;
	ngame=1;&lt;br /&gt;
	newgamemon();&lt;br /&gt;
	while(1){ //*********************MAIN*PROGRAM*LOOP**************************&lt;br /&gt;
  &lt;br /&gt;
  //normally a frame delay would go here, but the trs-80 is slow and we need all the speed we can get.&lt;br /&gt;
  &lt;br /&gt;
  unsigned char droppiece=0;&lt;br /&gt;
  dropframe++;&lt;br /&gt;
  if(dropframe&amp;gt;=(maxlevel-level)){&lt;br /&gt;
    dropframe=0;&lt;br /&gt;
    droppiece=1;&lt;br /&gt;
  }&lt;br /&gt;
  newgamemon(); &lt;br /&gt;
  &lt;br /&gt;
  if(lvladd!=0){&lt;br /&gt;
	  level+=lvladd;&lt;br /&gt;
	  lvladd=0;&lt;br /&gt;
	  recopyscreen();&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  if(newpc!=0){&lt;br /&gt;
	  newpc=0;&lt;br /&gt;
	  nextpiece();&lt;br /&gt;
	  recopyscreen();&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  if(rcontrol==1){&lt;br /&gt;
    pieceR++; if(pieceR&amp;gt;=5){pieceR=1;} //try to rotate piece&lt;br /&gt;
    if(checkCollide()){&lt;br /&gt;
      pieceR--; if(pieceR&amp;lt;=0){pieceR=4;} //undo rotation&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(rcontrol==2){&lt;br /&gt;
    pieceR--; if(pieceR&amp;lt;=0){pieceR=4;}&lt;br /&gt;
    if(checkCollide()){&lt;br /&gt;
      pieceR++; if(pieceR&amp;gt;=5){pieceR=1;}&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(dcontrol==4){&lt;br /&gt;
    pieceCX--; //try and see what happens if we move the piece left&lt;br /&gt;
    if(checkCollide()){//piece move is not valid&lt;br /&gt;
      pieceCX++; //take piece back&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(dcontrol==6){&lt;br /&gt;
    pieceCX++; //try and see what happens if we move the piece left&lt;br /&gt;
    if(checkCollide()){//piece move is not valid&lt;br /&gt;
      pieceCX--; //take piece back&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(dcontrol==8){&lt;br /&gt;
    pieceCY--; //try and see what happens if we move the piece up&lt;br /&gt;
    if(checkCollide()){//piece move is not valid&lt;br /&gt;
      pieceCY++; //take piece back&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(dcontrol==2||droppiece==1){&lt;br /&gt;
    pieceCY++; //try and see what happens if we move the piece down&lt;br /&gt;
    if(!(drepeatframe&amp;lt;=drepeatframes)){ //is in fast mode&lt;br /&gt;
      fdrop++;&lt;br /&gt;
    }else{&lt;br /&gt;
      fdrop=0;&lt;br /&gt;
    }&lt;br /&gt;
    if(checkCollide()){//piece move is not valid&lt;br /&gt;
      pieceCY--; //take piece back&lt;br /&gt;
      score+=fdrop; //add # of fast dropped blocks to score&lt;br /&gt;
      fdrop=0;&lt;br /&gt;
      piece2bg(); //copy piece to background and reset to next piece&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  clearControls();&lt;br /&gt;
  mdraw();&lt;br /&gt;
}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
==Nintendo DS version==&lt;br /&gt;
'''This is ridiculous. We took the TRS-80 version and developed it for a platform made 27 years later. And it works.'''&lt;br /&gt;
[[File:Wilson-tetris-NDS.png|thumb|Nintendo DS Version running. Note the split screens, one text, one graphics. The DS gives you almost nothing built in, it expects all game data to be provided in the game (including fonts and images), and there are only a handful included with ndslib. For me, that means one screen with graphics, and one with monochrome text.]]&lt;br /&gt;
Sure, there are already plenty of games available for the Nintendo DS, but if all you have is a Soviet Bloc Game clone, everything starts to look like a nail. The NDS defines handheld gaming for the children of my generation, so when I found one at my local rummage sale and paired it up with a generic China Special flash card so it could run programs off a micro sd card, I knew it had to be done.&lt;br /&gt;
&lt;br /&gt;
This is honestly the first time I have worked/played with an actual games machine since I was first learning to hack my Wii around 2010, and will be the first time I write a homebrew app of any kind. &lt;br /&gt;
&lt;br /&gt;
My port uses libnds (or is that ndslib?), compiled with the devkitPro enviroment. There isn't much development on the NDS nowadays, but even in 2019 I found updated instructions for installing an up to date development enviroment.&lt;br /&gt;
&lt;br /&gt;
I learned that the nds has a ton of fancy game featrues (as you might expect), like sprites and special memory hardware that can make really fast copies of some things. It also has two seperate processors, which do completely diferent things (update the screen and check the inputs, for example) yet still make a complete system work. This is, needless to say, much more complicated than the things I usually work on.&lt;br /&gt;
&lt;br /&gt;
However, I decided to forgo all the low level work and keep things simple. There is one video mode which, instead of making up sprites or loading in complex background images, is just that you write to a certain spot in memory with a certain value, and it turns a certain pixel of the screen a certain color (aka framebuffer mode). This is really easy to work with, although there are no included commands to draw lines or crcles or anything like that, so I added a few functions for this (although all I need to draw is boxes, which does make it somewhat easier.) This mode is also a lot slower than what the Ds is truely caplable of doing apparently, but I found it was more than fast enough to make Soviet Bloc Game work correctly. &lt;br /&gt;
&lt;br /&gt;
The DS is also equipped with color screens, so I got to try coloring the blocks for the first time; this too was very easy, since I had kept track of which block belongs to which piece type, all I had to do was assign a color to each piece when going through and printing the playfield matrix. In doing this, I left out a bit of the code from the TRS-80 version that only plots things on the screen where it needs to be changed (this was absolutely nessacary in the TRS-80 version to get a playable game), but I found that the DS was so fast that it didn't matter at all. So now the entire piece carrier gets redrawn instead of just those tiles which have changed, but I could probably redraw the entire playfield too without any issue at all because speed is very high.&lt;br /&gt;
&lt;br /&gt;
Speaking of speed, this ended up being a problem. In my two older versions, gameplay was quite slow. On the DS, I needed to introduce a lockout so that the next piece would not immediatly drop down while still holding the DOWN key. Then, it was a matter of adding frame delays to make a game that was not too fast (though I did increase the number of levels (speedup as lines are cleared) for faster gameplay as you keep playing).&lt;br /&gt;
&lt;br /&gt;
Other than that, the port went smoothly, and as usual the hardest parts were trying to figure out how to get inputs from the buttons and how to draw blocks on the screen. Once I got that done I had a working version of Soviet Bloc Game about 30 seconds later, and the rest was fine tuning it.&lt;br /&gt;
&lt;br /&gt;
The last step is, hey, the DS has speakers. The example code includes a way to play MOD-like tracker files (XM, S3M, and others). So, why not finish it out with a poorly rendered Midi version of All Star by Smash Mouth? This was literally a matter of adding about 5 lines to my existing code, and disabling a couple things in the setup that it turned out I wasn't using anyways. The player takes everything in the background, while playing Soviet Bloc Game still. It uses some hardware (interrupts, timers and the like) so you can't drop it into just any source code you've got, but for one like Soviet Bloc Game that barely uses any special hardware it works fine.&lt;br /&gt;
&lt;br /&gt;
==IBM-PC/DOS Version==&lt;br /&gt;
I got a bunch of 5-1/4 floppy drives recently and used them to fix the first computer I learned to program - the Sanyo MBC-550. This 1983 computer comes with MS-DOS and BASIC, but is not IBM-PC compatible (no CGA card, for instance, instead having it's own internal color graphics). As such, it has very little games availible for it.&lt;br /&gt;
&lt;br /&gt;
Since I had never written any code for a 8088 based system, and I have five computers that use a pre-286 CPU, I decided to port my Soviet Bloc Game to it. GCC does not work for anything below a 386, but Open WATCOM does, so that's what I used. &lt;br /&gt;
&lt;br /&gt;
This C comiler comes with modes for choosing text modes, but like most IBM PC software, it directly accesses the hardware to make it work. This makes things incompatible with the Sanyo. The Sanyo does support standard BIOS Interrupt calls, which are slower than the direct hardware calls but are fast enough for Soviet Bloc Game. However, I discovered that C functions to do text manipulation are too slow for Soviet Bloc Game, at least on a slow system like the Sanyo. So, the sections to write graphics and also read keys on the keyboard are done in Assembly. For proper timing between system of different clock speeds, I used the system tick - Int 1aH, which increments a counter about 18.2 times per second. &lt;br /&gt;
&lt;br /&gt;
So now, there is a version of my Soviet Bloc Game that works on pretty much any computer that runs MS-DOS.&lt;br /&gt;
&lt;br /&gt;
==Sources==&lt;br /&gt;
Here is a link to the source files and a compiled version: [[File:Wilson-tetris-NDS-t2.zip]]. You can build this just by running &amp;lt;code&amp;gt;make&amp;lt;/code&amp;gt; from within the devkitPro console.&lt;br /&gt;
&lt;br /&gt;
[[File:Wilson-tetris-PC.zip]] - the DOS/PC version&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=Wilson_Tetris&amp;diff=600</id>
		<title>Wilson Tetris</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=Wilson_Tetris&amp;diff=600"/>
		<updated>2023-09-20T20:19:05Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: Alnwlsn moved page Wilson Tetris to Soviet Bloc Game (C version): trademark infringement&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Soviet Bloc Game (C version)]]&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=Soviet_Bloc_Game_(C_version)&amp;diff=599</id>
		<title>Soviet Bloc Game (C version)</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=Soviet_Bloc_Game_(C_version)&amp;diff=599"/>
		<updated>2023-09-20T20:19:05Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: Alnwlsn moved page Wilson Tetris to Soviet Bloc Game (C version): trademark infringement&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:Wilson-tetris-trs80.png|thumb|Soviet Bloc Game v1.1 running in a trs-80 emulator]]&lt;br /&gt;
While thinking about ways to make my TRS-80 model I more useable in 2019, I realized that I could not easily find a version of Soviet Bloc Game for it (probably because the TRS-80 came out about 10ish years before Soviet Bloc Game was invented), so I thought I might try to write my own after watching an episode of ''the 8-bit Guy'' where David does the same. I used to play the Gameboy version of Soviet Bloc Game all the time on my Ti-nSpire calculator thanks to the nspire hacking scene and an emulator port, so that's the style of Soviet Bloc Game I will try to recreate.&lt;br /&gt;
&lt;br /&gt;
However, I have written few games, and never done a Soviet Bloc Game clone before, so I approach this mostly as a challenge.&lt;br /&gt;
&lt;br /&gt;
Logal notice: &amp;quot;Tetris&amp;quot; is a trademark of The Tetris Company (who have been known to take down unauthorized trademark versions before: https://github.com/jdah/tetris-os), so I have decided to rename this clone &amp;quot;Soviet Bloc Game&amp;quot;&lt;br /&gt;
&lt;br /&gt;
'''Objectives:'''&lt;br /&gt;
* Playfield the same size as GB version&lt;br /&gt;
* in color?&lt;br /&gt;
* same or similar scoring system to GB version&lt;br /&gt;
* same rotation style as GB version&lt;br /&gt;
* easily portable to different platforms (that are C like)&lt;br /&gt;
* somewhat wellishly documented&lt;br /&gt;
&lt;br /&gt;
This ended up being a sort of 24 hour challenge; I started this project at about 11pm yesterday, and by about the time of this page's creation I had created an Arduino version of Soviet Bloc Game that met most of these objectives.&lt;br /&gt;
&lt;br /&gt;
==Theory of Operation==&lt;br /&gt;
===Basics===&lt;br /&gt;
The basics of the game's functioning can be summed up with just a few components: a part that generates the  shapes, a playfield that contains already played pieces and open spots where the active piece can move and collision detection that says where the active piece can and can't go.&lt;br /&gt;
&lt;br /&gt;
The playfield size is adjustable, but in the gameboy version it's 10 wide by 18 tall. In my version I added an extra 4 lines on top (which are not to be displayed) for the new pieces to appear within. Within the program, it appears as a big 2D matrix called &amp;lt;code&amp;gt;playfield[][]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
====Drawing the pieces====&lt;br /&gt;
Rather than figure out all the complex rotations of the part, I hardcoded them (using the Nintendo rotation system as described at https://tetris.fandom.com/wiki/Nintendo_Rotation_System). The parts appear in a 4x4 piece container &amp;lt;code&amp;gt;pieceC[][]&amp;lt;/code&amp;gt; matrix. This is then overlaid onto the playfield, and can be moved around. Nonzero cells of the matrix define the shape of the piece, like:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 0|| 0|| 0|| 0&lt;br /&gt;
|-&lt;br /&gt;
| 0|| 5|| 0|| 0&lt;br /&gt;
|-&lt;br /&gt;
| 5|| 5|| 0|| 0&lt;br /&gt;
|-&lt;br /&gt;
| 0|| 5|| 0|| 0&lt;br /&gt;
|}&lt;br /&gt;
The numbers used to define the piece are unique to each piece, so if you write the display section to use each number as a color, you can have a color game.&lt;br /&gt;
&lt;br /&gt;
====Collision detection====&lt;br /&gt;
In the collision detection section, the overlaid piece carrier is moved as if you were to make that move, and then we check if any of the cells overlap with non-blank sections of the playfield, or if any part of the active piece lies outside the playfield area. If so, the move is undone, and you can decide what to do from there. If no collision occurs, the move is done for real and displayed the next time the screen is updated.&lt;br /&gt;
 &lt;br /&gt;
Usually, the collision detection is used to prevent motion if a piece or wall is in the way, but if the piece is attempting to move downwards, the piece is instead copied onto the playfield, and the numbers making up the piece are increased by 8. This is how the collision detection can differentiate between the active piece and dead pieces in the playfield.&lt;br /&gt;
&lt;br /&gt;
====Input conditioning====&lt;br /&gt;
I added some input conditioning as well. For rotations of the active piece, the move is not repeating, and you must release the rotation input and press it again for a rotation to happen again. For moves left, right, and down, a system similar to repeating keys on a computer keyboard is used, holding down the direction for a brief moment will make the action repeat. In the gameboy version, dropping a piece quickly will increase the score based on the number of tiles dropped while moving fast. This is also replicated (but probably not perfectly) in my version.&lt;br /&gt;
&lt;br /&gt;
===Details===&lt;br /&gt;
====Levels and speed====&lt;br /&gt;
I only update the screen every few moments. Call the time of one of these updates a '''frame'''. While the screen is waiting to be updated, it's a good time to check the controls and see what we need to do the next time the screen needs updating. All the processing of the moves and collisions happens just before the screen updates. Every few frames, the active piece drops one row. This is connected to the levels, which in my version go from 0-20, which I think is in line with the gb version. The drop happens every 20 frames at level 0, up to a delay of 0 frames at level 20. For every 10 lines that the user clears, the level is increased by 1, so that the game gets progressively faster, just like the gb version. &lt;br /&gt;
====Game over detection====&lt;br /&gt;
This one is pretty simple. If any non-active piece sections are found on the line above the top of the visible playfield, the game ends. Check this every frame.&lt;br /&gt;
====Line completion detection====&lt;br /&gt;
This is a little more complicated than the game over, but basically I check to see if any rows are completely filled in with dead pieces. I keep track of which lines are full (and how many of them there are), and animate the corresponding lines by having the whole row(s) flash for a moment. At this point I also add onto the number of completed lines counter displayed on the scoreboard. Then, I copy all the lines above the filled line down by one tile. Since this is done for each filled line in sequence, the effect moves the playfield down for all cases of any number of filled lines.  &lt;br /&gt;
====Scoring====&lt;br /&gt;
The number of filled lines cleared in one move (calculated above) is used in the scoring formula to add to the score. The full formula (according to the Nintendo scoring system) is:&lt;br /&gt;
* 1 line: 40*(level+1)&lt;br /&gt;
* 2 lines: 100*(level+1)&lt;br /&gt;
* 3 lines: 300*(level+1)&lt;br /&gt;
* 4 lines: 1200*(level+1) &lt;br /&gt;
&lt;br /&gt;
Additionally, the number of grid tiles that are dropped by the user pressing &amp;quot;down&amp;quot; (ie, soft drop), which I kept track of earlier, are also added to the score.&lt;br /&gt;
====New game====&lt;br /&gt;
I have a new game button also. When pressed (and the controls are checked), it picks new pieces, clears the level, score, and lines counters, and empties the playfield of dead pieces. Thus, a new game begins.&lt;br /&gt;
&lt;br /&gt;
==Hardware (Arduino version)==&lt;br /&gt;
I do most of my software development on arduino-like embedded systems, and am pretty familiar with I/O options on these devices (more so than on a stndard computer), so I used an arduino nano with a 128x64 st7920 monochrome LCD and the u8g2 libray for the display. For input, I wired up 3 buttons, 2 rotate and one new game, and an analog joystick (which I am using as a digital one) for directional moves.&lt;br /&gt;
&lt;br /&gt;
===Porting===&lt;br /&gt;
With any luck, my code can be ported to different platforms just my changing the section that displays the playfield on a screen, the input sections that take in the button presses, and maybe the delay sections. On the Arduino, my code takes up about 50% of the prgram storage space, and 50% of ram, with most of that being due to the u8g2 library. I'm not an expert on the super low-level processes on the arduino, nor do I think my game is the most effceiently coded, but my guess is that if the game works well on a system with as low specifications as the atmega328, it should be able to run on nearly anything else.&lt;br /&gt;
&lt;br /&gt;
==The Code (Arduino version)==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#define pfsizeX 10&lt;br /&gt;
#define pfsizeY 22 //note that top 4 lines are not drawn&lt;br /&gt;
uint8_t playfield[pfsizeX][pfsizeY];  //the game area&lt;br /&gt;
&lt;br /&gt;
uint8_t pieceC[4][4]; //piece container&lt;br /&gt;
int8_t pieceCX;  //location of upper left corner of piece container&lt;br /&gt;
int8_t pieceCY;&lt;br /&gt;
uint8_t pieceT; //type of piece&lt;br /&gt;
uint8_t pieceR; //rotation of piece &lt;br /&gt;
uint8_t nextpieceT; &lt;br /&gt;
uint8_t nextpieceR; &lt;br /&gt;
&lt;br /&gt;
uint8_t dcontrol; //locks in control every frame&lt;br /&gt;
uint8_t rcontrol;&lt;br /&gt;
&lt;br /&gt;
uint8_t lastdcontrol=0; //for &amp;quot;debouncing&amp;quot; of inputs&lt;br /&gt;
uint8_t lastrcontrol=0; &lt;br /&gt;
uint8_t drepeatframe=0;&lt;br /&gt;
#define drepeatframes 3 //wait _ frames before repeatedly going in one direction&lt;br /&gt;
&lt;br /&gt;
uint8_t dropframe=0;    //counter for number of frames between block drops&lt;br /&gt;
uint8_t level=0; //LEVEL decreases frame drop from 20 frames to 0 frames (levels 0 to 20)&lt;br /&gt;
&lt;br /&gt;
boolean ngame=0; //when set, starts new game&lt;br /&gt;
&lt;br /&gt;
uint16_t lines=0; //NUMBER OF LINES CLEARED&lt;br /&gt;
uint32_t score=0; //TOTAL SCORE (using NES rules)&lt;br /&gt;
&lt;br /&gt;
uint8_t fdrop;   //number of blocks that piece has been fast dropped&lt;br /&gt;
uint8_t lslvi=0; //lines since level increase (when this gets to 10, increase the level)&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;U8g2lib.h&amp;gt;&lt;br /&gt;
U8G2_ST7920_128X64_1_HW_SPI u8g2(U8G2_R0, /* CS=*/ 12, /* reset=*/ 8);&lt;br /&gt;
&lt;br /&gt;
void dispscreen(){&lt;br /&gt;
  u8g2.firstPage();  &lt;br /&gt;
  do {&lt;br /&gt;
    for(uint8_t j=4; j&amp;lt;pfsizeY; j++){ //draw screen&lt;br /&gt;
    for(uint8_t i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
      if(playfield[i][j]!=0){&lt;br /&gt;
        u8g2.drawBox(5*(j-4),5*(pfsizeX-1)-5*i,6,6);&lt;br /&gt;
      }else{&lt;br /&gt;
        u8g2.drawFrame(5*(j-4),5*(pfsizeX-1)-5*i,6,6);&lt;br /&gt;
        //if(j==3){u8g2.drawLine(5*j,5*(pfsizeX-1)-5*i,5*j+5,5*(pfsizeX-1)-5*i+5);}&lt;br /&gt;
      }&lt;br /&gt;
    }}&lt;br /&gt;
&lt;br /&gt;
  uint8_t temppieceT=pieceT; &lt;br /&gt;
  uint8_t temppieceR=pieceR;&lt;br /&gt;
  pieceT=nextpieceT;&lt;br /&gt;
  pieceR=nextpieceR;&lt;br /&gt;
  loadpiece(); &lt;br /&gt;
  for(uint8_t j=0; j&amp;lt;4; j++){ //draw next piece&lt;br /&gt;
  for(uint8_t i=0; i&amp;lt;4; i++){&lt;br /&gt;
    if(pieceC[i][j]!=0){&lt;br /&gt;
      u8g2.drawBox(5*(j+pfsizeY-3),5*(pfsizeX-1)-5*i,6,6);&lt;br /&gt;
    }else{&lt;br /&gt;
      u8g2.drawFrame(5*(j+pfsizeY-3),5*(pfsizeX-1)-5*i,6,6);&lt;br /&gt;
    }&lt;br /&gt;
  }}&lt;br /&gt;
  pieceT=temppieceT;&lt;br /&gt;
  pieceR=temppieceR;&lt;br /&gt;
  loadpiece();&lt;br /&gt;
&lt;br /&gt;
  u8g2.setFont(u8g2_font_6x10_tf);&lt;br /&gt;
  char zbuffer[32];&lt;br /&gt;
  sprintf(zbuffer, &amp;quot;N%d&amp;quot;, lines);&lt;br /&gt;
  u8g2.drawStr(0,60, zbuffer);&lt;br /&gt;
  sprintf(zbuffer, &amp;quot;L%d&amp;quot;, level);&lt;br /&gt;
  u8g2.drawStr(25,60, zbuffer);&lt;br /&gt;
  sprintf(zbuffer, &amp;quot;S%d&amp;quot;, score);&lt;br /&gt;
  u8g2.drawStr(50,60, zbuffer);  &lt;br /&gt;
  } while( u8g2.nextPage() );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void controls(){&lt;br /&gt;
  if(digitalRead(A3)==0){ngame=1;}&lt;br /&gt;
  if(dcontrol==0){&lt;br /&gt;
    if(analogRead(A7)&amp;lt;10){dcontrol=4;}&lt;br /&gt;
    if(analogRead(A7)&amp;gt;1014){dcontrol=6;}&lt;br /&gt;
    if(analogRead(A6)&amp;gt;1014){dcontrol=8;}&lt;br /&gt;
    if(analogRead(A6)&amp;lt;10){dcontrol=2;}&lt;br /&gt;
    if(dcontrol==0){drepeatframe=0;}&lt;br /&gt;
    if(dcontrol==lastdcontrol){          //short delay before fast motion&lt;br /&gt;
      if(drepeatframe&amp;lt;=drepeatframes){&lt;br /&gt;
        drepeatframe++;&lt;br /&gt;
        dcontrol=3; //lockout if within lockout period&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(rcontrol==0){&lt;br /&gt;
    if(digitalRead(A4)==0){rcontrol=1;}&lt;br /&gt;
    if(digitalRead(A5)==0){rcontrol=2;}&lt;br /&gt;
    if(rcontrol==lastrcontrol){rcontrol=3;}&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void clearControls(){&lt;br /&gt;
  if(rcontrol!=3){lastrcontrol=rcontrol;}&lt;br /&gt;
  if(dcontrol!=3){lastdcontrol=dcontrol;}&lt;br /&gt;
  rcontrol=0;&lt;br /&gt;
  dcontrol=0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void draw(){&lt;br /&gt;
  loadpiece(); //load piece into the piece carrier&lt;br /&gt;
  for(uint8_t i=0; i&amp;lt;pfsizeX; i++){   //clear any cells with active piece parts (will be written again with new pieceC&lt;br /&gt;
    for(uint8_t j=0; j&amp;lt;pfsizeY; j++){&lt;br /&gt;
      if(playfield[i][j]&amp;lt;=7){playfield[i][j]=0;}&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  for(uint8_t i=0; i&amp;lt;4; i++){ //copy active piece onto the playfield&lt;br /&gt;
    for(uint8_t j=0; j&amp;lt;4; j++){&lt;br /&gt;
      if(pieceCX+i&amp;gt;=0&amp;amp;&amp;amp;pieceCX+i&amp;lt;pfsizeX&amp;amp;&amp;amp;pieceCY+j&amp;gt;=0&amp;amp;&amp;amp;pieceCY+j&amp;lt;pfsizeY){//check if piece segment can be drawn on screen&lt;br /&gt;
        if(pieceC[i][j]!=0){playfield[i+pieceCX][j+pieceCY]=pieceC[i][j];}&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
boolean checkCollide(){ //move the piece carrier first, then check if anything collides&lt;br /&gt;
  loadpiece(); //load piece into the piece carrier&lt;br /&gt;
  boolean nonvalidity=0;&lt;br /&gt;
  for(uint8_t i=0; i&amp;lt;4; i++){ //run through all piece carrier cells&lt;br /&gt;
    for(uint8_t j=0; j&amp;lt;4; j++){&lt;br /&gt;
      if(pieceCX+i&amp;gt;=0&amp;amp;&amp;amp;pieceCX+i&amp;lt;pfsizeX&amp;amp;&amp;amp;pieceCY+j&amp;gt;=0&amp;amp;&amp;amp;pieceCY+j&amp;lt;pfsizeY){ //check if piece carrier segment can be drawn on screen&lt;br /&gt;
        if(pieceC[i][j]!=0&amp;amp;&amp;amp;playfield[i+pieceCX][j+pieceCY]&amp;gt;7){ //if both background and nonzero piece carrier segment collide&lt;br /&gt;
          nonvalidity=1;&lt;br /&gt;
        }&lt;br /&gt;
      }else{ //this segment of PC can't be drawn on the screen&lt;br /&gt;
        if(pieceC[i][j]!=0){ //a filled in segment would be drawn offscreen&lt;br /&gt;
          nonvalidity=1;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  return nonvalidity;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void piece2bg(){&lt;br /&gt;
  for(uint8_t i=0; i&amp;lt;4; i++){ //copy active piece onto the screen&lt;br /&gt;
  for(uint8_t j=0; j&amp;lt;4; j++){&lt;br /&gt;
    if(pieceC[i][j]!=0){playfield[i+pieceCX][j+pieceCY]=pieceC[i][j]+8;} //copy the piece into the playfield/background &lt;br /&gt;
  }}&lt;br /&gt;
  nextpiece();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void nextpiece(){//generate the next piece and move PC back to the top&lt;br /&gt;
  pieceT=nextpieceT;&lt;br /&gt;
  pieceR=nextpieceR;&lt;br /&gt;
  nextpieceT = rand()%7 + 1;&lt;br /&gt;
  nextpieceR = rand()%4 + 1;&lt;br /&gt;
  pieceCY=0; //move piece carrier back to the top of screen&lt;br /&gt;
  pieceCX=3;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void newgamemon(){&lt;br /&gt;
  if(ngame==0){return;}&lt;br /&gt;
  Serial.println(F(&amp;quot;NEW GAME&amp;quot;));&lt;br /&gt;
  ngame=0;&lt;br /&gt;
  for(uint8_t i=0; i&amp;lt;pfsizeX; i++){   //clear any cells with active piece parts (will be written again with new pieceC&lt;br /&gt;
    for(uint8_t j=0; j&amp;lt;pfsizeY; j++){&lt;br /&gt;
      playfield[i][j]=0;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  lines=0;&lt;br /&gt;
  level=0;&lt;br /&gt;
  score=0;&lt;br /&gt;
  lslvi=0;&lt;br /&gt;
  fdrop=0;&lt;br /&gt;
  nextpiece();&lt;br /&gt;
  nextpiece();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loadpiece(){ //hardcoded all pieces&lt;br /&gt;
  switch(pieceT){&lt;br /&gt;
    case 1: //long one&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=1; pieceC[1][2]=1; pieceC[2][2]=1; pieceC[3][2]=1;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0; &lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=1; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=1; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=0; pieceC[2][2]=1; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=1; pieceC[3][3]=0; &lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 2: //backwards L&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=2; pieceC[1][2]=2; pieceC[2][2]=2; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=2; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=2; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=2; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=2; pieceC[1][3]=2; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=2; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=2; pieceC[1][2]=2; pieceC[2][2]=2; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=2; pieceC[2][1]=2; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=2; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=2; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 3: //L&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=3; pieceC[1][2]=3; pieceC[2][2]=3; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=3; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=3; pieceC[1][1]=3; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=3; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=3; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=3; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=3; pieceC[1][2]=3; pieceC[2][2]=3; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=3; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=3; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=3; pieceC[2][3]=3; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 4: //s shape&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=4; pieceC[2][2]=4; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=4; pieceC[1][3]=4; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=4; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=4; pieceC[2][2]=4; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=4; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 5: //T shape&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=5; pieceC[1][2]=5; pieceC[2][2]=5; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=5; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=5; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=5; pieceC[1][2]=5; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=5; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=5; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=5; pieceC[1][2]=5; pieceC[2][2]=5; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=5; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=5; pieceC[2][2]=5; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=5; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 6: //reverse s&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=6; pieceC[1][2]=6; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=6; pieceC[2][3]=6; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=6; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=6; pieceC[2][2]=6; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=6; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 7: //square&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      default:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=7; pieceC[2][1]=7; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=7; pieceC[2][2]=7; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setup(){&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  u8g2.begin();&lt;br /&gt;
  pinMode(A4, INPUT_PULLUP);&lt;br /&gt;
  pinMode(A5, INPUT_PULLUP);&lt;br /&gt;
  pinMode(A3, INPUT_PULLUP); //new game button&lt;br /&gt;
&lt;br /&gt;
  ngame=1;&lt;br /&gt;
  newgamemon();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
  &lt;br /&gt;
  for(uint32_t h=0;h&amp;lt;=5;h++){//frame delay  //DELAY SECTION (BETWEEN FRAMES)&lt;br /&gt;
    delay(1);&lt;br /&gt;
    controls();&lt;br /&gt;
    newgamemon();&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  boolean droppiece=0;&lt;br /&gt;
  dropframe++;&lt;br /&gt;
  if(dropframe&amp;gt;=(20-level)){&lt;br /&gt;
    dropframe=0;&lt;br /&gt;
    droppiece=1;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  if(rcontrol==1){&lt;br /&gt;
    pieceR++; if(pieceR&amp;gt;=5){pieceR=1;} //try to rotate piece&lt;br /&gt;
    if(checkCollide()){&lt;br /&gt;
      pieceR--; if(pieceR&amp;lt;=0){pieceR=4;} //undo rotation&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(rcontrol==2){&lt;br /&gt;
    pieceR--; if(pieceR&amp;lt;=0){pieceR=4;}&lt;br /&gt;
    if(checkCollide()){&lt;br /&gt;
      pieceR++; if(pieceR&amp;gt;=5){pieceR=1;}&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(dcontrol==4){&lt;br /&gt;
    pieceCX--; //try and see what happens if we move the piece left&lt;br /&gt;
    if(checkCollide()){//piece move is not valid&lt;br /&gt;
      pieceCX++; //take piece back&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(dcontrol==6){&lt;br /&gt;
    pieceCX++; //try and see what happens if we move the piece left&lt;br /&gt;
    if(checkCollide()){//piece move is not valid&lt;br /&gt;
      pieceCX--; //take piece back&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(dcontrol==8){&lt;br /&gt;
    pieceCY--; //try and see what happens if we move the piece up&lt;br /&gt;
    if(checkCollide()){//piece move is not valid&lt;br /&gt;
      pieceCY++; //take piece back&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(dcontrol==2||droppiece==1){&lt;br /&gt;
    pieceCY++; //try and see what happens if we move the piece down&lt;br /&gt;
    if(!(drepeatframe&amp;lt;=drepeatframes)){ //is in fast mode&lt;br /&gt;
      fdrop++;&lt;br /&gt;
    }else{&lt;br /&gt;
      fdrop=0;&lt;br /&gt;
    }&lt;br /&gt;
    if(checkCollide()){//piece move is not valid&lt;br /&gt;
      pieceCY--; //take piece back&lt;br /&gt;
      score+=fdrop; //add # of fast dropped blocks to score&lt;br /&gt;
      fdrop=0;&lt;br /&gt;
      piece2bg(); //copy piece to background and reset to next piece&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  draw();&lt;br /&gt;
  dispscreen();&lt;br /&gt;
  &lt;br /&gt;
  clearControls();&lt;br /&gt;
&lt;br /&gt;
  //check for line clears&lt;br /&gt;
  boolean clearline[pfsizeY];&lt;br /&gt;
  uint8_t clearedlines=0;&lt;br /&gt;
  uint8_t templines=0;&lt;br /&gt;
  for(uint8_t j=4; j&amp;lt;pfsizeY; j++){&lt;br /&gt;
  clearline[j]=1; //assume line is cleared&lt;br /&gt;
  for(uint8_t i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
    if(playfield[i][j]&amp;lt;=7){clearline[j]=0;break;} //line is not full&lt;br /&gt;
  }&lt;br /&gt;
  clearedlines+=clearline[j];&lt;br /&gt;
  templines+=clearline[j];&lt;br /&gt;
  }&lt;br /&gt;
  if(clearedlines&amp;gt;0){//breifly animate the cleared lines, then clear them&lt;br /&gt;
    for(uint8_t f=0; f&amp;lt;=6; f++){&lt;br /&gt;
      for(uint8_t j=4; j&amp;lt;pfsizeY; j++){&lt;br /&gt;
        if(clearline[j]==1){&lt;br /&gt;
          for(uint8_t i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
            if(f%2==0){playfield[i][j]=8;}else{playfield[i][j]=0;}&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      draw();&lt;br /&gt;
      dispscreen();&lt;br /&gt;
      delay(200);&lt;br /&gt;
    }&lt;br /&gt;
    for(uint8_t j=4; j&amp;lt;pfsizeY; j++){ //accutally clear the lines&lt;br /&gt;
       if(clearline[j]==1){&lt;br /&gt;
        for(uint8_t t=j; t&amp;gt;=4; t--){&lt;br /&gt;
         for(uint8_t i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
           playfield[i][t]=playfield[i][t-1];&lt;br /&gt;
         }&lt;br /&gt;
        }&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
   lines+=templines; //add number of lines to lines counter&lt;br /&gt;
   switch(templines){ //calculate score&lt;br /&gt;
    case 1:&lt;br /&gt;
      score+=40*(level+1);&lt;br /&gt;
      break;&lt;br /&gt;
    case 2:&lt;br /&gt;
      score+=100*(level+1);&lt;br /&gt;
      break;&lt;br /&gt;
    case 3:&lt;br /&gt;
      score+=300*(level+1);&lt;br /&gt;
      break;&lt;br /&gt;
    default:&lt;br /&gt;
      score+=1200*(level+1);&lt;br /&gt;
      break;&lt;br /&gt;
   }&lt;br /&gt;
   for(uint8_t i=1; i&amp;lt;=templines; i++){ //see if the level needs increasing&lt;br /&gt;
    lslvi++;&lt;br /&gt;
    if(lslvi&amp;gt;=10){lslvi=0;level++;}&lt;br /&gt;
    if(level&amp;gt;=20){level=20;}&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
  //check gameover (scan through line 3 and see if there are any non-active pieces in it)&lt;br /&gt;
  for(uint8_t i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
    if(playfield[i][3]&amp;gt;7){&lt;br /&gt;
      //*********************GAME OVER********************&lt;br /&gt;
      Serial.println(F(&amp;quot;GAME OVER&amp;quot;));&lt;br /&gt;
      while(ngame==0){&lt;br /&gt;
        controls();&lt;br /&gt;
      }&lt;br /&gt;
      newgamemon();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  //check for completed lines&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
==The Code (TRS-80 version)==&lt;br /&gt;
I used the z88dk C compiler for the port. To compile, use &amp;lt;code&amp;gt;zcc  +trs80 -lndos -lm -create-app soviet-bloc-game.c -o soviet-bloc-game&amp;lt;/code&amp;gt; which will create a &amp;lt;code&amp;gt;.CAS&amp;lt;/code&amp;gt; file. Use &amp;lt;code&amp;gt;zcc  +trs80 -lndos -lm -create-app -subtype=disk soviet-bloc-game.c -o soviet-bloc-game&amp;lt;/code&amp;gt; to create a &amp;lt;code&amp;gt;.CMD&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Porting this to the TRS-80 turned out to be a much harder challange, mostly because the TRS-80 is a very slow, low power machine. Some of the improvements were obvious, like only checking the lines and game over only when an active piece becomes inactive. However, this isn't enough to make a playable version. The TRS-80 is slow enough that every comparison, operation, and graphics plot add noticable delay. Therefore, I only update parts of the display when needed (ie, the area around the active piece, 1 block around the piece carrier); the background is filled back in to erase piece moves, and the screen is only redrawn at the end when the active piece becomes inactive. This is possible because unlike the arduino version, the whole screen doesn't need to be rewitten every time it changes. I also had to take out any frame delay and reduce the levels to 10 because the machine is not fast enough to keep up. The version below is therefore clunky, but playable. The obvious next steps are to make an assembler version of Soviet Bloc Game.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//soviet-bloc-game.c - Soviet Bloc Game for TRS-80 - 05/28/2019&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt; &lt;br /&gt;
#include &amp;lt;graphics.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define pfsizeX 10 //playfield size (in cells)&lt;br /&gt;
#define pfsizeY 22 //note that top 4 lines are not drawn&lt;br /&gt;
#define pfX 8 //upper left corner of playfield location (pixel)&lt;br /&gt;
#define pfY 4&lt;br /&gt;
#define npX 60 //upper left corner of next piece indicator&lt;br /&gt;
#define npY 2&lt;br /&gt;
#define clspX 4 //spacing between cell elements (pixels)&lt;br /&gt;
#define clspY 2&lt;br /&gt;
&lt;br /&gt;
void controls(void);&lt;br /&gt;
&lt;br /&gt;
unsigned char playfield[pfsizeX][pfsizeY];  //the game area&lt;br /&gt;
unsigned char cscreen[pfsizeX][pfsizeY-4];  //a copy of what gets drawn on the screen (game area)&lt;br /&gt;
unsigned char pieceC[4][4]; //piece container&lt;br /&gt;
signed char pieceCX;  //location of upper left corner of piece container&lt;br /&gt;
signed char pieceCY;&lt;br /&gt;
unsigned char pieceT; //type of piece&lt;br /&gt;
unsigned char pieceR; //rotation of piece &lt;br /&gt;
unsigned char nextpieceT; &lt;br /&gt;
unsigned char nextpieceR; &lt;br /&gt;
&lt;br /&gt;
unsigned char dcontrol; //locks in control every frame&lt;br /&gt;
unsigned char rcontrol;&lt;br /&gt;
&lt;br /&gt;
unsigned char lastdcontrol=0; //for &amp;quot;debouncing&amp;quot; of inputs&lt;br /&gt;
unsigned char lastrcontrol=0; &lt;br /&gt;
unsigned char drepeatframe=0;&lt;br /&gt;
#define drepeatframes 3 //wait _ frames before repeatedly going in one direction&lt;br /&gt;
&lt;br /&gt;
unsigned char dropframe=0;    //counter for number of frames between block drops&lt;br /&gt;
unsigned char level=0; //LEVEL decreases frame drop from maxlevel frames to 0 frames (levels 0 to MAXLEVEL)&lt;br /&gt;
#define maxlevel 10&lt;br /&gt;
&lt;br /&gt;
unsigned char ngame=1; //when set, starts new game&lt;br /&gt;
unsigned char newpc=0; //when set, get a new piece&lt;br /&gt;
&lt;br /&gt;
unsigned int lines=0; //NUMBER OF LINES CLEARED&lt;br /&gt;
long score=0; //TOTAL SCORE (using NES rules)&lt;br /&gt;
&lt;br /&gt;
unsigned char fdrop;   //number of blocks that piece has been fast dropped&lt;br /&gt;
unsigned char lslvi=0; //lines since level increase (when this gets to 10, increase the level)&lt;br /&gt;
&lt;br /&gt;
char lvladd=0;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void setblock(char x, char y){&lt;br /&gt;
	for(char i=0; i&amp;lt;8; i++){&lt;br /&gt;
		controls();&lt;br /&gt;
     	plot(x+i%4,y+i/4);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
void clearblock(char x, char y){&lt;br /&gt;
	for(char i=0; i&amp;lt;8; i++){&lt;br /&gt;
		controls();&lt;br /&gt;
    	unplot(x+i%4,y+i/4);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
void cdraw(char x, char y, char color){//only draws on screen if it needs to (uses cell coordinates)&lt;br /&gt;
	if(cscreen[x][y]!=color){&lt;br /&gt;
		cscreen[x][y]=color;&lt;br /&gt;
		if(color!=0){&lt;br /&gt;
			setblock( (x)*clspX+pfX, (y)*clspY+pfY );&lt;br /&gt;
		}else{&lt;br /&gt;
			clearblock( (x)*clspX+pfX, (y)*clspY+pfY);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
void recopyscreen(){ //copies playfield to screen &lt;br /&gt;
  for(char i=0; i&amp;lt;pfsizeX; i++){ //manually redraw the entire screen&lt;br /&gt;
  for(char j=4; j&amp;lt;pfsizeY; j++){&lt;br /&gt;
	  cdraw(i,j-4,playfield[i][j]);&lt;br /&gt;
  }}&lt;br /&gt;
  printf(&amp;quot;%cY%c%c%ld&amp;quot;, 27, 32+3, 32+47, score);&lt;br /&gt;
  printf(&amp;quot;%cY%c%c%d&amp;quot;, 27, 32+5, 32+47, level);&lt;br /&gt;
  printf(&amp;quot;%cY%c%c%d&amp;quot;, 27, 32+7, 32+47, lines);&lt;br /&gt;
}&lt;br /&gt;
void controls(){&lt;br /&gt;
	/*&lt;br /&gt;
   if(bpeek(0x3802)&amp;amp;0b00000010){plot(126,46);}else{unplot(126,46);} //up button (I)&lt;br /&gt;
   if(bpeek(0x3802)&amp;amp;0b00000100){plot(125,47);}else{unplot(125,47);} //left button (J)&lt;br /&gt;
   if(bpeek(0x3802)&amp;amp;0b00001000){plot(126,47);}else{unplot(126,47);} //down button (K)&lt;br /&gt;
   if(bpeek(0x3802)&amp;amp;0b00010000){plot(127,47);}else{unplot(127,47);} //right button (L)&lt;br /&gt;
   if(bpeek(0x3801)&amp;amp;0b00000010){plot(121,47);}else{unplot(121,47);} //a button (A)&lt;br /&gt;
   if(bpeek(0x3804)&amp;amp;0b00001000){plot(122,47);}else{unplot(122,47);} //b button (S)&lt;br /&gt;
   if(bpeek(0x3801)&amp;amp;0b00000001){plot(119,47);}else{unplot(119,47);} //newgame (@)&lt;br /&gt;
	   */&lt;br /&gt;
  if(bpeek(0x3840)&amp;amp;0b00000100){exit(0);}&lt;br /&gt;
  if(bpeek(0x3810)&amp;amp;0b00000010){lvladd=1;}&lt;br /&gt;
  if(bpeek(0x3810)&amp;amp;0b00000001){lvladd=-1;}&lt;br /&gt;
  &lt;br /&gt;
  if(bpeek(0x3801)&amp;amp;0b00000001){ngame=1;}&lt;br /&gt;
  if(bpeek(0x3808)&amp;amp;0b00000010){newpc=1;}&lt;br /&gt;
  if(dcontrol==0){&lt;br /&gt;
    if(bpeek(0x3802)&amp;amp;0b00000100){dcontrol=4;}&lt;br /&gt;
    if(bpeek(0x3802)&amp;amp;0b00010000){dcontrol=6;}&lt;br /&gt;
    if(bpeek(0x3802)&amp;amp;0b00000010){dcontrol=8;}&lt;br /&gt;
    if(bpeek(0x3802)&amp;amp;0b00001000){dcontrol=2;}&lt;br /&gt;
    if(dcontrol==0){drepeatframe=0;}&lt;br /&gt;
    if(dcontrol==lastdcontrol){          //short delay before fast motion&lt;br /&gt;
      if(drepeatframe&amp;lt;=drepeatframes){&lt;br /&gt;
        drepeatframe++;&lt;br /&gt;
        dcontrol=3; //lockout if within lockout period&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(rcontrol==0){&lt;br /&gt;
    if(bpeek(0x3801)&amp;amp;0b00000010){rcontrol=1;}&lt;br /&gt;
    if(bpeek(0x3804)&amp;amp;0b00001000){rcontrol=2;}&lt;br /&gt;
    if(rcontrol==lastrcontrol){rcontrol=3;}&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
void clearControls(){&lt;br /&gt;
  if(rcontrol!=3){lastrcontrol=rcontrol;}&lt;br /&gt;
  if(dcontrol!=3){lastdcontrol=dcontrol;}&lt;br /&gt;
  rcontrol=0;&lt;br /&gt;
  dcontrol=0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void loadpiece(){ //hardcoded all pieces&lt;br /&gt;
  switch(pieceT){&lt;br /&gt;
    case 1: //long one&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=1; pieceC[1][2]=1; pieceC[2][2]=1; pieceC[3][2]=1;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0; &lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=1; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=1; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=0; pieceC[2][2]=1; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=1; pieceC[3][3]=0; &lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 2: //backwards L&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=2; pieceC[1][2]=2; pieceC[2][2]=2; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=2; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=2; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=2; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=2; pieceC[1][3]=2; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=2; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=2; pieceC[1][2]=2; pieceC[2][2]=2; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=2; pieceC[2][1]=2; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=2; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=2; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 3: //L&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=3; pieceC[1][2]=3; pieceC[2][2]=3; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=3; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=3; pieceC[1][1]=3; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=3; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=3; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=3; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=3; pieceC[1][2]=3; pieceC[2][2]=3; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=3; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=3; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=3; pieceC[2][3]=3; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 4: //s shape&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=4; pieceC[2][2]=4; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=4; pieceC[1][3]=4; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=4; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=4; pieceC[2][2]=4; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=4; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 5: //T shape&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=5; pieceC[1][2]=5; pieceC[2][2]=5; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=5; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=5; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=5; pieceC[1][2]=5; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=5; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=5; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=5; pieceC[1][2]=5; pieceC[2][2]=5; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=5; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=5; pieceC[2][2]=5; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=5; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 6: //reverse s&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=6; pieceC[1][2]=6; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=6; pieceC[2][3]=6; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=6; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=6; pieceC[2][2]=6; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=6; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 7: //square&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      default:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=7; pieceC[2][1]=7; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=7; pieceC[2][2]=7; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void nextpiece(){//generate the next piece and move PC back to the top&lt;br /&gt;
  unsigned char tempT=nextpieceT;&lt;br /&gt;
  unsigned char tempR=nextpieceR;&lt;br /&gt;
  nextpieceT = rand()%7 + 1;&lt;br /&gt;
  nextpieceR = rand()%4 + 1;&lt;br /&gt;
  pieceT=nextpieceT;&lt;br /&gt;
  pieceR=nextpieceR;&lt;br /&gt;
  loadpiece();&lt;br /&gt;
  for(char i=0; i&amp;lt;4; i++){&lt;br /&gt;
  for(char j=0; j&amp;lt;4; j++){&lt;br /&gt;
	  if(pieceC[i][j]!=0){&lt;br /&gt;
		  setblock((i)*clspX+npX, (j)*clspY+npY);&lt;br /&gt;
	  }else{&lt;br /&gt;
		  clearblock((i)*clspX+npX, (j)*clspY+npY);&lt;br /&gt;
	  }&lt;br /&gt;
  }}&lt;br /&gt;
  &lt;br /&gt;
  pieceT=tempT;&lt;br /&gt;
  pieceR=tempR;&lt;br /&gt;
  &lt;br /&gt;
  pieceCY=0; //move piece carrier back to the top of screen&lt;br /&gt;
  pieceCX=3;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void mdraw(){&lt;br /&gt;
  //printf(&amp;quot;%cY%c%c%d&amp;quot;, 27, 32+1, 32+55, fff); fff++;&lt;br /&gt;
  loadpiece(); //load piece into the piece carrier&lt;br /&gt;
  for(char i=0; i&amp;lt;6; i++){ //check piece carrier and one block offset outside it&lt;br /&gt;
  for(char j=0; j&amp;lt;6; j++){&lt;br /&gt;
	  controls();&lt;br /&gt;
	  if((pieceCX-1+i)&amp;gt;=0&amp;amp;&amp;amp;(pieceCX-1+i)&amp;lt;pfsizeX&amp;amp;&amp;amp;(pieceCY-1+j)&amp;gt;=4&amp;amp;&amp;amp;(pieceCY-1+j)&amp;lt;(pfsizeY)){//check if this segment be drawn on screen&lt;br /&gt;
		if(i&amp;gt;=1&amp;amp;&amp;amp;i&amp;lt;=4&amp;amp;&amp;amp;j&amp;gt;=1&amp;amp;&amp;amp;j&amp;lt;=4){//accessing piece carrier section here}&lt;br /&gt;
			if(pieceC[i-1][j-1]&amp;gt;0){//if piece carrier block is confirmed should be white&lt;br /&gt;
				cdraw(i-1+pieceCX,j-5+pieceCY,pieceC[i-1][j-1]);&lt;br /&gt;
			}else{&lt;br /&gt;
				cdraw(i-1+pieceCX,j-5+pieceCY,playfield[i-1+pieceCX][j-1+pieceCY]); //color it in with the existing playfield background&lt;br /&gt;
			}&lt;br /&gt;
		}else{&lt;br /&gt;
			cdraw(i-1+pieceCX,j-5+pieceCY,playfield[i-1+pieceCX][j-1+pieceCY]);&lt;br /&gt;
		}&lt;br /&gt;
	  }&lt;br /&gt;
  }}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
unsigned char checkCollide(){ //move the piece carrier first, then check if anything collides&lt;br /&gt;
  loadpiece(); //load piece into the piece carrier&lt;br /&gt;
  unsigned char nonvalidity=0;&lt;br /&gt;
  for(char i=0; i&amp;lt;4; i++){ //run through all piece carrier cells&lt;br /&gt;
    for(char j=0; j&amp;lt;4; j++){&lt;br /&gt;
      if(pieceCX+i&amp;gt;=0&amp;amp;&amp;amp;pieceCX+i&amp;lt;pfsizeX&amp;amp;&amp;amp;pieceCY+j&amp;gt;=0&amp;amp;&amp;amp;pieceCY+j&amp;lt;pfsizeY){ //check if piece carrier segment can be drawn on screen&lt;br /&gt;
        if(pieceC[i][j]!=0&amp;amp;&amp;amp;playfield[i+pieceCX][j+pieceCY]&amp;gt;7){ //if both background and nonzero piece carrier segment collide&lt;br /&gt;
          nonvalidity=1;&lt;br /&gt;
        }&lt;br /&gt;
      }else{ //this segment of PC can't be drawn on the screen&lt;br /&gt;
        if(pieceC[i][j]!=0){ //a filled in segment would be drawn offscreen&lt;br /&gt;
          nonvalidity=1;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  return nonvalidity;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void newgamemon(){&lt;br /&gt;
  if(ngame==0){return;}&lt;br /&gt;
  //Serial.println(F(&amp;quot;NEW GAME&amp;quot;));&lt;br /&gt;
  printf(&amp;quot;%cY%c%c         &amp;quot;, 27, 32+14, 32+40);&lt;br /&gt;
  printf(&amp;quot;%cY%c%c        &amp;quot;, 27, 32+3, 32+47);&lt;br /&gt;
  printf(&amp;quot;%cY%c%c        &amp;quot;, 27, 32+5, 32+47);&lt;br /&gt;
  printf(&amp;quot;%cY%c%c        &amp;quot;, 27, 32+7, 32+47);&lt;br /&gt;
  ngame=0;&lt;br /&gt;
  for(unsigned char i=0; i&amp;lt;pfsizeX; i++){   //clear any cells with active piece parts (will be written again with new pieceC&lt;br /&gt;
    for(unsigned char j=0; j&amp;lt;pfsizeY; j++){&lt;br /&gt;
      playfield[i][j]=0;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  lines=0;&lt;br /&gt;
  level=0;&lt;br /&gt;
  score=0;&lt;br /&gt;
  lslvi=0;&lt;br /&gt;
  fdrop=0;&lt;br /&gt;
  nextpiece();&lt;br /&gt;
  nextpiece();&lt;br /&gt;
  recopyscreen();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void piece2bg(){ //will copy active piece into background and check for lines and game over&lt;br /&gt;
  for(unsigned char i=0; i&amp;lt;4; i++){ //copy piece onto background&lt;br /&gt;
  for(unsigned char j=0; j&amp;lt;4; j++){&lt;br /&gt;
    if(pieceC[i][j]!=0){playfield[i+pieceCX][j+pieceCY]=pieceC[i][j]+8;} //copy the piece into the playfield/background &lt;br /&gt;
  }}&lt;br /&gt;
  nextpiece();&lt;br /&gt;
  &lt;br /&gt;
  //check for line clears&lt;br /&gt;
  unsigned char clearline[pfsizeY];&lt;br /&gt;
  unsigned char clearedlines=0;&lt;br /&gt;
  unsigned char templines=0;&lt;br /&gt;
  for(unsigned char j=4; j&amp;lt;pfsizeY; j++){&lt;br /&gt;
  clearline[j]=1; //assume line is cleared&lt;br /&gt;
  for(unsigned char i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
    if(playfield[i][j]&amp;lt;=7){clearline[j]=0;break;} //line is not full&lt;br /&gt;
  }&lt;br /&gt;
  clearedlines+=clearline[j];&lt;br /&gt;
  templines+=clearline[j];&lt;br /&gt;
  }&lt;br /&gt;
  if(clearedlines&amp;gt;0){//breifly animate the cleared lines, then clear them&lt;br /&gt;
  &lt;br /&gt;
    for(unsigned char f=0; f&amp;lt;=4; f++){&lt;br /&gt;
    for(unsigned char j=4; j&amp;lt;pfsizeY; j++){&lt;br /&gt;
        if(clearline[j]==1){&lt;br /&gt;
          for(unsigned char i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
			  cdraw(i,j-4,f%2);&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      t_delay(2000);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    for(unsigned char j=4; j&amp;lt;pfsizeY; j++){ //accutally clear the lines&lt;br /&gt;
       if(clearline[j]==1){&lt;br /&gt;
        for(unsigned char t=j; t&amp;gt;=4; t--){&lt;br /&gt;
         for(unsigned char i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
           playfield[i][t]=playfield[i][t-1];&lt;br /&gt;
         }&lt;br /&gt;
        }&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
   lines+=templines; //add number of lines to lines counter&lt;br /&gt;
   switch(templines){ //calculate score&lt;br /&gt;
    case 1:&lt;br /&gt;
      score+=40*(level+1);&lt;br /&gt;
      break;&lt;br /&gt;
    case 2:&lt;br /&gt;
      score+=100*(level+1);&lt;br /&gt;
      break;&lt;br /&gt;
    case 3:&lt;br /&gt;
      score+=300*(level+1);&lt;br /&gt;
      break;&lt;br /&gt;
    default:&lt;br /&gt;
      score+=1200*(level+1);&lt;br /&gt;
      break;&lt;br /&gt;
   }&lt;br /&gt;
   for(unsigned char i=1; i&amp;lt;=templines; i++){ //see if the level needs increasing&lt;br /&gt;
    lslvi++;&lt;br /&gt;
    if(lslvi&amp;gt;=10){lslvi=0;level++;}&lt;br /&gt;
    if(level&amp;gt;=maxlevel){level=maxlevel;}&lt;br /&gt;
   }&lt;br /&gt;
  }&lt;br /&gt;
  //check gameover (scan through line 3 and see if there are any non-active pieces in it)&lt;br /&gt;
  for(unsigned char i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
    if(playfield[i][3]&amp;gt;7){&lt;br /&gt;
      //*********************GAME OVER********************&lt;br /&gt;
      //Serial.println(F(&amp;quot;GAME OVER&amp;quot;));&lt;br /&gt;
	  printf(&amp;quot;%cY%c%cGAME OVER&amp;quot;, 27, 32+14, 32+40);&lt;br /&gt;
      while(ngame==0){&lt;br /&gt;
        controls();&lt;br /&gt;
      }&lt;br /&gt;
      newgamemon();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  recopyscreen();&lt;br /&gt;
	  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void main(){&lt;br /&gt;
	printf(&amp;quot;%cE&amp;quot;, 27);&lt;br /&gt;
	printf(&amp;quot;%cY%c%cSoviet Bloc Game 2019&amp;quot;, 27, 32+1, 32+40);&lt;br /&gt;
	printf(&amp;quot;%cY%c%cScore:&amp;quot;, 27, 32+3, 32+40);&lt;br /&gt;
	printf(&amp;quot;%cY%c%cLevel:&amp;quot;, 27, 32+5, 32+40);&lt;br /&gt;
	printf(&amp;quot;%cY%c%cLines:&amp;quot;, 27, 32+7, 32+40);&lt;br /&gt;
	printf(&amp;quot;%cY%c%cNext&amp;quot;, 27, 32+4, 32+30);&lt;br /&gt;
	&lt;br /&gt;
	printf(&amp;quot;%cY%c%cJ K L to move&amp;quot;, 27, 32+10, 32+32);&lt;br /&gt;
	printf(&amp;quot;%cY%c%c A S  to rotate&amp;quot;, 27, 32+11, 32+32);&lt;br /&gt;
	printf(&amp;quot;%cY%c%c  @   to start new game&amp;quot;, 27, 32+12, 32+32);&lt;br /&gt;
	&lt;br /&gt;
	drawb(pfX-2, pfY-1, (pfsizeX+1)*clspX, ((pfsizeY-3)*clspY));&lt;br /&gt;
	&lt;br /&gt;
&lt;br /&gt;
	ngame=1;&lt;br /&gt;
	newgamemon();&lt;br /&gt;
	while(1){ //*********************MAIN*PROGRAM*LOOP**************************&lt;br /&gt;
  &lt;br /&gt;
  //normally a frame delay would go here, but the trs-80 is slow and we need all the speed we can get.&lt;br /&gt;
  &lt;br /&gt;
  unsigned char droppiece=0;&lt;br /&gt;
  dropframe++;&lt;br /&gt;
  if(dropframe&amp;gt;=(maxlevel-level)){&lt;br /&gt;
    dropframe=0;&lt;br /&gt;
    droppiece=1;&lt;br /&gt;
  }&lt;br /&gt;
  newgamemon(); &lt;br /&gt;
  &lt;br /&gt;
  if(lvladd!=0){&lt;br /&gt;
	  level+=lvladd;&lt;br /&gt;
	  lvladd=0;&lt;br /&gt;
	  recopyscreen();&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  if(newpc!=0){&lt;br /&gt;
	  newpc=0;&lt;br /&gt;
	  nextpiece();&lt;br /&gt;
	  recopyscreen();&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  if(rcontrol==1){&lt;br /&gt;
    pieceR++; if(pieceR&amp;gt;=5){pieceR=1;} //try to rotate piece&lt;br /&gt;
    if(checkCollide()){&lt;br /&gt;
      pieceR--; if(pieceR&amp;lt;=0){pieceR=4;} //undo rotation&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(rcontrol==2){&lt;br /&gt;
    pieceR--; if(pieceR&amp;lt;=0){pieceR=4;}&lt;br /&gt;
    if(checkCollide()){&lt;br /&gt;
      pieceR++; if(pieceR&amp;gt;=5){pieceR=1;}&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(dcontrol==4){&lt;br /&gt;
    pieceCX--; //try and see what happens if we move the piece left&lt;br /&gt;
    if(checkCollide()){//piece move is not valid&lt;br /&gt;
      pieceCX++; //take piece back&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(dcontrol==6){&lt;br /&gt;
    pieceCX++; //try and see what happens if we move the piece left&lt;br /&gt;
    if(checkCollide()){//piece move is not valid&lt;br /&gt;
      pieceCX--; //take piece back&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(dcontrol==8){&lt;br /&gt;
    pieceCY--; //try and see what happens if we move the piece up&lt;br /&gt;
    if(checkCollide()){//piece move is not valid&lt;br /&gt;
      pieceCY++; //take piece back&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(dcontrol==2||droppiece==1){&lt;br /&gt;
    pieceCY++; //try and see what happens if we move the piece down&lt;br /&gt;
    if(!(drepeatframe&amp;lt;=drepeatframes)){ //is in fast mode&lt;br /&gt;
      fdrop++;&lt;br /&gt;
    }else{&lt;br /&gt;
      fdrop=0;&lt;br /&gt;
    }&lt;br /&gt;
    if(checkCollide()){//piece move is not valid&lt;br /&gt;
      pieceCY--; //take piece back&lt;br /&gt;
      score+=fdrop; //add # of fast dropped blocks to score&lt;br /&gt;
      fdrop=0;&lt;br /&gt;
      piece2bg(); //copy piece to background and reset to next piece&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  clearControls();&lt;br /&gt;
  mdraw();&lt;br /&gt;
}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
==Nintendo DS version==&lt;br /&gt;
'''This is ridiculous. We took the TRS-80 version and developed it for a platform made 27 years later. And it works.'''&lt;br /&gt;
[[File:Wilson-tetris-NDS.png|thumb|Nintendo DS Version running. Note the split screens, one text, one graphics. The DS gives you almost nothing built in, it expects all game data to be provided in the game (including fonts and images), and there are only a handful included with ndslib. For me, that means one screen with graphics, and one with monochrome text.]]&lt;br /&gt;
Sure, there are already plenty of games available for the Nintendo DS, but if all you have is a Soviet Bloc Game clone, everything starts to look like a nail. The NDS defines handheld gaming for the children of my generation, so when I found one at my local rummage sale and paired it up with a generic China Special flash card so it could run programs off a micro sd card, I knew it had to be done.&lt;br /&gt;
&lt;br /&gt;
This is honestly the first time I have worked/played with an actual games machine since I was first learning to hack my Wii around 2010, and will be the first time I write a homebrew app of any kind. &lt;br /&gt;
&lt;br /&gt;
My port uses libnds (or is that ndslib?), compiled with the devkitPro enviroment. There isn't much development on the NDS nowadays, but even in 2019 I found updated instructions for installing an up to date development enviroment.&lt;br /&gt;
&lt;br /&gt;
I learned that the nds has a ton of fancy game featrues (as you might expect), like sprites and special memory hardware that can make really fast copies of some things. It also has two seperate processors, which do completely diferent things (update the screen and check the inputs, for example) yet still make a complete system work. This is, needless to say, much more complicated than the things I usually work on.&lt;br /&gt;
&lt;br /&gt;
However, I decided to forgo all the low level work and keep things simple. There is one video mode which, instead of making up sprites or loading in complex background images, is just that you write to a certain spot in memory with a certain value, and it turns a certain pixel of the screen a certain color (aka framebuffer mode). This is really easy to work with, although there are no included commands to draw lines or crcles or anything like that, so I added a few functions for this (although all I need to draw is boxes, which does make it somewhat easier.) This mode is also a lot slower than what the Ds is truely caplable of doing apparently, but I found it was more than fast enough to make Soviet Bloc Game work correctly. &lt;br /&gt;
&lt;br /&gt;
The DS is also equipped with color screens, so I got to try coloring the blocks for the first time; this too was very easy, since I had kept track of which block belongs to which piece type, all I had to do was assign a color to each piece when going through and printing the playfield matrix. In doing this, I left out a bit of the code from the TRS-80 version that only plots things on the screen where it needs to be changed (this was absolutely nessacary in the TRS-80 version to get a playable game), but I found that the DS was so fast that it didn't matter at all. So now the entire piece carrier gets redrawn instead of just those tiles which have changed, but I could probably redraw the entire playfield too without any issue at all because speed is very high.&lt;br /&gt;
&lt;br /&gt;
Speaking of speed, this ended up being a problem. In my two older versions, gameplay was quite slow. On the DS, I needed to introduce a lockout so that the next piece would not immediatly drop down while still holding the DOWN key. Then, it was a matter of adding frame delays to make a game that was not too fast (though I did increase the number of levels (speedup as lines are cleared) for faster gameplay as you keep playing).&lt;br /&gt;
&lt;br /&gt;
Other than that, the port went smoothly, and as usual the hardest parts were trying to figure out how to get inputs from the buttons and how to draw blocks on the screen. Once I got that done I had a working version of Soviet Bloc Game about 30 seconds later, and the rest was fine tuning it.&lt;br /&gt;
&lt;br /&gt;
The last step is, hey, the DS has speakers. The example code includes a way to play MOD-like tracker files (XM, S3M, and others). So, why not finish it out with a poorly rendered Midi version of All Star by Smash Mouth? This was literally a matter of adding about 5 lines to my existing code, and disabling a couple things in the setup that it turned out I wasn't using anyways. The player takes everything in the background, while playing Soviet Bloc Game still. It uses some hardware (interrupts, timers and the like) so you can't drop it into just any source code you've got, but for one like Soviet Bloc Game that barely uses any special hardware it works fine.&lt;br /&gt;
&lt;br /&gt;
==IBM-PC/DOS Version==&lt;br /&gt;
I got a bunch of 5-1/4 floppy drives recently and used them to fix the first computer I learned to program - the Sanyo MBC-550. This 1983 computer comes with MS-DOS and BASIC, but is not IBM-PC compatible (no CGA card, for instance, instead having it's own internal color graphics). As such, it has very little games availible for it.&lt;br /&gt;
&lt;br /&gt;
Since I had never written any code for a 8088 based system, and I have five computers that use a pre-286 CPU, I decided to port my Soviet Bloc Game to it. GCC does not work for anything below a 386, but Open WATCOM does, so that's what I used. &lt;br /&gt;
&lt;br /&gt;
This C comiler comes with modes for choosing text modes, but like most IBM PC software, it directly accesses the hardware to make it work. This makes things incompatible with the Sanyo. The Sanyo does support standard BIOS Interrupt calls, which are slower than the direct hardware calls but are fast enough for Soviet Bloc Game. However, I discovered that C functions to do text manipulation are too slow for Soviet Bloc Game, at least on a slow system like the Sanyo. So, the sections to write graphics and also read keys on the keyboard are done in Assembly. For proper timing between system of different clock speeds, I used the system tick - Int 1aH, which increments a counter about 18.2 times per second. &lt;br /&gt;
&lt;br /&gt;
So now, there is a version of my Soviet Bloc Game that works on pretty much any computer that runs MS-DOS.&lt;br /&gt;
&lt;br /&gt;
==Sources==&lt;br /&gt;
Here is a link to the source files and a compiled version: [[File:Wilson-tetris-NDS-t2.zip]]. You can build this just by running &amp;lt;code&amp;gt;make&amp;lt;/code&amp;gt; from within the devkitPro console.&lt;br /&gt;
&lt;br /&gt;
[[File:Wilson-tetris-PC.zip]] - the DOS/PC version&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=Soviet_Bloc_Game_(C_version)&amp;diff=598</id>
		<title>Soviet Bloc Game (C version)</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=Soviet_Bloc_Game_(C_version)&amp;diff=598"/>
		<updated>2023-09-20T20:18:21Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:Wilson-tetris-trs80.png|thumb|Soviet Bloc Game v1.1 running in a trs-80 emulator]]&lt;br /&gt;
While thinking about ways to make my TRS-80 model I more useable in 2019, I realized that I could not easily find a version of Soviet Bloc Game for it (probably because the TRS-80 came out about 10ish years before Soviet Bloc Game was invented), so I thought I might try to write my own after watching an episode of ''the 8-bit Guy'' where David does the same. I used to play the Gameboy version of Soviet Bloc Game all the time on my Ti-nSpire calculator thanks to the nspire hacking scene and an emulator port, so that's the style of Soviet Bloc Game I will try to recreate.&lt;br /&gt;
&lt;br /&gt;
However, I have written few games, and never done a Soviet Bloc Game clone before, so I approach this mostly as a challenge.&lt;br /&gt;
&lt;br /&gt;
Logal notice: &amp;quot;Tetris&amp;quot; is a trademark of The Tetris Company (who have been known to take down unauthorized trademark versions before: https://github.com/jdah/tetris-os), so I have decided to rename this clone &amp;quot;Soviet Bloc Game&amp;quot;&lt;br /&gt;
&lt;br /&gt;
'''Objectives:'''&lt;br /&gt;
* Playfield the same size as GB version&lt;br /&gt;
* in color?&lt;br /&gt;
* same or similar scoring system to GB version&lt;br /&gt;
* same rotation style as GB version&lt;br /&gt;
* easily portable to different platforms (that are C like)&lt;br /&gt;
* somewhat wellishly documented&lt;br /&gt;
&lt;br /&gt;
This ended up being a sort of 24 hour challenge; I started this project at about 11pm yesterday, and by about the time of this page's creation I had created an Arduino version of Soviet Bloc Game that met most of these objectives.&lt;br /&gt;
&lt;br /&gt;
==Theory of Operation==&lt;br /&gt;
===Basics===&lt;br /&gt;
The basics of the game's functioning can be summed up with just a few components: a part that generates the  shapes, a playfield that contains already played pieces and open spots where the active piece can move and collision detection that says where the active piece can and can't go.&lt;br /&gt;
&lt;br /&gt;
The playfield size is adjustable, but in the gameboy version it's 10 wide by 18 tall. In my version I added an extra 4 lines on top (which are not to be displayed) for the new pieces to appear within. Within the program, it appears as a big 2D matrix called &amp;lt;code&amp;gt;playfield[][]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
====Drawing the pieces====&lt;br /&gt;
Rather than figure out all the complex rotations of the part, I hardcoded them (using the Nintendo rotation system as described at https://tetris.fandom.com/wiki/Nintendo_Rotation_System). The parts appear in a 4x4 piece container &amp;lt;code&amp;gt;pieceC[][]&amp;lt;/code&amp;gt; matrix. This is then overlaid onto the playfield, and can be moved around. Nonzero cells of the matrix define the shape of the piece, like:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 0|| 0|| 0|| 0&lt;br /&gt;
|-&lt;br /&gt;
| 0|| 5|| 0|| 0&lt;br /&gt;
|-&lt;br /&gt;
| 5|| 5|| 0|| 0&lt;br /&gt;
|-&lt;br /&gt;
| 0|| 5|| 0|| 0&lt;br /&gt;
|}&lt;br /&gt;
The numbers used to define the piece are unique to each piece, so if you write the display section to use each number as a color, you can have a color game.&lt;br /&gt;
&lt;br /&gt;
====Collision detection====&lt;br /&gt;
In the collision detection section, the overlaid piece carrier is moved as if you were to make that move, and then we check if any of the cells overlap with non-blank sections of the playfield, or if any part of the active piece lies outside the playfield area. If so, the move is undone, and you can decide what to do from there. If no collision occurs, the move is done for real and displayed the next time the screen is updated.&lt;br /&gt;
 &lt;br /&gt;
Usually, the collision detection is used to prevent motion if a piece or wall is in the way, but if the piece is attempting to move downwards, the piece is instead copied onto the playfield, and the numbers making up the piece are increased by 8. This is how the collision detection can differentiate between the active piece and dead pieces in the playfield.&lt;br /&gt;
&lt;br /&gt;
====Input conditioning====&lt;br /&gt;
I added some input conditioning as well. For rotations of the active piece, the move is not repeating, and you must release the rotation input and press it again for a rotation to happen again. For moves left, right, and down, a system similar to repeating keys on a computer keyboard is used, holding down the direction for a brief moment will make the action repeat. In the gameboy version, dropping a piece quickly will increase the score based on the number of tiles dropped while moving fast. This is also replicated (but probably not perfectly) in my version.&lt;br /&gt;
&lt;br /&gt;
===Details===&lt;br /&gt;
====Levels and speed====&lt;br /&gt;
I only update the screen every few moments. Call the time of one of these updates a '''frame'''. While the screen is waiting to be updated, it's a good time to check the controls and see what we need to do the next time the screen needs updating. All the processing of the moves and collisions happens just before the screen updates. Every few frames, the active piece drops one row. This is connected to the levels, which in my version go from 0-20, which I think is in line with the gb version. The drop happens every 20 frames at level 0, up to a delay of 0 frames at level 20. For every 10 lines that the user clears, the level is increased by 1, so that the game gets progressively faster, just like the gb version. &lt;br /&gt;
====Game over detection====&lt;br /&gt;
This one is pretty simple. If any non-active piece sections are found on the line above the top of the visible playfield, the game ends. Check this every frame.&lt;br /&gt;
====Line completion detection====&lt;br /&gt;
This is a little more complicated than the game over, but basically I check to see if any rows are completely filled in with dead pieces. I keep track of which lines are full (and how many of them there are), and animate the corresponding lines by having the whole row(s) flash for a moment. At this point I also add onto the number of completed lines counter displayed on the scoreboard. Then, I copy all the lines above the filled line down by one tile. Since this is done for each filled line in sequence, the effect moves the playfield down for all cases of any number of filled lines.  &lt;br /&gt;
====Scoring====&lt;br /&gt;
The number of filled lines cleared in one move (calculated above) is used in the scoring formula to add to the score. The full formula (according to the Nintendo scoring system) is:&lt;br /&gt;
* 1 line: 40*(level+1)&lt;br /&gt;
* 2 lines: 100*(level+1)&lt;br /&gt;
* 3 lines: 300*(level+1)&lt;br /&gt;
* 4 lines: 1200*(level+1) &lt;br /&gt;
&lt;br /&gt;
Additionally, the number of grid tiles that are dropped by the user pressing &amp;quot;down&amp;quot; (ie, soft drop), which I kept track of earlier, are also added to the score.&lt;br /&gt;
====New game====&lt;br /&gt;
I have a new game button also. When pressed (and the controls are checked), it picks new pieces, clears the level, score, and lines counters, and empties the playfield of dead pieces. Thus, a new game begins.&lt;br /&gt;
&lt;br /&gt;
==Hardware (Arduino version)==&lt;br /&gt;
I do most of my software development on arduino-like embedded systems, and am pretty familiar with I/O options on these devices (more so than on a stndard computer), so I used an arduino nano with a 128x64 st7920 monochrome LCD and the u8g2 libray for the display. For input, I wired up 3 buttons, 2 rotate and one new game, and an analog joystick (which I am using as a digital one) for directional moves.&lt;br /&gt;
&lt;br /&gt;
===Porting===&lt;br /&gt;
With any luck, my code can be ported to different platforms just my changing the section that displays the playfield on a screen, the input sections that take in the button presses, and maybe the delay sections. On the Arduino, my code takes up about 50% of the prgram storage space, and 50% of ram, with most of that being due to the u8g2 library. I'm not an expert on the super low-level processes on the arduino, nor do I think my game is the most effceiently coded, but my guess is that if the game works well on a system with as low specifications as the atmega328, it should be able to run on nearly anything else.&lt;br /&gt;
&lt;br /&gt;
==The Code (Arduino version)==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#define pfsizeX 10&lt;br /&gt;
#define pfsizeY 22 //note that top 4 lines are not drawn&lt;br /&gt;
uint8_t playfield[pfsizeX][pfsizeY];  //the game area&lt;br /&gt;
&lt;br /&gt;
uint8_t pieceC[4][4]; //piece container&lt;br /&gt;
int8_t pieceCX;  //location of upper left corner of piece container&lt;br /&gt;
int8_t pieceCY;&lt;br /&gt;
uint8_t pieceT; //type of piece&lt;br /&gt;
uint8_t pieceR; //rotation of piece &lt;br /&gt;
uint8_t nextpieceT; &lt;br /&gt;
uint8_t nextpieceR; &lt;br /&gt;
&lt;br /&gt;
uint8_t dcontrol; //locks in control every frame&lt;br /&gt;
uint8_t rcontrol;&lt;br /&gt;
&lt;br /&gt;
uint8_t lastdcontrol=0; //for &amp;quot;debouncing&amp;quot; of inputs&lt;br /&gt;
uint8_t lastrcontrol=0; &lt;br /&gt;
uint8_t drepeatframe=0;&lt;br /&gt;
#define drepeatframes 3 //wait _ frames before repeatedly going in one direction&lt;br /&gt;
&lt;br /&gt;
uint8_t dropframe=0;    //counter for number of frames between block drops&lt;br /&gt;
uint8_t level=0; //LEVEL decreases frame drop from 20 frames to 0 frames (levels 0 to 20)&lt;br /&gt;
&lt;br /&gt;
boolean ngame=0; //when set, starts new game&lt;br /&gt;
&lt;br /&gt;
uint16_t lines=0; //NUMBER OF LINES CLEARED&lt;br /&gt;
uint32_t score=0; //TOTAL SCORE (using NES rules)&lt;br /&gt;
&lt;br /&gt;
uint8_t fdrop;   //number of blocks that piece has been fast dropped&lt;br /&gt;
uint8_t lslvi=0; //lines since level increase (when this gets to 10, increase the level)&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;U8g2lib.h&amp;gt;&lt;br /&gt;
U8G2_ST7920_128X64_1_HW_SPI u8g2(U8G2_R0, /* CS=*/ 12, /* reset=*/ 8);&lt;br /&gt;
&lt;br /&gt;
void dispscreen(){&lt;br /&gt;
  u8g2.firstPage();  &lt;br /&gt;
  do {&lt;br /&gt;
    for(uint8_t j=4; j&amp;lt;pfsizeY; j++){ //draw screen&lt;br /&gt;
    for(uint8_t i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
      if(playfield[i][j]!=0){&lt;br /&gt;
        u8g2.drawBox(5*(j-4),5*(pfsizeX-1)-5*i,6,6);&lt;br /&gt;
      }else{&lt;br /&gt;
        u8g2.drawFrame(5*(j-4),5*(pfsizeX-1)-5*i,6,6);&lt;br /&gt;
        //if(j==3){u8g2.drawLine(5*j,5*(pfsizeX-1)-5*i,5*j+5,5*(pfsizeX-1)-5*i+5);}&lt;br /&gt;
      }&lt;br /&gt;
    }}&lt;br /&gt;
&lt;br /&gt;
  uint8_t temppieceT=pieceT; &lt;br /&gt;
  uint8_t temppieceR=pieceR;&lt;br /&gt;
  pieceT=nextpieceT;&lt;br /&gt;
  pieceR=nextpieceR;&lt;br /&gt;
  loadpiece(); &lt;br /&gt;
  for(uint8_t j=0; j&amp;lt;4; j++){ //draw next piece&lt;br /&gt;
  for(uint8_t i=0; i&amp;lt;4; i++){&lt;br /&gt;
    if(pieceC[i][j]!=0){&lt;br /&gt;
      u8g2.drawBox(5*(j+pfsizeY-3),5*(pfsizeX-1)-5*i,6,6);&lt;br /&gt;
    }else{&lt;br /&gt;
      u8g2.drawFrame(5*(j+pfsizeY-3),5*(pfsizeX-1)-5*i,6,6);&lt;br /&gt;
    }&lt;br /&gt;
  }}&lt;br /&gt;
  pieceT=temppieceT;&lt;br /&gt;
  pieceR=temppieceR;&lt;br /&gt;
  loadpiece();&lt;br /&gt;
&lt;br /&gt;
  u8g2.setFont(u8g2_font_6x10_tf);&lt;br /&gt;
  char zbuffer[32];&lt;br /&gt;
  sprintf(zbuffer, &amp;quot;N%d&amp;quot;, lines);&lt;br /&gt;
  u8g2.drawStr(0,60, zbuffer);&lt;br /&gt;
  sprintf(zbuffer, &amp;quot;L%d&amp;quot;, level);&lt;br /&gt;
  u8g2.drawStr(25,60, zbuffer);&lt;br /&gt;
  sprintf(zbuffer, &amp;quot;S%d&amp;quot;, score);&lt;br /&gt;
  u8g2.drawStr(50,60, zbuffer);  &lt;br /&gt;
  } while( u8g2.nextPage() );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void controls(){&lt;br /&gt;
  if(digitalRead(A3)==0){ngame=1;}&lt;br /&gt;
  if(dcontrol==0){&lt;br /&gt;
    if(analogRead(A7)&amp;lt;10){dcontrol=4;}&lt;br /&gt;
    if(analogRead(A7)&amp;gt;1014){dcontrol=6;}&lt;br /&gt;
    if(analogRead(A6)&amp;gt;1014){dcontrol=8;}&lt;br /&gt;
    if(analogRead(A6)&amp;lt;10){dcontrol=2;}&lt;br /&gt;
    if(dcontrol==0){drepeatframe=0;}&lt;br /&gt;
    if(dcontrol==lastdcontrol){          //short delay before fast motion&lt;br /&gt;
      if(drepeatframe&amp;lt;=drepeatframes){&lt;br /&gt;
        drepeatframe++;&lt;br /&gt;
        dcontrol=3; //lockout if within lockout period&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(rcontrol==0){&lt;br /&gt;
    if(digitalRead(A4)==0){rcontrol=1;}&lt;br /&gt;
    if(digitalRead(A5)==0){rcontrol=2;}&lt;br /&gt;
    if(rcontrol==lastrcontrol){rcontrol=3;}&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void clearControls(){&lt;br /&gt;
  if(rcontrol!=3){lastrcontrol=rcontrol;}&lt;br /&gt;
  if(dcontrol!=3){lastdcontrol=dcontrol;}&lt;br /&gt;
  rcontrol=0;&lt;br /&gt;
  dcontrol=0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void draw(){&lt;br /&gt;
  loadpiece(); //load piece into the piece carrier&lt;br /&gt;
  for(uint8_t i=0; i&amp;lt;pfsizeX; i++){   //clear any cells with active piece parts (will be written again with new pieceC&lt;br /&gt;
    for(uint8_t j=0; j&amp;lt;pfsizeY; j++){&lt;br /&gt;
      if(playfield[i][j]&amp;lt;=7){playfield[i][j]=0;}&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  for(uint8_t i=0; i&amp;lt;4; i++){ //copy active piece onto the playfield&lt;br /&gt;
    for(uint8_t j=0; j&amp;lt;4; j++){&lt;br /&gt;
      if(pieceCX+i&amp;gt;=0&amp;amp;&amp;amp;pieceCX+i&amp;lt;pfsizeX&amp;amp;&amp;amp;pieceCY+j&amp;gt;=0&amp;amp;&amp;amp;pieceCY+j&amp;lt;pfsizeY){//check if piece segment can be drawn on screen&lt;br /&gt;
        if(pieceC[i][j]!=0){playfield[i+pieceCX][j+pieceCY]=pieceC[i][j];}&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
boolean checkCollide(){ //move the piece carrier first, then check if anything collides&lt;br /&gt;
  loadpiece(); //load piece into the piece carrier&lt;br /&gt;
  boolean nonvalidity=0;&lt;br /&gt;
  for(uint8_t i=0; i&amp;lt;4; i++){ //run through all piece carrier cells&lt;br /&gt;
    for(uint8_t j=0; j&amp;lt;4; j++){&lt;br /&gt;
      if(pieceCX+i&amp;gt;=0&amp;amp;&amp;amp;pieceCX+i&amp;lt;pfsizeX&amp;amp;&amp;amp;pieceCY+j&amp;gt;=0&amp;amp;&amp;amp;pieceCY+j&amp;lt;pfsizeY){ //check if piece carrier segment can be drawn on screen&lt;br /&gt;
        if(pieceC[i][j]!=0&amp;amp;&amp;amp;playfield[i+pieceCX][j+pieceCY]&amp;gt;7){ //if both background and nonzero piece carrier segment collide&lt;br /&gt;
          nonvalidity=1;&lt;br /&gt;
        }&lt;br /&gt;
      }else{ //this segment of PC can't be drawn on the screen&lt;br /&gt;
        if(pieceC[i][j]!=0){ //a filled in segment would be drawn offscreen&lt;br /&gt;
          nonvalidity=1;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  return nonvalidity;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void piece2bg(){&lt;br /&gt;
  for(uint8_t i=0; i&amp;lt;4; i++){ //copy active piece onto the screen&lt;br /&gt;
  for(uint8_t j=0; j&amp;lt;4; j++){&lt;br /&gt;
    if(pieceC[i][j]!=0){playfield[i+pieceCX][j+pieceCY]=pieceC[i][j]+8;} //copy the piece into the playfield/background &lt;br /&gt;
  }}&lt;br /&gt;
  nextpiece();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void nextpiece(){//generate the next piece and move PC back to the top&lt;br /&gt;
  pieceT=nextpieceT;&lt;br /&gt;
  pieceR=nextpieceR;&lt;br /&gt;
  nextpieceT = rand()%7 + 1;&lt;br /&gt;
  nextpieceR = rand()%4 + 1;&lt;br /&gt;
  pieceCY=0; //move piece carrier back to the top of screen&lt;br /&gt;
  pieceCX=3;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void newgamemon(){&lt;br /&gt;
  if(ngame==0){return;}&lt;br /&gt;
  Serial.println(F(&amp;quot;NEW GAME&amp;quot;));&lt;br /&gt;
  ngame=0;&lt;br /&gt;
  for(uint8_t i=0; i&amp;lt;pfsizeX; i++){   //clear any cells with active piece parts (will be written again with new pieceC&lt;br /&gt;
    for(uint8_t j=0; j&amp;lt;pfsizeY; j++){&lt;br /&gt;
      playfield[i][j]=0;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  lines=0;&lt;br /&gt;
  level=0;&lt;br /&gt;
  score=0;&lt;br /&gt;
  lslvi=0;&lt;br /&gt;
  fdrop=0;&lt;br /&gt;
  nextpiece();&lt;br /&gt;
  nextpiece();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loadpiece(){ //hardcoded all pieces&lt;br /&gt;
  switch(pieceT){&lt;br /&gt;
    case 1: //long one&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=1; pieceC[1][2]=1; pieceC[2][2]=1; pieceC[3][2]=1;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0; &lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=1; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=1; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=0; pieceC[2][2]=1; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=1; pieceC[3][3]=0; &lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 2: //backwards L&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=2; pieceC[1][2]=2; pieceC[2][2]=2; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=2; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=2; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=2; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=2; pieceC[1][3]=2; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=2; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=2; pieceC[1][2]=2; pieceC[2][2]=2; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=2; pieceC[2][1]=2; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=2; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=2; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 3: //L&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=3; pieceC[1][2]=3; pieceC[2][2]=3; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=3; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=3; pieceC[1][1]=3; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=3; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=3; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=3; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=3; pieceC[1][2]=3; pieceC[2][2]=3; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=3; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=3; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=3; pieceC[2][3]=3; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 4: //s shape&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=4; pieceC[2][2]=4; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=4; pieceC[1][3]=4; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=4; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=4; pieceC[2][2]=4; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=4; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 5: //T shape&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=5; pieceC[1][2]=5; pieceC[2][2]=5; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=5; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=5; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=5; pieceC[1][2]=5; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=5; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=5; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=5; pieceC[1][2]=5; pieceC[2][2]=5; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=5; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=5; pieceC[2][2]=5; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=5; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 6: //reverse s&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=6; pieceC[1][2]=6; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=6; pieceC[2][3]=6; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=6; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=6; pieceC[2][2]=6; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=6; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 7: //square&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      default:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=7; pieceC[2][1]=7; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=7; pieceC[2][2]=7; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setup(){&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  u8g2.begin();&lt;br /&gt;
  pinMode(A4, INPUT_PULLUP);&lt;br /&gt;
  pinMode(A5, INPUT_PULLUP);&lt;br /&gt;
  pinMode(A3, INPUT_PULLUP); //new game button&lt;br /&gt;
&lt;br /&gt;
  ngame=1;&lt;br /&gt;
  newgamemon();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
  &lt;br /&gt;
  for(uint32_t h=0;h&amp;lt;=5;h++){//frame delay  //DELAY SECTION (BETWEEN FRAMES)&lt;br /&gt;
    delay(1);&lt;br /&gt;
    controls();&lt;br /&gt;
    newgamemon();&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  boolean droppiece=0;&lt;br /&gt;
  dropframe++;&lt;br /&gt;
  if(dropframe&amp;gt;=(20-level)){&lt;br /&gt;
    dropframe=0;&lt;br /&gt;
    droppiece=1;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  if(rcontrol==1){&lt;br /&gt;
    pieceR++; if(pieceR&amp;gt;=5){pieceR=1;} //try to rotate piece&lt;br /&gt;
    if(checkCollide()){&lt;br /&gt;
      pieceR--; if(pieceR&amp;lt;=0){pieceR=4;} //undo rotation&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(rcontrol==2){&lt;br /&gt;
    pieceR--; if(pieceR&amp;lt;=0){pieceR=4;}&lt;br /&gt;
    if(checkCollide()){&lt;br /&gt;
      pieceR++; if(pieceR&amp;gt;=5){pieceR=1;}&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(dcontrol==4){&lt;br /&gt;
    pieceCX--; //try and see what happens if we move the piece left&lt;br /&gt;
    if(checkCollide()){//piece move is not valid&lt;br /&gt;
      pieceCX++; //take piece back&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(dcontrol==6){&lt;br /&gt;
    pieceCX++; //try and see what happens if we move the piece left&lt;br /&gt;
    if(checkCollide()){//piece move is not valid&lt;br /&gt;
      pieceCX--; //take piece back&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(dcontrol==8){&lt;br /&gt;
    pieceCY--; //try and see what happens if we move the piece up&lt;br /&gt;
    if(checkCollide()){//piece move is not valid&lt;br /&gt;
      pieceCY++; //take piece back&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(dcontrol==2||droppiece==1){&lt;br /&gt;
    pieceCY++; //try and see what happens if we move the piece down&lt;br /&gt;
    if(!(drepeatframe&amp;lt;=drepeatframes)){ //is in fast mode&lt;br /&gt;
      fdrop++;&lt;br /&gt;
    }else{&lt;br /&gt;
      fdrop=0;&lt;br /&gt;
    }&lt;br /&gt;
    if(checkCollide()){//piece move is not valid&lt;br /&gt;
      pieceCY--; //take piece back&lt;br /&gt;
      score+=fdrop; //add # of fast dropped blocks to score&lt;br /&gt;
      fdrop=0;&lt;br /&gt;
      piece2bg(); //copy piece to background and reset to next piece&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  draw();&lt;br /&gt;
  dispscreen();&lt;br /&gt;
  &lt;br /&gt;
  clearControls();&lt;br /&gt;
&lt;br /&gt;
  //check for line clears&lt;br /&gt;
  boolean clearline[pfsizeY];&lt;br /&gt;
  uint8_t clearedlines=0;&lt;br /&gt;
  uint8_t templines=0;&lt;br /&gt;
  for(uint8_t j=4; j&amp;lt;pfsizeY; j++){&lt;br /&gt;
  clearline[j]=1; //assume line is cleared&lt;br /&gt;
  for(uint8_t i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
    if(playfield[i][j]&amp;lt;=7){clearline[j]=0;break;} //line is not full&lt;br /&gt;
  }&lt;br /&gt;
  clearedlines+=clearline[j];&lt;br /&gt;
  templines+=clearline[j];&lt;br /&gt;
  }&lt;br /&gt;
  if(clearedlines&amp;gt;0){//breifly animate the cleared lines, then clear them&lt;br /&gt;
    for(uint8_t f=0; f&amp;lt;=6; f++){&lt;br /&gt;
      for(uint8_t j=4; j&amp;lt;pfsizeY; j++){&lt;br /&gt;
        if(clearline[j]==1){&lt;br /&gt;
          for(uint8_t i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
            if(f%2==0){playfield[i][j]=8;}else{playfield[i][j]=0;}&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      draw();&lt;br /&gt;
      dispscreen();&lt;br /&gt;
      delay(200);&lt;br /&gt;
    }&lt;br /&gt;
    for(uint8_t j=4; j&amp;lt;pfsizeY; j++){ //accutally clear the lines&lt;br /&gt;
       if(clearline[j]==1){&lt;br /&gt;
        for(uint8_t t=j; t&amp;gt;=4; t--){&lt;br /&gt;
         for(uint8_t i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
           playfield[i][t]=playfield[i][t-1];&lt;br /&gt;
         }&lt;br /&gt;
        }&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
   lines+=templines; //add number of lines to lines counter&lt;br /&gt;
   switch(templines){ //calculate score&lt;br /&gt;
    case 1:&lt;br /&gt;
      score+=40*(level+1);&lt;br /&gt;
      break;&lt;br /&gt;
    case 2:&lt;br /&gt;
      score+=100*(level+1);&lt;br /&gt;
      break;&lt;br /&gt;
    case 3:&lt;br /&gt;
      score+=300*(level+1);&lt;br /&gt;
      break;&lt;br /&gt;
    default:&lt;br /&gt;
      score+=1200*(level+1);&lt;br /&gt;
      break;&lt;br /&gt;
   }&lt;br /&gt;
   for(uint8_t i=1; i&amp;lt;=templines; i++){ //see if the level needs increasing&lt;br /&gt;
    lslvi++;&lt;br /&gt;
    if(lslvi&amp;gt;=10){lslvi=0;level++;}&lt;br /&gt;
    if(level&amp;gt;=20){level=20;}&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
  //check gameover (scan through line 3 and see if there are any non-active pieces in it)&lt;br /&gt;
  for(uint8_t i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
    if(playfield[i][3]&amp;gt;7){&lt;br /&gt;
      //*********************GAME OVER********************&lt;br /&gt;
      Serial.println(F(&amp;quot;GAME OVER&amp;quot;));&lt;br /&gt;
      while(ngame==0){&lt;br /&gt;
        controls();&lt;br /&gt;
      }&lt;br /&gt;
      newgamemon();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  //check for completed lines&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
==The Code (TRS-80 version)==&lt;br /&gt;
I used the z88dk C compiler for the port. To compile, use &amp;lt;code&amp;gt;zcc  +trs80 -lndos -lm -create-app soviet-bloc-game.c -o soviet-bloc-game&amp;lt;/code&amp;gt; which will create a &amp;lt;code&amp;gt;.CAS&amp;lt;/code&amp;gt; file. Use &amp;lt;code&amp;gt;zcc  +trs80 -lndos -lm -create-app -subtype=disk soviet-bloc-game.c -o soviet-bloc-game&amp;lt;/code&amp;gt; to create a &amp;lt;code&amp;gt;.CMD&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Porting this to the TRS-80 turned out to be a much harder challange, mostly because the TRS-80 is a very slow, low power machine. Some of the improvements were obvious, like only checking the lines and game over only when an active piece becomes inactive. However, this isn't enough to make a playable version. The TRS-80 is slow enough that every comparison, operation, and graphics plot add noticable delay. Therefore, I only update parts of the display when needed (ie, the area around the active piece, 1 block around the piece carrier); the background is filled back in to erase piece moves, and the screen is only redrawn at the end when the active piece becomes inactive. This is possible because unlike the arduino version, the whole screen doesn't need to be rewitten every time it changes. I also had to take out any frame delay and reduce the levels to 10 because the machine is not fast enough to keep up. The version below is therefore clunky, but playable. The obvious next steps are to make an assembler version of Soviet Bloc Game.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//soviet-bloc-game.c - Soviet Bloc Game for TRS-80 - 05/28/2019&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt; &lt;br /&gt;
#include &amp;lt;graphics.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define pfsizeX 10 //playfield size (in cells)&lt;br /&gt;
#define pfsizeY 22 //note that top 4 lines are not drawn&lt;br /&gt;
#define pfX 8 //upper left corner of playfield location (pixel)&lt;br /&gt;
#define pfY 4&lt;br /&gt;
#define npX 60 //upper left corner of next piece indicator&lt;br /&gt;
#define npY 2&lt;br /&gt;
#define clspX 4 //spacing between cell elements (pixels)&lt;br /&gt;
#define clspY 2&lt;br /&gt;
&lt;br /&gt;
void controls(void);&lt;br /&gt;
&lt;br /&gt;
unsigned char playfield[pfsizeX][pfsizeY];  //the game area&lt;br /&gt;
unsigned char cscreen[pfsizeX][pfsizeY-4];  //a copy of what gets drawn on the screen (game area)&lt;br /&gt;
unsigned char pieceC[4][4]; //piece container&lt;br /&gt;
signed char pieceCX;  //location of upper left corner of piece container&lt;br /&gt;
signed char pieceCY;&lt;br /&gt;
unsigned char pieceT; //type of piece&lt;br /&gt;
unsigned char pieceR; //rotation of piece &lt;br /&gt;
unsigned char nextpieceT; &lt;br /&gt;
unsigned char nextpieceR; &lt;br /&gt;
&lt;br /&gt;
unsigned char dcontrol; //locks in control every frame&lt;br /&gt;
unsigned char rcontrol;&lt;br /&gt;
&lt;br /&gt;
unsigned char lastdcontrol=0; //for &amp;quot;debouncing&amp;quot; of inputs&lt;br /&gt;
unsigned char lastrcontrol=0; &lt;br /&gt;
unsigned char drepeatframe=0;&lt;br /&gt;
#define drepeatframes 3 //wait _ frames before repeatedly going in one direction&lt;br /&gt;
&lt;br /&gt;
unsigned char dropframe=0;    //counter for number of frames between block drops&lt;br /&gt;
unsigned char level=0; //LEVEL decreases frame drop from maxlevel frames to 0 frames (levels 0 to MAXLEVEL)&lt;br /&gt;
#define maxlevel 10&lt;br /&gt;
&lt;br /&gt;
unsigned char ngame=1; //when set, starts new game&lt;br /&gt;
unsigned char newpc=0; //when set, get a new piece&lt;br /&gt;
&lt;br /&gt;
unsigned int lines=0; //NUMBER OF LINES CLEARED&lt;br /&gt;
long score=0; //TOTAL SCORE (using NES rules)&lt;br /&gt;
&lt;br /&gt;
unsigned char fdrop;   //number of blocks that piece has been fast dropped&lt;br /&gt;
unsigned char lslvi=0; //lines since level increase (when this gets to 10, increase the level)&lt;br /&gt;
&lt;br /&gt;
char lvladd=0;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void setblock(char x, char y){&lt;br /&gt;
	for(char i=0; i&amp;lt;8; i++){&lt;br /&gt;
		controls();&lt;br /&gt;
     	plot(x+i%4,y+i/4);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
void clearblock(char x, char y){&lt;br /&gt;
	for(char i=0; i&amp;lt;8; i++){&lt;br /&gt;
		controls();&lt;br /&gt;
    	unplot(x+i%4,y+i/4);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
void cdraw(char x, char y, char color){//only draws on screen if it needs to (uses cell coordinates)&lt;br /&gt;
	if(cscreen[x][y]!=color){&lt;br /&gt;
		cscreen[x][y]=color;&lt;br /&gt;
		if(color!=0){&lt;br /&gt;
			setblock( (x)*clspX+pfX, (y)*clspY+pfY );&lt;br /&gt;
		}else{&lt;br /&gt;
			clearblock( (x)*clspX+pfX, (y)*clspY+pfY);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
void recopyscreen(){ //copies playfield to screen &lt;br /&gt;
  for(char i=0; i&amp;lt;pfsizeX; i++){ //manually redraw the entire screen&lt;br /&gt;
  for(char j=4; j&amp;lt;pfsizeY; j++){&lt;br /&gt;
	  cdraw(i,j-4,playfield[i][j]);&lt;br /&gt;
  }}&lt;br /&gt;
  printf(&amp;quot;%cY%c%c%ld&amp;quot;, 27, 32+3, 32+47, score);&lt;br /&gt;
  printf(&amp;quot;%cY%c%c%d&amp;quot;, 27, 32+5, 32+47, level);&lt;br /&gt;
  printf(&amp;quot;%cY%c%c%d&amp;quot;, 27, 32+7, 32+47, lines);&lt;br /&gt;
}&lt;br /&gt;
void controls(){&lt;br /&gt;
	/*&lt;br /&gt;
   if(bpeek(0x3802)&amp;amp;0b00000010){plot(126,46);}else{unplot(126,46);} //up button (I)&lt;br /&gt;
   if(bpeek(0x3802)&amp;amp;0b00000100){plot(125,47);}else{unplot(125,47);} //left button (J)&lt;br /&gt;
   if(bpeek(0x3802)&amp;amp;0b00001000){plot(126,47);}else{unplot(126,47);} //down button (K)&lt;br /&gt;
   if(bpeek(0x3802)&amp;amp;0b00010000){plot(127,47);}else{unplot(127,47);} //right button (L)&lt;br /&gt;
   if(bpeek(0x3801)&amp;amp;0b00000010){plot(121,47);}else{unplot(121,47);} //a button (A)&lt;br /&gt;
   if(bpeek(0x3804)&amp;amp;0b00001000){plot(122,47);}else{unplot(122,47);} //b button (S)&lt;br /&gt;
   if(bpeek(0x3801)&amp;amp;0b00000001){plot(119,47);}else{unplot(119,47);} //newgame (@)&lt;br /&gt;
	   */&lt;br /&gt;
  if(bpeek(0x3840)&amp;amp;0b00000100){exit(0);}&lt;br /&gt;
  if(bpeek(0x3810)&amp;amp;0b00000010){lvladd=1;}&lt;br /&gt;
  if(bpeek(0x3810)&amp;amp;0b00000001){lvladd=-1;}&lt;br /&gt;
  &lt;br /&gt;
  if(bpeek(0x3801)&amp;amp;0b00000001){ngame=1;}&lt;br /&gt;
  if(bpeek(0x3808)&amp;amp;0b00000010){newpc=1;}&lt;br /&gt;
  if(dcontrol==0){&lt;br /&gt;
    if(bpeek(0x3802)&amp;amp;0b00000100){dcontrol=4;}&lt;br /&gt;
    if(bpeek(0x3802)&amp;amp;0b00010000){dcontrol=6;}&lt;br /&gt;
    if(bpeek(0x3802)&amp;amp;0b00000010){dcontrol=8;}&lt;br /&gt;
    if(bpeek(0x3802)&amp;amp;0b00001000){dcontrol=2;}&lt;br /&gt;
    if(dcontrol==0){drepeatframe=0;}&lt;br /&gt;
    if(dcontrol==lastdcontrol){          //short delay before fast motion&lt;br /&gt;
      if(drepeatframe&amp;lt;=drepeatframes){&lt;br /&gt;
        drepeatframe++;&lt;br /&gt;
        dcontrol=3; //lockout if within lockout period&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(rcontrol==0){&lt;br /&gt;
    if(bpeek(0x3801)&amp;amp;0b00000010){rcontrol=1;}&lt;br /&gt;
    if(bpeek(0x3804)&amp;amp;0b00001000){rcontrol=2;}&lt;br /&gt;
    if(rcontrol==lastrcontrol){rcontrol=3;}&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
void clearControls(){&lt;br /&gt;
  if(rcontrol!=3){lastrcontrol=rcontrol;}&lt;br /&gt;
  if(dcontrol!=3){lastdcontrol=dcontrol;}&lt;br /&gt;
  rcontrol=0;&lt;br /&gt;
  dcontrol=0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void loadpiece(){ //hardcoded all pieces&lt;br /&gt;
  switch(pieceT){&lt;br /&gt;
    case 1: //long one&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=1; pieceC[1][2]=1; pieceC[2][2]=1; pieceC[3][2]=1;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0; &lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=1; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=1; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=0; pieceC[2][2]=1; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=1; pieceC[3][3]=0; &lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 2: //backwards L&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=2; pieceC[1][2]=2; pieceC[2][2]=2; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=2; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=2; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=2; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=2; pieceC[1][3]=2; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=2; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=2; pieceC[1][2]=2; pieceC[2][2]=2; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=2; pieceC[2][1]=2; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=2; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=2; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 3: //L&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=3; pieceC[1][2]=3; pieceC[2][2]=3; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=3; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=3; pieceC[1][1]=3; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=3; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=3; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=3; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=3; pieceC[1][2]=3; pieceC[2][2]=3; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=3; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=3; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=3; pieceC[2][3]=3; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 4: //s shape&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=4; pieceC[2][2]=4; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=4; pieceC[1][3]=4; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=4; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=4; pieceC[2][2]=4; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=4; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 5: //T shape&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=5; pieceC[1][2]=5; pieceC[2][2]=5; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=5; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=5; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=5; pieceC[1][2]=5; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=5; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=5; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=5; pieceC[1][2]=5; pieceC[2][2]=5; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=5; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=5; pieceC[2][2]=5; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=5; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 6: //reverse s&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      case 1:&lt;br /&gt;
      case 3:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=0; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=6; pieceC[1][2]=6; pieceC[2][2]=0; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=6; pieceC[2][3]=6; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
      case 2:&lt;br /&gt;
      case 4:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=0; pieceC[2][1]=6; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=6; pieceC[2][2]=6; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=6; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
   case 7: //square&lt;br /&gt;
     switch(pieceR){&lt;br /&gt;
      default:&lt;br /&gt;
        pieceC[0][0]=0; pieceC[1][0]=0; pieceC[2][0]=0; pieceC[3][0]=0;&lt;br /&gt;
        pieceC[0][1]=0; pieceC[1][1]=7; pieceC[2][1]=7; pieceC[3][1]=0;&lt;br /&gt;
        pieceC[0][2]=0; pieceC[1][2]=7; pieceC[2][2]=7; pieceC[3][2]=0;&lt;br /&gt;
        pieceC[0][3]=0; pieceC[1][3]=0; pieceC[2][3]=0; pieceC[3][3]=0;&lt;br /&gt;
        break;&lt;br /&gt;
     }&lt;br /&gt;
     break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void nextpiece(){//generate the next piece and move PC back to the top&lt;br /&gt;
  unsigned char tempT=nextpieceT;&lt;br /&gt;
  unsigned char tempR=nextpieceR;&lt;br /&gt;
  nextpieceT = rand()%7 + 1;&lt;br /&gt;
  nextpieceR = rand()%4 + 1;&lt;br /&gt;
  pieceT=nextpieceT;&lt;br /&gt;
  pieceR=nextpieceR;&lt;br /&gt;
  loadpiece();&lt;br /&gt;
  for(char i=0; i&amp;lt;4; i++){&lt;br /&gt;
  for(char j=0; j&amp;lt;4; j++){&lt;br /&gt;
	  if(pieceC[i][j]!=0){&lt;br /&gt;
		  setblock((i)*clspX+npX, (j)*clspY+npY);&lt;br /&gt;
	  }else{&lt;br /&gt;
		  clearblock((i)*clspX+npX, (j)*clspY+npY);&lt;br /&gt;
	  }&lt;br /&gt;
  }}&lt;br /&gt;
  &lt;br /&gt;
  pieceT=tempT;&lt;br /&gt;
  pieceR=tempR;&lt;br /&gt;
  &lt;br /&gt;
  pieceCY=0; //move piece carrier back to the top of screen&lt;br /&gt;
  pieceCX=3;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void mdraw(){&lt;br /&gt;
  //printf(&amp;quot;%cY%c%c%d&amp;quot;, 27, 32+1, 32+55, fff); fff++;&lt;br /&gt;
  loadpiece(); //load piece into the piece carrier&lt;br /&gt;
  for(char i=0; i&amp;lt;6; i++){ //check piece carrier and one block offset outside it&lt;br /&gt;
  for(char j=0; j&amp;lt;6; j++){&lt;br /&gt;
	  controls();&lt;br /&gt;
	  if((pieceCX-1+i)&amp;gt;=0&amp;amp;&amp;amp;(pieceCX-1+i)&amp;lt;pfsizeX&amp;amp;&amp;amp;(pieceCY-1+j)&amp;gt;=4&amp;amp;&amp;amp;(pieceCY-1+j)&amp;lt;(pfsizeY)){//check if this segment be drawn on screen&lt;br /&gt;
		if(i&amp;gt;=1&amp;amp;&amp;amp;i&amp;lt;=4&amp;amp;&amp;amp;j&amp;gt;=1&amp;amp;&amp;amp;j&amp;lt;=4){//accessing piece carrier section here}&lt;br /&gt;
			if(pieceC[i-1][j-1]&amp;gt;0){//if piece carrier block is confirmed should be white&lt;br /&gt;
				cdraw(i-1+pieceCX,j-5+pieceCY,pieceC[i-1][j-1]);&lt;br /&gt;
			}else{&lt;br /&gt;
				cdraw(i-1+pieceCX,j-5+pieceCY,playfield[i-1+pieceCX][j-1+pieceCY]); //color it in with the existing playfield background&lt;br /&gt;
			}&lt;br /&gt;
		}else{&lt;br /&gt;
			cdraw(i-1+pieceCX,j-5+pieceCY,playfield[i-1+pieceCX][j-1+pieceCY]);&lt;br /&gt;
		}&lt;br /&gt;
	  }&lt;br /&gt;
  }}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
unsigned char checkCollide(){ //move the piece carrier first, then check if anything collides&lt;br /&gt;
  loadpiece(); //load piece into the piece carrier&lt;br /&gt;
  unsigned char nonvalidity=0;&lt;br /&gt;
  for(char i=0; i&amp;lt;4; i++){ //run through all piece carrier cells&lt;br /&gt;
    for(char j=0; j&amp;lt;4; j++){&lt;br /&gt;
      if(pieceCX+i&amp;gt;=0&amp;amp;&amp;amp;pieceCX+i&amp;lt;pfsizeX&amp;amp;&amp;amp;pieceCY+j&amp;gt;=0&amp;amp;&amp;amp;pieceCY+j&amp;lt;pfsizeY){ //check if piece carrier segment can be drawn on screen&lt;br /&gt;
        if(pieceC[i][j]!=0&amp;amp;&amp;amp;playfield[i+pieceCX][j+pieceCY]&amp;gt;7){ //if both background and nonzero piece carrier segment collide&lt;br /&gt;
          nonvalidity=1;&lt;br /&gt;
        }&lt;br /&gt;
      }else{ //this segment of PC can't be drawn on the screen&lt;br /&gt;
        if(pieceC[i][j]!=0){ //a filled in segment would be drawn offscreen&lt;br /&gt;
          nonvalidity=1;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  return nonvalidity;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void newgamemon(){&lt;br /&gt;
  if(ngame==0){return;}&lt;br /&gt;
  //Serial.println(F(&amp;quot;NEW GAME&amp;quot;));&lt;br /&gt;
  printf(&amp;quot;%cY%c%c         &amp;quot;, 27, 32+14, 32+40);&lt;br /&gt;
  printf(&amp;quot;%cY%c%c        &amp;quot;, 27, 32+3, 32+47);&lt;br /&gt;
  printf(&amp;quot;%cY%c%c        &amp;quot;, 27, 32+5, 32+47);&lt;br /&gt;
  printf(&amp;quot;%cY%c%c        &amp;quot;, 27, 32+7, 32+47);&lt;br /&gt;
  ngame=0;&lt;br /&gt;
  for(unsigned char i=0; i&amp;lt;pfsizeX; i++){   //clear any cells with active piece parts (will be written again with new pieceC&lt;br /&gt;
    for(unsigned char j=0; j&amp;lt;pfsizeY; j++){&lt;br /&gt;
      playfield[i][j]=0;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  lines=0;&lt;br /&gt;
  level=0;&lt;br /&gt;
  score=0;&lt;br /&gt;
  lslvi=0;&lt;br /&gt;
  fdrop=0;&lt;br /&gt;
  nextpiece();&lt;br /&gt;
  nextpiece();&lt;br /&gt;
  recopyscreen();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void piece2bg(){ //will copy active piece into background and check for lines and game over&lt;br /&gt;
  for(unsigned char i=0; i&amp;lt;4; i++){ //copy piece onto background&lt;br /&gt;
  for(unsigned char j=0; j&amp;lt;4; j++){&lt;br /&gt;
    if(pieceC[i][j]!=0){playfield[i+pieceCX][j+pieceCY]=pieceC[i][j]+8;} //copy the piece into the playfield/background &lt;br /&gt;
  }}&lt;br /&gt;
  nextpiece();&lt;br /&gt;
  &lt;br /&gt;
  //check for line clears&lt;br /&gt;
  unsigned char clearline[pfsizeY];&lt;br /&gt;
  unsigned char clearedlines=0;&lt;br /&gt;
  unsigned char templines=0;&lt;br /&gt;
  for(unsigned char j=4; j&amp;lt;pfsizeY; j++){&lt;br /&gt;
  clearline[j]=1; //assume line is cleared&lt;br /&gt;
  for(unsigned char i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
    if(playfield[i][j]&amp;lt;=7){clearline[j]=0;break;} //line is not full&lt;br /&gt;
  }&lt;br /&gt;
  clearedlines+=clearline[j];&lt;br /&gt;
  templines+=clearline[j];&lt;br /&gt;
  }&lt;br /&gt;
  if(clearedlines&amp;gt;0){//breifly animate the cleared lines, then clear them&lt;br /&gt;
  &lt;br /&gt;
    for(unsigned char f=0; f&amp;lt;=4; f++){&lt;br /&gt;
    for(unsigned char j=4; j&amp;lt;pfsizeY; j++){&lt;br /&gt;
        if(clearline[j]==1){&lt;br /&gt;
          for(unsigned char i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
			  cdraw(i,j-4,f%2);&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      t_delay(2000);&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    for(unsigned char j=4; j&amp;lt;pfsizeY; j++){ //accutally clear the lines&lt;br /&gt;
       if(clearline[j]==1){&lt;br /&gt;
        for(unsigned char t=j; t&amp;gt;=4; t--){&lt;br /&gt;
         for(unsigned char i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
           playfield[i][t]=playfield[i][t-1];&lt;br /&gt;
         }&lt;br /&gt;
        }&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
   lines+=templines; //add number of lines to lines counter&lt;br /&gt;
   switch(templines){ //calculate score&lt;br /&gt;
    case 1:&lt;br /&gt;
      score+=40*(level+1);&lt;br /&gt;
      break;&lt;br /&gt;
    case 2:&lt;br /&gt;
      score+=100*(level+1);&lt;br /&gt;
      break;&lt;br /&gt;
    case 3:&lt;br /&gt;
      score+=300*(level+1);&lt;br /&gt;
      break;&lt;br /&gt;
    default:&lt;br /&gt;
      score+=1200*(level+1);&lt;br /&gt;
      break;&lt;br /&gt;
   }&lt;br /&gt;
   for(unsigned char i=1; i&amp;lt;=templines; i++){ //see if the level needs increasing&lt;br /&gt;
    lslvi++;&lt;br /&gt;
    if(lslvi&amp;gt;=10){lslvi=0;level++;}&lt;br /&gt;
    if(level&amp;gt;=maxlevel){level=maxlevel;}&lt;br /&gt;
   }&lt;br /&gt;
  }&lt;br /&gt;
  //check gameover (scan through line 3 and see if there are any non-active pieces in it)&lt;br /&gt;
  for(unsigned char i=0; i&amp;lt;pfsizeX; i++){&lt;br /&gt;
    if(playfield[i][3]&amp;gt;7){&lt;br /&gt;
      //*********************GAME OVER********************&lt;br /&gt;
      //Serial.println(F(&amp;quot;GAME OVER&amp;quot;));&lt;br /&gt;
	  printf(&amp;quot;%cY%c%cGAME OVER&amp;quot;, 27, 32+14, 32+40);&lt;br /&gt;
      while(ngame==0){&lt;br /&gt;
        controls();&lt;br /&gt;
      }&lt;br /&gt;
      newgamemon();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  recopyscreen();&lt;br /&gt;
	  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void main(){&lt;br /&gt;
	printf(&amp;quot;%cE&amp;quot;, 27);&lt;br /&gt;
	printf(&amp;quot;%cY%c%cSoviet Bloc Game 2019&amp;quot;, 27, 32+1, 32+40);&lt;br /&gt;
	printf(&amp;quot;%cY%c%cScore:&amp;quot;, 27, 32+3, 32+40);&lt;br /&gt;
	printf(&amp;quot;%cY%c%cLevel:&amp;quot;, 27, 32+5, 32+40);&lt;br /&gt;
	printf(&amp;quot;%cY%c%cLines:&amp;quot;, 27, 32+7, 32+40);&lt;br /&gt;
	printf(&amp;quot;%cY%c%cNext&amp;quot;, 27, 32+4, 32+30);&lt;br /&gt;
	&lt;br /&gt;
	printf(&amp;quot;%cY%c%cJ K L to move&amp;quot;, 27, 32+10, 32+32);&lt;br /&gt;
	printf(&amp;quot;%cY%c%c A S  to rotate&amp;quot;, 27, 32+11, 32+32);&lt;br /&gt;
	printf(&amp;quot;%cY%c%c  @   to start new game&amp;quot;, 27, 32+12, 32+32);&lt;br /&gt;
	&lt;br /&gt;
	drawb(pfX-2, pfY-1, (pfsizeX+1)*clspX, ((pfsizeY-3)*clspY));&lt;br /&gt;
	&lt;br /&gt;
&lt;br /&gt;
	ngame=1;&lt;br /&gt;
	newgamemon();&lt;br /&gt;
	while(1){ //*********************MAIN*PROGRAM*LOOP**************************&lt;br /&gt;
  &lt;br /&gt;
  //normally a frame delay would go here, but the trs-80 is slow and we need all the speed we can get.&lt;br /&gt;
  &lt;br /&gt;
  unsigned char droppiece=0;&lt;br /&gt;
  dropframe++;&lt;br /&gt;
  if(dropframe&amp;gt;=(maxlevel-level)){&lt;br /&gt;
    dropframe=0;&lt;br /&gt;
    droppiece=1;&lt;br /&gt;
  }&lt;br /&gt;
  newgamemon(); &lt;br /&gt;
  &lt;br /&gt;
  if(lvladd!=0){&lt;br /&gt;
	  level+=lvladd;&lt;br /&gt;
	  lvladd=0;&lt;br /&gt;
	  recopyscreen();&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  if(newpc!=0){&lt;br /&gt;
	  newpc=0;&lt;br /&gt;
	  nextpiece();&lt;br /&gt;
	  recopyscreen();&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  if(rcontrol==1){&lt;br /&gt;
    pieceR++; if(pieceR&amp;gt;=5){pieceR=1;} //try to rotate piece&lt;br /&gt;
    if(checkCollide()){&lt;br /&gt;
      pieceR--; if(pieceR&amp;lt;=0){pieceR=4;} //undo rotation&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(rcontrol==2){&lt;br /&gt;
    pieceR--; if(pieceR&amp;lt;=0){pieceR=4;}&lt;br /&gt;
    if(checkCollide()){&lt;br /&gt;
      pieceR++; if(pieceR&amp;gt;=5){pieceR=1;}&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(dcontrol==4){&lt;br /&gt;
    pieceCX--; //try and see what happens if we move the piece left&lt;br /&gt;
    if(checkCollide()){//piece move is not valid&lt;br /&gt;
      pieceCX++; //take piece back&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(dcontrol==6){&lt;br /&gt;
    pieceCX++; //try and see what happens if we move the piece left&lt;br /&gt;
    if(checkCollide()){//piece move is not valid&lt;br /&gt;
      pieceCX--; //take piece back&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(dcontrol==8){&lt;br /&gt;
    pieceCY--; //try and see what happens if we move the piece up&lt;br /&gt;
    if(checkCollide()){//piece move is not valid&lt;br /&gt;
      pieceCY++; //take piece back&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(dcontrol==2||droppiece==1){&lt;br /&gt;
    pieceCY++; //try and see what happens if we move the piece down&lt;br /&gt;
    if(!(drepeatframe&amp;lt;=drepeatframes)){ //is in fast mode&lt;br /&gt;
      fdrop++;&lt;br /&gt;
    }else{&lt;br /&gt;
      fdrop=0;&lt;br /&gt;
    }&lt;br /&gt;
    if(checkCollide()){//piece move is not valid&lt;br /&gt;
      pieceCY--; //take piece back&lt;br /&gt;
      score+=fdrop; //add # of fast dropped blocks to score&lt;br /&gt;
      fdrop=0;&lt;br /&gt;
      piece2bg(); //copy piece to background and reset to next piece&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  clearControls();&lt;br /&gt;
  mdraw();&lt;br /&gt;
}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
==Nintendo DS version==&lt;br /&gt;
'''This is ridiculous. We took the TRS-80 version and developed it for a platform made 27 years later. And it works.'''&lt;br /&gt;
[[File:Wilson-tetris-NDS.png|thumb|Nintendo DS Version running. Note the split screens, one text, one graphics. The DS gives you almost nothing built in, it expects all game data to be provided in the game (including fonts and images), and there are only a handful included with ndslib. For me, that means one screen with graphics, and one with monochrome text.]]&lt;br /&gt;
Sure, there are already plenty of games available for the Nintendo DS, but if all you have is a Soviet Bloc Game clone, everything starts to look like a nail. The NDS defines handheld gaming for the children of my generation, so when I found one at my local rummage sale and paired it up with a generic China Special flash card so it could run programs off a micro sd card, I knew it had to be done.&lt;br /&gt;
&lt;br /&gt;
This is honestly the first time I have worked/played with an actual games machine since I was first learning to hack my Wii around 2010, and will be the first time I write a homebrew app of any kind. &lt;br /&gt;
&lt;br /&gt;
My port uses libnds (or is that ndslib?), compiled with the devkitPro enviroment. There isn't much development on the NDS nowadays, but even in 2019 I found updated instructions for installing an up to date development enviroment.&lt;br /&gt;
&lt;br /&gt;
I learned that the nds has a ton of fancy game featrues (as you might expect), like sprites and special memory hardware that can make really fast copies of some things. It also has two seperate processors, which do completely diferent things (update the screen and check the inputs, for example) yet still make a complete system work. This is, needless to say, much more complicated than the things I usually work on.&lt;br /&gt;
&lt;br /&gt;
However, I decided to forgo all the low level work and keep things simple. There is one video mode which, instead of making up sprites or loading in complex background images, is just that you write to a certain spot in memory with a certain value, and it turns a certain pixel of the screen a certain color (aka framebuffer mode). This is really easy to work with, although there are no included commands to draw lines or crcles or anything like that, so I added a few functions for this (although all I need to draw is boxes, which does make it somewhat easier.) This mode is also a lot slower than what the Ds is truely caplable of doing apparently, but I found it was more than fast enough to make Soviet Bloc Game work correctly. &lt;br /&gt;
&lt;br /&gt;
The DS is also equipped with color screens, so I got to try coloring the blocks for the first time; this too was very easy, since I had kept track of which block belongs to which piece type, all I had to do was assign a color to each piece when going through and printing the playfield matrix. In doing this, I left out a bit of the code from the TRS-80 version that only plots things on the screen where it needs to be changed (this was absolutely nessacary in the TRS-80 version to get a playable game), but I found that the DS was so fast that it didn't matter at all. So now the entire piece carrier gets redrawn instead of just those tiles which have changed, but I could probably redraw the entire playfield too without any issue at all because speed is very high.&lt;br /&gt;
&lt;br /&gt;
Speaking of speed, this ended up being a problem. In my two older versions, gameplay was quite slow. On the DS, I needed to introduce a lockout so that the next piece would not immediatly drop down while still holding the DOWN key. Then, it was a matter of adding frame delays to make a game that was not too fast (though I did increase the number of levels (speedup as lines are cleared) for faster gameplay as you keep playing).&lt;br /&gt;
&lt;br /&gt;
Other than that, the port went smoothly, and as usual the hardest parts were trying to figure out how to get inputs from the buttons and how to draw blocks on the screen. Once I got that done I had a working version of Soviet Bloc Game about 30 seconds later, and the rest was fine tuning it.&lt;br /&gt;
&lt;br /&gt;
The last step is, hey, the DS has speakers. The example code includes a way to play MOD-like tracker files (XM, S3M, and others). So, why not finish it out with a poorly rendered Midi version of All Star by Smash Mouth? This was literally a matter of adding about 5 lines to my existing code, and disabling a couple things in the setup that it turned out I wasn't using anyways. The player takes everything in the background, while playing Soviet Bloc Game still. It uses some hardware (interrupts, timers and the like) so you can't drop it into just any source code you've got, but for one like Soviet Bloc Game that barely uses any special hardware it works fine.&lt;br /&gt;
&lt;br /&gt;
==IBM-PC/DOS Version==&lt;br /&gt;
I got a bunch of 5-1/4 floppy drives recently and used them to fix the first computer I learned to program - the Sanyo MBC-550. This 1983 computer comes with MS-DOS and BASIC, but is not IBM-PC compatible (no CGA card, for instance, instead having it's own internal color graphics). As such, it has very little games availible for it.&lt;br /&gt;
&lt;br /&gt;
Since I had never written any code for a 8088 based system, and I have five computers that use a pre-286 CPU, I decided to port my Soviet Bloc Game to it. GCC does not work for anything below a 386, but Open WATCOM does, so that's what I used. &lt;br /&gt;
&lt;br /&gt;
This C comiler comes with modes for choosing text modes, but like most IBM PC software, it directly accesses the hardware to make it work. This makes things incompatible with the Sanyo. The Sanyo does support standard BIOS Interrupt calls, which are slower than the direct hardware calls but are fast enough for Soviet Bloc Game. However, I discovered that C functions to do text manipulation are too slow for Soviet Bloc Game, at least on a slow system like the Sanyo. So, the sections to write graphics and also read keys on the keyboard are done in Assembly. For proper timing between system of different clock speeds, I used the system tick - Int 1aH, which increments a counter about 18.2 times per second. &lt;br /&gt;
&lt;br /&gt;
So now, there is a version of my Soviet Bloc Game that works on pretty much any computer that runs MS-DOS.&lt;br /&gt;
&lt;br /&gt;
==Sources==&lt;br /&gt;
Here is a link to the source files and a compiled version: [[File:Wilson-tetris-NDS-t2.zip]]. You can build this just by running &amp;lt;code&amp;gt;make&amp;lt;/code&amp;gt; from within the devkitPro console.&lt;br /&gt;
&lt;br /&gt;
[[File:Wilson-tetris-PC.zip]] - the DOS/PC version&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=Main_Page&amp;diff=597</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=Main_Page&amp;diff=597"/>
		<updated>2022-03-17T01:09:31Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''This site''' serves documentation for my various projects, mostly for my own reference, but also as a way to share them. Using a wiki for this purpose is certainly overkill, but it is easy to edit and format, and I am familiar with it. Whenever I do a fairly involved project, there's a decent chance I will write up the things I learned on this site. As it stands, this site is still relatively sparse, because I like working on projects more than writing about them, like most people.&lt;br /&gt;
&lt;br /&gt;
This web server and the content it provides is also a project in and of itself. This is running on server hardware and software that I set up myself, so as such even the content you are reading now is experimental. I guarantee no stability or reliable access, although I will do my best to keep this available online for as long as possible (though as of 2020 I have had a server like this running continuously for over 5 years).&lt;br /&gt;
&lt;br /&gt;
==Project List==&lt;br /&gt;
More major projects are listed below, in no particular order. See the list of [[Special:AllPages|all pages here.]]&lt;br /&gt;
&lt;br /&gt;
* [[AWOL Box]] - My take on the infamous Mikal Hart Reverse Geocache™ puzzle&lt;br /&gt;
* [[Nixie Clock 1]] - First completed Nixie clock/display unit&lt;br /&gt;
* [[Nixie Clock 2]] - Second Nixie display&lt;br /&gt;
* [[RC Wheelchair 1]] - Hacking the joystick of an electric wheelchair for remote control&lt;br /&gt;
* [[RC Wheelchair 2]] - A complete high-power h-bridge controller, controls, and radio system.&lt;br /&gt;
* [[RC Wheelchair 3]] - Latest version, using an rs485 bus between different modules &lt;br /&gt;
* [[Wilson X carriage]] - Improved x-carriage for the Prusa i3 style 3d printer.&lt;br /&gt;
* [[COW arm]] - a robot arm for the wheelchair robot base&lt;br /&gt;
* [[Lora module]] - module that makes using LoRa radio communications easier to work with.&lt;br /&gt;
* [[Environmental logger]] - A time capsule experiment&lt;br /&gt;
* [[Model T speedometer]] - General purpose GPS based speedometer for cars that don't have one. &lt;br /&gt;
* [[Geiger counter]] - my homemade Geiger counter using a SBM20 tube&lt;br /&gt;
* [[Raspberry Pi DVR]] - my main way of recording TV off the air&lt;br /&gt;
* [[Wilson Tetris]] - my version of a Tetris clone (in C)&lt;br /&gt;
* [[Door monitor]] - putting an unused security system to &amp;quot;good&amp;quot; use.&lt;br /&gt;
* [[uScreen]] - general purpose module with esp8266 and Nokia LCD&lt;br /&gt;
* [[6888 Transmitter]] - 6888 tube-based AM transmitter&lt;br /&gt;
* [[Sky Pointer]] - a thingy for pointing out objects in the sky.&lt;br /&gt;
* [[Atlas Gears]] - Replacement lathe/mill changegears&lt;br /&gt;
* [[Smart Response XE]] - Hacking of a school response system&lt;br /&gt;
* [[Floppy disk drive hacking]] - Poking around floppy drives with a logic analyzer&lt;br /&gt;
* [[Coronameter]] - Dedicated screen for pandemic panicking.&lt;br /&gt;
* [[midiCNC]] - python program for playing MIDI files on a CNC machine / stepper motors with gcode&lt;br /&gt;
* [[Miscellaneous]] - short things that aren't long enough for their own page&lt;br /&gt;
* [[Unpublished Projects]]&lt;br /&gt;
&lt;br /&gt;
==TRS-80 Stuff==&lt;br /&gt;
* [[TRS-80 Model 1 - Character ROM]]&lt;br /&gt;
* [[TRS-80 Model 1 - Wilson Expansion Interface]] - an Expansion Interface (needed to connect floppy drives and other peripherals to the 1977 TRS-80 microcomputer) &lt;br /&gt;
* [[TRS-80 Plug 'n Power Controller]] - reverse engineering an extinct trs-80 accessory&lt;br /&gt;
* [[WC-80]] - A work in progress to build TRS-80 compatible Z80 computer&lt;br /&gt;
&lt;br /&gt;
==Other Services==&lt;br /&gt;
This server has hosted a number of different things since it's original incarnation in high school. Some of the old services are still up (but others are unlisted or taken down due to spam or security risks)&lt;br /&gt;
&lt;br /&gt;
* [http://alnwlsn.com/lists/ Public Lists] - experimental spam collection&lt;br /&gt;
* [http://alnwlsn.com/wintercamp/notifier/ Winter Camp Notifier Script] - service that emails you when new Winter Camp discussion takes place&lt;br /&gt;
* [http://alnwlsn.com/wicm/index.php/Main_Page WICM] - A copy of the Winter Camp encyclopedia translated into MediaWiki&lt;br /&gt;
&lt;br /&gt;
===What is the fish?===&lt;br /&gt;
The green fish is from a game I made when I first learned how to program. Over time, it turned into the logo that I use for my projects.&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=Main_Page&amp;diff=596</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=Main_Page&amp;diff=596"/>
		<updated>2021-03-01T04:31:54Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''This site''' serves documentation for my various projects, mostly for my own reference, but also as a way to share them. Using a wiki for this purpose is certainly overkill, but it is easy to edit and format, and I am familiar with it. Whenever I do a fairly involved project, there's a decent chance I will write up the things I learned on this site. As it stands, this site is still relatively sparse, because I like working on projects more than writing about them, like most people.&lt;br /&gt;
&lt;br /&gt;
This web server and the content it provides is also a project in and of itself. This is running on server hardware and software that I set up myself, so as such even the content you are reading now is experimental. I guarantee no stability or reliable access, although I will do my best to keep this available online for as long as possible (though as of 2020 I have had a server like this running continuously for over 5 years).&lt;br /&gt;
&lt;br /&gt;
==Project List==&lt;br /&gt;
More major projects are listed below, in no particular order. See the list of [[Special:AllPages|all pages here.]]&lt;br /&gt;
&lt;br /&gt;
* [[AWOL Box]] - My take on the infamous Mikal Hart Reverse Geocache™ puzzle&lt;br /&gt;
* [[Nixie Clock 1]] - First completed Nixie clock/display unit&lt;br /&gt;
* [[Nixie Clock 2]] - Second Nixie display&lt;br /&gt;
* [[RC Wheelchair 1]] - Hacking the joystick of an electric wheelchair for remote control&lt;br /&gt;
* [[RC Wheelchair 2]] - A complete high-power h-bridge controller, controls, and radio system.&lt;br /&gt;
* [[RC Wheelchair 3]] - Latest version, using an rs485 bus between different modules &lt;br /&gt;
* [[Wilson X carriage]] - Improved x-carriage for the Prusa i3 style 3d printer.&lt;br /&gt;
* [[COW arm]] - a robot arm for the wheelchair robot base&lt;br /&gt;
* [[Lora module]] - module that makes using LoRa radio communications easier to work with.&lt;br /&gt;
* [[Environmental logger]] - A time capsule experiment&lt;br /&gt;
* [[Model T speedometer]] - General purpose GPS based speedometer for cars that don't have one. &lt;br /&gt;
* [[Geiger counter]] - my homemade Geiger counter using a SBM20 tube&lt;br /&gt;
* [[Raspberry Pi DVR]] - my main way of recording TV off the air&lt;br /&gt;
* [[Wilson Tetris]] - my version of a Tetris clone (in C)&lt;br /&gt;
* [[Door monitor]] - putting an unused security system to &amp;quot;good&amp;quot; use.&lt;br /&gt;
* [[uScreen]] - general purpose module with esp8266 and Nokia LCD&lt;br /&gt;
* [[6888 Transmitter]] - 6888 tube-based AM transmitter&lt;br /&gt;
* [[Sky Pointer]] - a thingy for pointing out objects in the sky.&lt;br /&gt;
* [[Atlas Gears]] - Replacement lathe/mill changegears&lt;br /&gt;
* [[Smart Response XE]] - Hacking of a school response system&lt;br /&gt;
* [[Floppy disk drive hacking]] - Poking around floppy drives with a logic analyzer&lt;br /&gt;
* [[Coronameter]] - Dedicated screen for pandemic panicking.&lt;br /&gt;
* [[midiCNC]] - python program for playing MIDI files on a CNC machine / stepper motors with gcode&lt;br /&gt;
* [[Miscellaneous]] - short things that aren't long enough for their own page&lt;br /&gt;
* [[Unpublished Projects]]&lt;br /&gt;
&lt;br /&gt;
==TRS-80 Stuff==&lt;br /&gt;
* [[TRS-80 Model 1 - Character ROM]]&lt;br /&gt;
* [[TRS-80 Model 1 - Wilson Expansion Interface]] - an Expansion Interface (needed to connect floppy drives and other peripherals to the 1977 TRS-80 microcomputer) &lt;br /&gt;
* [[TRS-80 Plug 'n Power Controller]] - reverse engineering an extinct trs-80 accessory&lt;br /&gt;
* [[WC-80]] - A work in progress to build TRS-80 compatible Z80 computer&lt;br /&gt;
&lt;br /&gt;
==Other Services==&lt;br /&gt;
This server has hosted a number of different things since it's original incarnation in high school. Some of the old services are still up (but others are unlisted or taken down due to spam or security risks)&lt;br /&gt;
&lt;br /&gt;
* [http://alnwlsn.ax.lt/lists/ Public Lists] - experimental spam collection&lt;br /&gt;
* [http://alnwlsn.ax.lt/wintercamp/notifier/ Winter Camp Notifier Script] - service that emails you when new Winter Camp discussion takes place&lt;br /&gt;
* [http://alnwlsn.ax.lt/wicm/index.php/Main_Page WICM] - A copy of the Winter Camp encyclopedia translated into MediaWiki&lt;br /&gt;
&lt;br /&gt;
===What is the fish?===&lt;br /&gt;
The green fish is from a game I made when I first learned how to program. Over time, it turned into the logo that I use for my projects.&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=Esp-rov&amp;diff=595</id>
		<title>Esp-rov</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=Esp-rov&amp;diff=595"/>
		<updated>2020-12-14T03:33:38Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The tiny robots are a custom design made from China components. As usual, most of these can only be found with a few keywords from the usual places:&lt;br /&gt;
&lt;br /&gt;
- ESP32-CAM board (ESP32 wifi microcontroller with a tiny camera and SD card slot)&lt;br /&gt;
- 2x N20 gearmotor, 6V. These come in a variety of gear ratios, don't remember which one I used, probably 60RPM&lt;br /&gt;
- 2x &amp;quot;34mm N20 wheel&amp;quot; - Could have printed these but didn't feel like it&lt;br /&gt;
- MX1508 H-bridge - usually listed as something like Dual Channel L298N DC Motor Driver Board (though L298N claim is wrong)&lt;br /&gt;
- 1x 624 ball bearing&lt;br /&gt;
- Micro 9g servo&lt;br /&gt;
&lt;br /&gt;
The rest of the parts are some generic nuts and bolts, and a bunch of 3d printed parts&lt;br /&gt;
&lt;br /&gt;
[[File:Esp-rov-v1.zip|thumb|software, firmware, 3D files]]&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=File:Esp-rov-v1.zip&amp;diff=594</id>
		<title>File:Esp-rov-v1.zip</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=File:Esp-rov-v1.zip&amp;diff=594"/>
		<updated>2020-12-10T14:33:21Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: Software and 3D STL files for the esp32 camera rover thingys&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Software and 3D STL files for the esp32 camera rover thingys&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=Esp-rov&amp;diff=593</id>
		<title>Esp-rov</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=Esp-rov&amp;diff=593"/>
		<updated>2020-12-10T14:32:38Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: Created page with &amp;quot;placeholder&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;placeholder&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=RC_Wheelchair_3&amp;diff=592</id>
		<title>RC Wheelchair 3</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=RC_Wheelchair_3&amp;diff=592"/>
		<updated>2020-09-03T17:25:25Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Since the previous version, I added [[COW arm|an arm]] and a new LORA radio module. With the only microcontroller being ATmega1284p in the original motor controller box, the project was beginning to outgrow both the IO and processing capability of the existing system. Plus, if I ever wanted to add more stuff, like GPS tracking, LIDAR, more sensors, etc. it would only make the problem worse. &lt;br /&gt;
&lt;br /&gt;
So, after my first taste of the CAN bus from work, I decided to upgrade the wheelchair to use a proper bus for communication between modules. CAN is a bit complex for my tastes, but a similar (but lower level) technology is RS485 which is used in industrial automation. With just 1 converter chip required, this basically takes all the serial ports on all the different modules and ties them together with a 2 wire bus. So, a transmission from 1 reaches all other devices. It's half duplex, so the different modules have to take turns, but that's plenty fine for my case.&lt;br /&gt;
&lt;br /&gt;
The different modules then become greatly simplified; instead of implementing complex control algorithms on the microcontrollers themselves, each module only needs to be responsible for managing IO, mabye counting some encoders, etc. As of today, I have modules for the wheelchair motor controller and arm motor controllers.&lt;br /&gt;
&lt;br /&gt;
==Protocol==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| Bit || 0 || 1 || 2 || 3&lt;br /&gt;
|-&lt;br /&gt;
| Rx || address ||&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=RC_Wheelchair_3&amp;diff=591</id>
		<title>RC Wheelchair 3</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=RC_Wheelchair_3&amp;diff=591"/>
		<updated>2020-09-03T17:18:58Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Since the previous version, I added [[COW arm|an arm]] and a new LORA radio module. With the only microcontroller being ATmega1284p in the original motor controller box, the project was beginning to outgrow both the IO and processing capability of the existing system. Plus, if I ever wanted to add more stuff, like GPS tracking, LIDAR, more sensors, etc. it would only make the problem worse. &lt;br /&gt;
&lt;br /&gt;
So, after my first taste of the CAN bus from work, I decided to upgrade the wheelchair to use a proper bus for communication between modules. CAN is a bit complex for my tastes, but a similar (but lower level) technology is RS485 which is used in industrial automation. With just 1 converter chip required, this basically takes all the serial ports on all the different modules and ties them together with a 2 wire bus. So, a transmission from 1 reaches all other devices. It's half duplex, so the different modules have to take turns, but that's plenty fine for my case.&lt;br /&gt;
&lt;br /&gt;
As of today, I have modules for the wheelchair motor controller and arm motor controllers.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| Bit || 0 || 1 || 2 || 3&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=RC_Wheelchair_3&amp;diff=590</id>
		<title>RC Wheelchair 3</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=RC_Wheelchair_3&amp;diff=590"/>
		<updated>2020-09-03T17:18:06Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: Created page with &amp;quot;Since the previous version, I added an arm and a new LORA radio module. With the only microcontroller being ATmega1284p in the original motor controller box, the p...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Since the previous version, I added [[COW arm|an arm]] and a new LORA radio module. With the only microcontroller being ATmega1284p in the original motor controller box, the project was beginning to outgrow both the IO and processing capability of the existing system. Plus, if I ever wanted to add more stuff, like GPS tracking, LIDAR, more sensors, etc. it would only make the problem worse. &lt;br /&gt;
&lt;br /&gt;
So, after my first taste of the CAN bus from work, I decided to upgrade the wheelchair to use a proper bus for communication between modules. CAN is a bit complex for my tastes, but a similar (but lower level) technology is RS485 which is used in industrial automation. With just 1 converter chip required, this basically takes all the serial ports on all the different modules and ties them together with a 2 wire bus. So, a transmission from 1 reaches all other devices. It's half duplex, so the different modules have to take turns, but that's plenty fine for my case.&lt;br /&gt;
&lt;br /&gt;
As of today, I have modules for the wheelchair motor controller and arm motor controllers.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example&lt;br /&gt;
|-&lt;br /&gt;
| Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example&lt;br /&gt;
|-&lt;br /&gt;
| Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example || Example&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=Main_Page&amp;diff=589</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=Main_Page&amp;diff=589"/>
		<updated>2020-09-03T17:02:28Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: /* Project List */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''This site''' serves documentation for my various projects, mostly for my own reference, but also as a way to share them. Using a wiki for this purpose is certainly overkill, but it is easy to edit and format, and I am familiar with it. Whenever I do a fairly involved project, there's a decent chance I will write up the things I learned on this site. As it stands, this site is still relatively sparse, because I like working on projects more than writing about them, like most people.&lt;br /&gt;
&lt;br /&gt;
This web server and the content it provides is also a project in and of itself. This is running on server hardware and software that I set up myself, so as such even the content you are reading now is experimental. I guarantee no stability or reliable access, although I will do my best to keep this available online for as long as possible (though as of 2020 I have had a server like this running continuously for over 5 years).&lt;br /&gt;
&lt;br /&gt;
==Project List==&lt;br /&gt;
More major projects are listed below, in no particular order. See the list of [[Special:AllPages|all pages here.]]&lt;br /&gt;
&lt;br /&gt;
* [[AWOL Box]] - My take on the infamous Mikal Hart Reverse Geocache™ puzzle&lt;br /&gt;
* [[Nixie Clock 1]] - First completed Nixie clock/display unit&lt;br /&gt;
* [[Nixie Clock 2]] - Second Nixie display&lt;br /&gt;
* [[RC Wheelchair 1]] - Hacking the joystick of an electric wheelchair for remote control&lt;br /&gt;
* [[RC Wheelchair 2]] - A complete high-power h-bridge controller, controls, and radio system.&lt;br /&gt;
* [[RC Wheelchair 3]] - Latest version, using an rs485 bus between different modules &lt;br /&gt;
* [[Wilson X carriage]] - Improved x-carriage for the Prusa i3 style 3d printer.&lt;br /&gt;
* [[COW arm]] - a robot arm for the wheelchair robot base&lt;br /&gt;
* [[Lora module]] - module that makes using LoRa radio communications easier to work with.&lt;br /&gt;
* [[Environmental logger]] - A time capsule experiment&lt;br /&gt;
* [[Model T speedometer]] - General purpose GPS based speedometer for cars that don't have one. &lt;br /&gt;
* [[Geiger counter]] - my homemade Geiger counter using a SBM20 tube&lt;br /&gt;
* [[Raspberry Pi DVR]] - my main way of recording TV off the air&lt;br /&gt;
* [[Wilson Tetris]] - my version of a Tetris clone (in C)&lt;br /&gt;
* [[Door monitor]] - putting an unused security system to &amp;quot;good&amp;quot; use.&lt;br /&gt;
* [[uScreen]] - general purpose module with esp8266 and Nokia LCD&lt;br /&gt;
* [[6888 Transmitter]] - 6888 tube-based AM transmitter&lt;br /&gt;
* [[Sky Pointer]] - a thingy for pointing out objects in the sky.&lt;br /&gt;
* [[Atlas Gears]] - Replacement lathe/mill changegears&lt;br /&gt;
* [[Smart Response XE]] - Hacking of a school response system&lt;br /&gt;
* [[Floppy disk drive hacking]] - Poking around floppy drives with a logic analyzer&lt;br /&gt;
* [[Coronameter]] - Dedicated screen for pandemic panicking.&lt;br /&gt;
* [[Miscellaneous]] - short things that aren't long enough for their own page&lt;br /&gt;
* [[Unpublished Projects]]&lt;br /&gt;
&lt;br /&gt;
==TRS-80 Stuff==&lt;br /&gt;
* [[TRS-80 Model 1 - Character ROM]]&lt;br /&gt;
* [[TRS-80 Model 1 - Wilson Expansion Interface]] - an Expansion Interface (needed to connect floppy drives and other peripherals to the 1977 TRS-80 microcomputer) &lt;br /&gt;
* [[TRS-80 Plug 'n Power Controller]] - reverse engineering an extinct trs-80 accessory&lt;br /&gt;
* [[WC-80]] - A work in progress to build TRS-80 compatible Z80 computer&lt;br /&gt;
&lt;br /&gt;
==Other Services==&lt;br /&gt;
This server has hosted a number of different things since it's original incarnation in high school. Some of the old services are still up (but others are unlisted or taken down due to spam or security risks)&lt;br /&gt;
&lt;br /&gt;
* [http://alnwlsn.ax.lt/lists/ Public Lists] - experimental spam collection&lt;br /&gt;
* [http://alnwlsn.ax.lt/wintercamp/notifier/ Winter Camp Notifier Script] - service that emails you when new Winter Camp discussion takes place&lt;br /&gt;
* [http://alnwlsn.ax.lt/wicm/index.php/Main_Page WICM] - A copy of the Winter Camp encyclopedia translated into MediaWiki&lt;br /&gt;
&lt;br /&gt;
===What is the fish?===&lt;br /&gt;
The green fish is from a game I made when I first learned how to program. Over time, it turned into the logo that I use for my projects.&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=Electronic_Time_Capsule&amp;diff=588</id>
		<title>Electronic Time Capsule</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=Electronic_Time_Capsule&amp;diff=588"/>
		<updated>2020-08-13T13:00:34Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: /* envilog1 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:Eagle.png|thumb]]&lt;br /&gt;
This is a board with a bme280 pressure, humidity and temperature sensor, a ds3232m realtime clock, and 4MB of flash memory. The clock wakes up an atmega328p, it takes readings and stores them, and then goes back to sleep. A low dropout, low quiescent current voltage regulator provides 3.3v power to the board from an external battery. &lt;br /&gt;
&lt;br /&gt;
I built this to be buried inside a time capsule (though it has plenty of other uses), so it has to be able to survive freezing temperatures, and should last at least 5 years without maintenance. A quick calculation of current draw on my prototype says that it should be able to run for about 15 years on AA batteries, and there is plenty of room in the flash memory for hourly measurements (15ish years worth).&lt;br /&gt;
&lt;br /&gt;
Over 10 years, the board is calculated to use &amp;lt;pre&amp;gt;88000hr*((20mA+4)*(.5/3600)+.35mA*(57/3600)+.006mA) = 1309 mAh&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Unit Status==&lt;br /&gt;
===envilog2===&lt;br /&gt;
buried in 2018 time 5 year capsule (started/sealed WC 42,hourly), lithium batteries&lt;br /&gt;
===envilog1===&lt;br /&gt;
On deck in tupperware outside house, started Jan 2019 (hourly), lithium batteries&lt;br /&gt;
* Checked at July 2019 Winter Camp party, status ok.&lt;br /&gt;
* Checked Jan 26, 2020, status OK. Left inside for few days to dry after finding the tupperware cracked. Time was about 65 seconds behind EST.&lt;br /&gt;
* Checked May 20, 2020 - after a heavy rainstorm the past few weeks, I found the tupperware cracked and the unit submerged in about an inch of water. All batteries are dead, although I do not think water reacted quickly with the lithium as I see no melting of the plastic case. After a wash in acetone and brake cleaner, most of the corrosion was removed, and I was able to power up the unit and retrieve the data out the serial port. The water first shorted the trigger pin for the wakeup, before apparently draining the batteries and causing the unit to reboot several times. Interestingly, at these times the on-board humidity sensor recorded &amp;quot;100.00%&amp;quot;&lt;br /&gt;
* As of 2020-08-13, the unit is running on regular batteries indoors. The LEDs had stopped working after the board was submerged in water, but seems to have since dried out and started working again.&lt;br /&gt;
&lt;br /&gt;
===envilog5===&lt;br /&gt;
At Jan trailer, started Jul 2019, hourly, lithium batteries&lt;br /&gt;
* checked 2020-08-05. All OK, clock was 28 seconds later than UTC&lt;br /&gt;
&lt;br /&gt;
===envilog3===&lt;br /&gt;
buried in 2019 time 5 year capsule (started/sealed WC 43,hourly), alkaline batteries&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
* [[File:Envilog-pcb-files.zip]] - The eagle and gerber files needed to make the boards&lt;br /&gt;
* [[File:Envilog-arduino-code.zip]] - The arduino program that runs on the board. Additional board hardware and libraries listed below are needed also.&lt;br /&gt;
&lt;br /&gt;
==Parts List==&lt;br /&gt;
* BME280 board measuring temperature, humidity and pressure.&lt;br /&gt;
* DS3231M measuring time, and temperature&lt;br /&gt;
* Optional voltage divider to measure battery voltage&lt;br /&gt;
* MCP1703AT-3302E/MB - 3.3V voltage low dropout linear regulator&lt;br /&gt;
* ATMEGA328P-AU&lt;br /&gt;
* Winbond 25q32fvsig&lt;br /&gt;
&lt;br /&gt;
==Interface Description==&lt;br /&gt;
[[File:Envilog-boardoutline.png|thumb|The board layout]]&lt;br /&gt;
The board contains 4 dip switches, which pretty much work independently, and one button.&lt;br /&gt;
The switches should be labeled 1-4, with 1 being closest to the center of the board. The off position of the switch has the handle toward the 3 indicator LEDs, and the on position is towards the button. &lt;br /&gt;
&lt;br /&gt;
The functions of the switches are:&lt;br /&gt;
* Switch 1 (on) - Enters the clock setting procedure. The clock is set using only the single button, pressed a number of times to set the date and time. '''You can only set the clock when the board is first connected to power or reset'''. &lt;br /&gt;
* Switch 2 (on) - '''Erases the flash chip.''' The flash must be empty to write new data from the beginning. An extra button press is needed to confirm you really want to do this. If the clock set switch is also on, then you will set the clock first, then erase the flash. '''You can only erase the flash when the board is first connected to power or reset'''.  This helps avoid doing it accidentally. &lt;br /&gt;
* Switch 3 (on) - Dumps the flash. Over serial, the board will output a tab-formatted table of the measurements that have been recorded. The dump will continue until either the entire chip has been read (including blank areas), or Switch 3 has been turned off. To start a dump, turn switch 3 on and then either wait for the RTC to wake up the system, or wake it up yourself with a single press of the button.&lt;br /&gt;
&lt;br /&gt;
* Switch 4 (on) - Blinks a green light every time the 328 powers up. Turn off to save power.&lt;br /&gt;
&lt;br /&gt;
The LED functions are, in general:&lt;br /&gt;
* Green - Status - Blinks when something happens / has happened&lt;br /&gt;
* Red - Working - Turns on when user input is acknowledged &lt;br /&gt;
* Yellow - Waiting for user input&lt;br /&gt;
&lt;br /&gt;
==Software Libraries used==&lt;br /&gt;
The following software was used in the program for this device:&lt;br /&gt;
* Arduino MiniCore hardware package. The board is programmed like a normal arduino board with the arduino bootloader, but this package makes it easier to get a bare 328 chip working with many of the power-heavy features disabled.&lt;br /&gt;
* avr/power, avr/sleep, Wire, SPI arduino libraries, included with the arduino IDE.&lt;br /&gt;
* https://github.com/Marzogh/SPIMemory - for using the external flash memory chip that stores the measurements&lt;br /&gt;
* The SparkFun BME280 library - For configuring the BME280 sensor&lt;br /&gt;
&lt;br /&gt;
==Build / Operating Instructions==&lt;br /&gt;
* Make the board and assemble the components onto it.  Everything is surface mount except the sensor board, which is an Aliexpress special. &lt;br /&gt;
* Flash the 328 on the board with the Arduino MiniCore bootloader. Make sure that the BOD is disabled, and the 8 mhz clock speed is selected. I tried 1 mhz as well, but found that the flash chip did not work well at this speed. The 328 can be programmed with ICSP, just like flashing a normal Arduino. The reset line, VCC and ground pins are brought out on the outside 5 pin header. The remaining SPI pins needed for ICP can be connected to either some test points on the board, or by clipping onto the legs of the flash chip.&lt;br /&gt;
* The board can now be programmed with a usb-serial adapter like any other arduino board. The tx and rx pins on the board connect to the rx and tx pins on the usb-serial device. The reset pin on the board connects through a 0.1uf cap to the DTR pin on the usb-serial, because I didn't include one on the board. Upload the main program.&lt;br /&gt;
* Push switches 1 (clock set) , 2 (flash erase) and 4 (status) on, and connect the board to a serial monitor and power (between 4 and 15 volts).&lt;br /&gt;
* Use the single button to set the clock following the serial terminal instructions. For example, to set the first parameter, the year, to 2018, press the button repeatedly until 18 is shown, then wait 2 seconds to continue. &lt;br /&gt;
* Erase the flash by pressing the button once when prompted.&lt;br /&gt;
* Return all dip switches to off position. Switch 4 can be left on, which will blink the green led whenever the 328 is powered up, but can be turned off to save power.&lt;br /&gt;
* After data is collected, turn switch 3 on and press the button. The data will be output over serial in the form of a table.&lt;br /&gt;
&lt;br /&gt;
==Example Output==&lt;br /&gt;
This is what it looks like when running / dumping measurements in the serial terminal. To create a dump, move switch 3 to on position and press button once. Data is dumped out the TXD pin at 115200 baud.&lt;br /&gt;
&amp;lt;pre&amp;gt;TIME CAPSULE LONG DURATION ENVIROMENTAL LOGGER - Alan J. WIlson 2018&lt;br /&gt;
Current time: 18/09/05 23:29:49&lt;br /&gt;
Flash capacity: 4194304 bytes, 131072 frames&lt;br /&gt;
Frame size: 32&lt;br /&gt;
Next ram index: 457&lt;br /&gt;
&lt;br /&gt;
Current time: 18/09/05 23:29:49&lt;br /&gt;
Cycle: 0&lt;br /&gt;
Index: 457&lt;br /&gt;
Battery ADC: 0&lt;br /&gt;
DS3231 Temperature (C): 22.75&lt;br /&gt;
BME280 Temperature (C): 22.70&lt;br /&gt;
BME280 Pressure   (Pa): 99898.25&lt;br /&gt;
BME280 Humidity    (%): 51.34&lt;br /&gt;
Write OK&lt;br /&gt;
&lt;br /&gt;
FLASH MEMORY DATA DUMP FOLLOWS&lt;br /&gt;
Index	Cycle	Year	Month	Day	Hour	Minute	Second	DS3231 Temperature (C)	Temperature (C)	Pressure (Pa)	Humidity (%)	Battery (10-bit ADC)&lt;br /&gt;
0	0	18	9	5	14	44	45	22.50	22.22	99527.77	45.77	0	&lt;br /&gt;
1	1	18	9	5	14	44	45	22.50	22.19	99525.35	46.32	0	&lt;br /&gt;
2	2	18	9	5	14	44	58	22.75	22.21	99520.82	46.35	0	&lt;br /&gt;
3	3	18	9	5	14	45	10	22.75	22.19	99530.60	46.18	0	&lt;br /&gt;
4	4	18	9	5	14	46	10	22.50	22.23	99526.82	46.55	0	&lt;br /&gt;
5	5	18	9	5	14	47	10	22.25	22.29	99525.55	46.44	0	&lt;br /&gt;
6	6	18	9	5	14	48	10	22.25	22.02	99528.21	47.05	0	&lt;br /&gt;
7	7	18	9	5	14	49	10	22.00	21.90	99535.94	47.95	0	&lt;br /&gt;
8	8	18	9	5	14	50	10	22.00	21.79	99527.93	48.24	0	&lt;br /&gt;
9	9	18	9	5	14	51	10	22.00	21.75	99536.94	48.41	0	&lt;br /&gt;
10	10	18	9	5	14	52	10	22.00	21.74	99537.06	48.59	0	&lt;br /&gt;
11	11	18	9	5	14	53	10	22.00	21.74	99530.03	48.66	0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Serial Console (12/08/2018)==&lt;br /&gt;
In the original version of the software, I use the single button to do most of the setup. This can be very difficult, because the board and button are tiny, and pressing it is required many times, especially for setting the time. Since I implement a simple serial console tool into many of my other projects, I figured it would be a good idea to have one here too, since it is probably fairly likely that you will have the device hooked up to a computer when setting it up anyways.&lt;br /&gt;
&lt;br /&gt;
The serial console I made accepts commands one line at a time, but there is a timeout so that the system does not get stuck waiting forever for a character that never comes. This makes it excellent for programs that send data one line at a time, like the Arduino IDE, but it will not work that well for standard dumb terminal unless you can type very quickly.&lt;br /&gt;
&lt;br /&gt;
The console accepts commands in the form of a single letter, followed by a floating point number, and finally the newline character. For example:&lt;br /&gt;
&amp;lt;pre&amp;gt;y18.00\n&amp;lt;/pre&amp;gt;&lt;br /&gt;
The letter gets passed along as the command, and the number gets passed along as the parameter. It doesn't have to stay floating point either, it will just get converted into an integer or whatever you need. &lt;br /&gt;
&lt;br /&gt;
To use the serial console, set your terminal to 9600 baud and reset the board. Then enter '''e'''. I found that the baudrate of the board's normal 115200 is too high to reliably accept serial input, but works fine for outputing data at high speed during normal operation of the board. &lt;br /&gt;
&lt;br /&gt;
The following table documents all the commands and their functions:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Command !! Parameter !! Description !! Example&lt;br /&gt;
|-&lt;br /&gt;
| e || any || Enters the Serial control mode || &lt;br /&gt;
|-&lt;br /&gt;
| t || 1 - start listing&amp;lt;br&amp;gt;0 - stop listing || Lists continuously the current time. This is helpful when setting it exactly to another source. || t1 - lists time&lt;br /&gt;
|-&lt;br /&gt;
| a || 1 - start listing&amp;lt;br&amp;gt;0 - stop listing || Lists continuously the flash data. This is much slower than using the switch #3 because it is only at 9600 baud. || a1&lt;br /&gt;
|-&lt;br /&gt;
| r || any || Displays the DS3232M RTC registers ||&lt;br /&gt;
|-&lt;br /&gt;
| y || 2 digit year || Sets the year of the RTC || y18 - sets to year 2018&lt;br /&gt;
|-&lt;br /&gt;
| m || 2 digit month || Sets the month of the RTC || m12 - sets to month December&lt;br /&gt;
|-&lt;br /&gt;
| d || 2 digit day || Sets the day of month of the RTC || d8 - sets to day 8&lt;br /&gt;
|-&lt;br /&gt;
| H || 2 digit hour || Sets the hour of the RTC || H32 - sets to hour 1PM&lt;br /&gt;
|-&lt;br /&gt;
| M || 2 digit minute || Sets the minute of the RTC || M32 - sets to minute 32&lt;br /&gt;
|-&lt;br /&gt;
| S || 2 digit second || Sets the seconds of the RTC || S0 - sets to second 0&lt;br /&gt;
|-&lt;br /&gt;
| F || value 12345 || Erases the flash memory || F12345&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Octave Plotter==&lt;br /&gt;
If the output table is changed from tab formatting to CSV (you can do this with Notepad++ or some other text editor), you can use this script in GNU Octave (an open source MatLab alternative) to make a handy plot of the data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
clear;&lt;br /&gt;
clc;&lt;br /&gt;
clf;&lt;br /&gt;
&lt;br /&gt;
raw = load(&amp;quot;j.csv&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
%frame format:&lt;br /&gt;
%[ time, ds3231T, temp, pressure, humidity ]&lt;br /&gt;
&lt;br /&gt;
i = 0;&lt;br /&gt;
while(i&amp;lt;length(raw(:,1)))&lt;br /&gt;
  i++;&lt;br /&gt;
  frame(i,:)=[datenum([2000+raw(i,3),raw(i,4),raw(i,5),raw(i,6),raw(i,7),raw(i,8)]),raw(i,9),raw(i,10),raw(i,11),raw(i,12)];&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
figure(1)&lt;br /&gt;
ha(1)=subplot(3,1,1);&lt;br /&gt;
hold on;&lt;br /&gt;
title(strcat(&amp;quot;Enviroment data: &amp;quot;,datestr(frame(1,1),&amp;quot; mmm 'yy &amp;quot;),&amp;quot; to&amp;quot;,datestr(frame(i,1),&amp;quot; mmm 'yy &amp;quot;)));&lt;br /&gt;
plot(frame(:,1),frame(:,2)*(9/5)+32);&lt;br /&gt;
plot(frame(:,1),frame(:,3)*(9/5)+32);&lt;br /&gt;
datetick(&amp;quot;x&amp;quot;, &amp;quot;mmm/yy&amp;quot;);&lt;br /&gt;
grid on;&lt;br /&gt;
xlabel(&amp;quot;Time&amp;quot;);&lt;br /&gt;
ylabel(&amp;quot;Temperature (F)&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
ha(2)=subplot(3,1,2);&lt;br /&gt;
plot(frame(:,1),frame(:,4));&lt;br /&gt;
datetick(&amp;quot;x&amp;quot;, &amp;quot;mmm/yy&amp;quot;);&lt;br /&gt;
grid on;&lt;br /&gt;
xlabel(&amp;quot;Time&amp;quot;);&lt;br /&gt;
ylabel(&amp;quot;Pressure (Pa)&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
ha(3)=subplot(3,1,3);&lt;br /&gt;
plot(frame(:,1),frame(:,5));&lt;br /&gt;
datetick(&amp;quot;x&amp;quot;, &amp;quot;mmm/yy&amp;quot;);&lt;br /&gt;
grid on;&lt;br /&gt;
xlabel(&amp;quot;Time&amp;quot;);&lt;br /&gt;
ylabel(&amp;quot;Humidity (%)&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
linkaxes(ha, &amp;quot;x&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Details (hackaday.io)==&lt;br /&gt;
This project has been ongoing for a few months now, but I, like many people, don't document projects as well / as often as I should. This project represents a lot of firsts for me, such as the first time working with low power, first time ever soldering SMD components, and only the second project where I have designed a PCB. Most of my projects consist of either breadboards, prototyping boards, or some bizarre freeform/deadbug. I am not an electrical engineer or programmer, just a self taught hobbyist, which does sometimes mean that I have no idea if I am doing something &amp;quot;the right way.&amp;quot; I want to keep my designs simple, using components that are well known, and programming no more complex than Arduino.  With that in mind, the following ramblings explain my thoughts in building this project.&lt;br /&gt;
&lt;br /&gt;
Since this was designed to be buried in a time capsule, the first order of business is how to get power. I had never worked with any low power sleep modes on microcontrollers, but I have plenty of devices that last for years with just batteries. Plenty of projects here on HAD that use a coin cell, but i'd be hard pressed to get 5 years out of one of those. Sounds like a good solution is normal AA batteries. I will use enough so that even when the battery is near dead, the voltage will still be high enough to power the device. It also gives me a bit of extra room to not worry so much about squeezing every last drop of juice out the batteries.&lt;br /&gt;
&lt;br /&gt;
===The Microcontroller===&lt;br /&gt;
It's an ATmega328, nothing special. I thought about using an ATtiny, but since both have the same sleep power requirements, I kept the 328 for some extra pins and more ram, in case I wanted to expand the system a little or do some kind of heavier processing. Yes, this is essentially the same thing as building your own Arduino, but it's simple and it works.&lt;br /&gt;
&lt;br /&gt;
The important thing is to be able to get the 328 into and out of sleep mode. Having never experimented with this mode before, Nick Gammon's tutorial taught me most of what I needed to know. I want this board to run for a long time, so everything must go, like the ADCs, and brownout detection. This brings the sleep current down to an impressive 0.1 uA. I'll re-enable any periperials I need upon waking up.&lt;br /&gt;
&lt;br /&gt;
To wake up the chip, I'll use one of the interrupt pins.&lt;br /&gt;
&lt;br /&gt;
===The Clock===&lt;br /&gt;
I'm using a real-time clock chip to wake up the 328. It's much more accurate than an internal watchdog timer, and lets me add a timestamp to my measurements. I've used the DS3232 rtc before in the form of some inexpensive modules, so I decided to use them for this project as well. This chip contains a real time clock with temperature compensation. The clock accuracy is supposed to be quite good, on the order of a few PPM.&lt;br /&gt;
&lt;br /&gt;
The DS3231 can be powered exclusively through the Vbatt pin instead of the normal Vcc pin. This saves some power according to the datasheet, but also increases the temperature compensation time to once a minute rather than every few seconds. For my purposes, this is fine. &lt;br /&gt;
For interfacing to the 328, 3 pins are used. Two are the i2c pins, and the third is the INT pin. The DS3231 has an alarm function, which can trigger this pin to go LOW when tripped. Connected to an interrupt pin on the 328, it is used to wake up the microcontroller from deep sleep. The 328 then resets the alarm, and reads out a timestamp over i2c. Alarm functions are not inplemented in most of the Arduino libraries I have found, so I wrote a simple one to make working directly with the RTC easier (but it only uses one of the two alarms).&lt;br /&gt;
&lt;br /&gt;
My initial testing with RTC and microcontroller seemed promising, with the circuit waking up and blinking an LED once per minute as I had set it to do. However, when placing the setup in the freezer, the clock was more than a minute off after only a few hours. This is probably because the DS3231s I got from eBay were counterfeit. They work fine for room temperature operation (I have a counterfeit one in a nixie clock I made and it keeps accurate time) but my board needs to survive in freezing temperatures. A few genuine DS3232M from Maxim solved the problem, and my boards now keep accurate time even in the freezer. The M version is a smaller package, and is very slightly less accurate but is supposed to be more durable, using a MEMS oscillator instead of a crystal. The 3232 part number includes some extra battery backed general purpose memory, which I thought might be useful but is still not used yet in my design. The new chip takes the same commands as the old one, so no code changes were needed.&lt;br /&gt;
&lt;br /&gt;
===The Memory===&lt;br /&gt;
Initially, I tried using a 128kb 24lc1025 EEprom as the main storage space, but quickly found it would run out of space in less than the 5 year design life, so I would need to do something else.&lt;br /&gt;
&lt;br /&gt;
While working on this project, I came across the Pearl cave project, where the same idea is used to make environmental sensors for monitoring caves. Their solution was to use an SD card, but it was found that different SD cards used different amounts of power, and that the cards did not like being turned on and off. Rather than an SD card, I thought about using a flash memory chip, maybe in the same way SPIFFS is used in my esp8266 projects. It would also be soldered to the board, with no spring connectors that could corrode or come loose. I settled on the Winbond 25q32fvsig, a 4mbyte flash chip. This would have plenty of room for years of measurements, and also has a sleep mode which takes the power down to a sub-microamp level. I used Marzogh's SPIMemory library for this, which takes care of most of the work in interfacing with the flash. When working with these chips, I found I could not write my 17 byte long measurement frame sequentially like I could with the EEPROM. Increasing the frame size to 32 bytes aligned them with the flash pages and I was able to read and write without errors, as well as fit a few extra measurements in. Now I can store 128k samples, instead of just 128k bytes. At one sampling per hour, this is almost 15 years of storage.&lt;br /&gt;
&lt;br /&gt;
On the board in my picture, you can spot my mistake in the board design. I accidentally connected the reset and write protect pins to GND instead of VCC. This means that the chip is in a constant reset state, and cannot be read or written. With a Dremel, I carefully cut the traces. The reset pin is right next to the VCC pin, so a little excess solder is enough to bridge them. A jumper of thin wire across the top of the chip brings the write protect pin positive as well. This error is fixed in the released version provided in Files. &lt;br /&gt;
&lt;br /&gt;
Initially, all I wanted to measure was temperature. However, that's not a lot of payout for 5 years work. Since the time capsule supposed to be a sealed PVC tube, I thought that it would be interesting to see what the internal pressure was like.  The BME280 can do both temperature and pressure, and also humidity. It also has a low power sleep mode, and communicates over i2c. Since I've never soldered a part that small, I cheated and used a premade breakout board module, which fits on to the i2c header behind the main header.  The header allows for more expansion also, in addition to or if another i2c sensor is to be used.&lt;br /&gt;
&lt;br /&gt;
===Power===&lt;br /&gt;
With the selection of a 3.3v memory chip and sensor, I was limited to running the board at 3.3v instead of the 5v I had originally planned. I didn't think that two AA batteries or a coin cell could last long enough and still be enough voltage to power the board after it lost some charge. Since I care more about reliability with no maintenance, and ton't have too much of a size constraint on the size of the batteries. I thought it would be better to run the components at some stable voltage that did not drop with the batteries (though I recognize that this will increase idle current draw). I could have run the 328 at 4.5v with 3 AAs, but I would still need an additional 3.3v supply and some kind of logic level  conversion. The pearl cave project had the same problem, and solved it by using a simple linear regulator. However, the regular has to be a low quiesent current type, which drew little current on it's own, unlike an lm1117 found on most of the cheap Arduino boards. It took me a while to find a version that had an input voltage greater than 5.5v (in case I decided to use more than 3 cells), and still had good performance. Both the Mcp1703 and HT7773 are good candidates, and come in a sot89 package with the same pinout. I happened to have some of the through hole HT regulators on hand, and used them while I waited for some eBay mcp1703s to arrive. Unfortunately, the eBay mcp1703s turned out to be counterfeit also, and did not meet the claimed quiescent current listed in the datasheet. The current was instead around 70 microamps, more than 10x what I had planned. Again, some genuine samples from Microchip fixed the problem. Interestingly, the HT chips which are produced by a Chinese company and were bought overseas from an unknown seller performed perfectly.&lt;br /&gt;
&lt;br /&gt;
===Operator interface===&lt;br /&gt;
Only seven IO pins on the 328 were being used thus far. Two for i2c, one for the clock wake up pin, and 4 for the SPI flash memory. I added an eighth, a voltage divider of high value resistors on an ADC pin to measure the battery voltage, which has not been fitted yet. But that still leaves lots of other pins open on the 328. I connected four of them to a small 4 position dip switch that I had on hand, three more to colored LEDs, and a button switch to the other interrupt pin. In this way, the board can still be verified working after disconnecting from the programmer. Otherwise, I can't tell if the board is working fine or a brick. The button enables waking up the 328 without waiting for the clock alarm to trip, which could take an hour on the slow setting. The dip switches allow for selecting different models, like setting the clock accurately to the second, or erasing the flash to start over, or dumping the flash contents over serial.&lt;br /&gt;
&lt;br /&gt;
I decided to include a serial interface header on one side, so it will be easier to get the data off of the board when the time comes. I'll use the Arduino bootloader so I can program the chip over serial, since I have many more usb serial adapters than AVR programmers, and the cheap AVR programmers I do have only work 5% of the time anyways. VCC, GND, Tx, Rx, and the 328 /RST pins are brought out on the 5 pin header.&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=Electronic_Time_Capsule&amp;diff=587</id>
		<title>Electronic Time Capsule</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=Electronic_Time_Capsule&amp;diff=587"/>
		<updated>2020-08-13T12:55:39Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: /* envilog5 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:Eagle.png|thumb]]&lt;br /&gt;
This is a board with a bme280 pressure, humidity and temperature sensor, a ds3232m realtime clock, and 4MB of flash memory. The clock wakes up an atmega328p, it takes readings and stores them, and then goes back to sleep. A low dropout, low quiescent current voltage regulator provides 3.3v power to the board from an external battery. &lt;br /&gt;
&lt;br /&gt;
I built this to be buried inside a time capsule (though it has plenty of other uses), so it has to be able to survive freezing temperatures, and should last at least 5 years without maintenance. A quick calculation of current draw on my prototype says that it should be able to run for about 15 years on AA batteries, and there is plenty of room in the flash memory for hourly measurements (15ish years worth).&lt;br /&gt;
&lt;br /&gt;
Over 10 years, the board is calculated to use &amp;lt;pre&amp;gt;88000hr*((20mA+4)*(.5/3600)+.35mA*(57/3600)+.006mA) = 1309 mAh&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Unit Status==&lt;br /&gt;
===envilog2===&lt;br /&gt;
buried in 2018 time 5 year capsule (started/sealed WC 42,hourly), lithium batteries&lt;br /&gt;
===envilog1===&lt;br /&gt;
On deck in tupperware outside house, started Jan 2019 (hourly), lithium batteries&lt;br /&gt;
* Checked at July 2019 Winter Camp party, status ok.&lt;br /&gt;
* Checked Jan 26, 2020, status OK. Left inside for few days to dry after finding the tupperware cracked. Time was about 65 seconds behind EST.&lt;br /&gt;
* Checked May 20, 2020 - after a heavy rainstorm the past few weeks, I found the tupperware cracked and the unit submerged in about an inch of water. All batteries are dead, although I do not think water reacted quickly with the lithium as I see no melting of the plastic case. After a wash in acetone and brake cleaner, most of the corrosion was removed, and I was able to power up the unit and retrieve the data out the serial port. The water first shorted the trigger pin for the wakeup, before apparently draining the batteries and causing the unit to reboot several times. Interestingly, at these times the on-board humidity sensor recorded &amp;quot;100.00%&amp;quot;&lt;br /&gt;
&lt;br /&gt;
===envilog5===&lt;br /&gt;
At Jan trailer, started Jul 2019, hourly, lithium batteries&lt;br /&gt;
* checked 2020-08-05. All OK, clock was 28 seconds later than UTC&lt;br /&gt;
&lt;br /&gt;
===envilog3===&lt;br /&gt;
buried in 2019 time 5 year capsule (started/sealed WC 43,hourly), alkaline batteries&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
* [[File:Envilog-pcb-files.zip]] - The eagle and gerber files needed to make the boards&lt;br /&gt;
* [[File:Envilog-arduino-code.zip]] - The arduino program that runs on the board. Additional board hardware and libraries listed below are needed also.&lt;br /&gt;
&lt;br /&gt;
==Parts List==&lt;br /&gt;
* BME280 board measuring temperature, humidity and pressure.&lt;br /&gt;
* DS3231M measuring time, and temperature&lt;br /&gt;
* Optional voltage divider to measure battery voltage&lt;br /&gt;
* MCP1703AT-3302E/MB - 3.3V voltage low dropout linear regulator&lt;br /&gt;
* ATMEGA328P-AU&lt;br /&gt;
* Winbond 25q32fvsig&lt;br /&gt;
&lt;br /&gt;
==Interface Description==&lt;br /&gt;
[[File:Envilog-boardoutline.png|thumb|The board layout]]&lt;br /&gt;
The board contains 4 dip switches, which pretty much work independently, and one button.&lt;br /&gt;
The switches should be labeled 1-4, with 1 being closest to the center of the board. The off position of the switch has the handle toward the 3 indicator LEDs, and the on position is towards the button. &lt;br /&gt;
&lt;br /&gt;
The functions of the switches are:&lt;br /&gt;
* Switch 1 (on) - Enters the clock setting procedure. The clock is set using only the single button, pressed a number of times to set the date and time. '''You can only set the clock when the board is first connected to power or reset'''. &lt;br /&gt;
* Switch 2 (on) - '''Erases the flash chip.''' The flash must be empty to write new data from the beginning. An extra button press is needed to confirm you really want to do this. If the clock set switch is also on, then you will set the clock first, then erase the flash. '''You can only erase the flash when the board is first connected to power or reset'''.  This helps avoid doing it accidentally. &lt;br /&gt;
* Switch 3 (on) - Dumps the flash. Over serial, the board will output a tab-formatted table of the measurements that have been recorded. The dump will continue until either the entire chip has been read (including blank areas), or Switch 3 has been turned off. To start a dump, turn switch 3 on and then either wait for the RTC to wake up the system, or wake it up yourself with a single press of the button.&lt;br /&gt;
&lt;br /&gt;
* Switch 4 (on) - Blinks a green light every time the 328 powers up. Turn off to save power.&lt;br /&gt;
&lt;br /&gt;
The LED functions are, in general:&lt;br /&gt;
* Green - Status - Blinks when something happens / has happened&lt;br /&gt;
* Red - Working - Turns on when user input is acknowledged &lt;br /&gt;
* Yellow - Waiting for user input&lt;br /&gt;
&lt;br /&gt;
==Software Libraries used==&lt;br /&gt;
The following software was used in the program for this device:&lt;br /&gt;
* Arduino MiniCore hardware package. The board is programmed like a normal arduino board with the arduino bootloader, but this package makes it easier to get a bare 328 chip working with many of the power-heavy features disabled.&lt;br /&gt;
* avr/power, avr/sleep, Wire, SPI arduino libraries, included with the arduino IDE.&lt;br /&gt;
* https://github.com/Marzogh/SPIMemory - for using the external flash memory chip that stores the measurements&lt;br /&gt;
* The SparkFun BME280 library - For configuring the BME280 sensor&lt;br /&gt;
&lt;br /&gt;
==Build / Operating Instructions==&lt;br /&gt;
* Make the board and assemble the components onto it.  Everything is surface mount except the sensor board, which is an Aliexpress special. &lt;br /&gt;
* Flash the 328 on the board with the Arduino MiniCore bootloader. Make sure that the BOD is disabled, and the 8 mhz clock speed is selected. I tried 1 mhz as well, but found that the flash chip did not work well at this speed. The 328 can be programmed with ICSP, just like flashing a normal Arduino. The reset line, VCC and ground pins are brought out on the outside 5 pin header. The remaining SPI pins needed for ICP can be connected to either some test points on the board, or by clipping onto the legs of the flash chip.&lt;br /&gt;
* The board can now be programmed with a usb-serial adapter like any other arduino board. The tx and rx pins on the board connect to the rx and tx pins on the usb-serial device. The reset pin on the board connects through a 0.1uf cap to the DTR pin on the usb-serial, because I didn't include one on the board. Upload the main program.&lt;br /&gt;
* Push switches 1 (clock set) , 2 (flash erase) and 4 (status) on, and connect the board to a serial monitor and power (between 4 and 15 volts).&lt;br /&gt;
* Use the single button to set the clock following the serial terminal instructions. For example, to set the first parameter, the year, to 2018, press the button repeatedly until 18 is shown, then wait 2 seconds to continue. &lt;br /&gt;
* Erase the flash by pressing the button once when prompted.&lt;br /&gt;
* Return all dip switches to off position. Switch 4 can be left on, which will blink the green led whenever the 328 is powered up, but can be turned off to save power.&lt;br /&gt;
* After data is collected, turn switch 3 on and press the button. The data will be output over serial in the form of a table.&lt;br /&gt;
&lt;br /&gt;
==Example Output==&lt;br /&gt;
This is what it looks like when running / dumping measurements in the serial terminal. To create a dump, move switch 3 to on position and press button once. Data is dumped out the TXD pin at 115200 baud.&lt;br /&gt;
&amp;lt;pre&amp;gt;TIME CAPSULE LONG DURATION ENVIROMENTAL LOGGER - Alan J. WIlson 2018&lt;br /&gt;
Current time: 18/09/05 23:29:49&lt;br /&gt;
Flash capacity: 4194304 bytes, 131072 frames&lt;br /&gt;
Frame size: 32&lt;br /&gt;
Next ram index: 457&lt;br /&gt;
&lt;br /&gt;
Current time: 18/09/05 23:29:49&lt;br /&gt;
Cycle: 0&lt;br /&gt;
Index: 457&lt;br /&gt;
Battery ADC: 0&lt;br /&gt;
DS3231 Temperature (C): 22.75&lt;br /&gt;
BME280 Temperature (C): 22.70&lt;br /&gt;
BME280 Pressure   (Pa): 99898.25&lt;br /&gt;
BME280 Humidity    (%): 51.34&lt;br /&gt;
Write OK&lt;br /&gt;
&lt;br /&gt;
FLASH MEMORY DATA DUMP FOLLOWS&lt;br /&gt;
Index	Cycle	Year	Month	Day	Hour	Minute	Second	DS3231 Temperature (C)	Temperature (C)	Pressure (Pa)	Humidity (%)	Battery (10-bit ADC)&lt;br /&gt;
0	0	18	9	5	14	44	45	22.50	22.22	99527.77	45.77	0	&lt;br /&gt;
1	1	18	9	5	14	44	45	22.50	22.19	99525.35	46.32	0	&lt;br /&gt;
2	2	18	9	5	14	44	58	22.75	22.21	99520.82	46.35	0	&lt;br /&gt;
3	3	18	9	5	14	45	10	22.75	22.19	99530.60	46.18	0	&lt;br /&gt;
4	4	18	9	5	14	46	10	22.50	22.23	99526.82	46.55	0	&lt;br /&gt;
5	5	18	9	5	14	47	10	22.25	22.29	99525.55	46.44	0	&lt;br /&gt;
6	6	18	9	5	14	48	10	22.25	22.02	99528.21	47.05	0	&lt;br /&gt;
7	7	18	9	5	14	49	10	22.00	21.90	99535.94	47.95	0	&lt;br /&gt;
8	8	18	9	5	14	50	10	22.00	21.79	99527.93	48.24	0	&lt;br /&gt;
9	9	18	9	5	14	51	10	22.00	21.75	99536.94	48.41	0	&lt;br /&gt;
10	10	18	9	5	14	52	10	22.00	21.74	99537.06	48.59	0	&lt;br /&gt;
11	11	18	9	5	14	53	10	22.00	21.74	99530.03	48.66	0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Serial Console (12/08/2018)==&lt;br /&gt;
In the original version of the software, I use the single button to do most of the setup. This can be very difficult, because the board and button are tiny, and pressing it is required many times, especially for setting the time. Since I implement a simple serial console tool into many of my other projects, I figured it would be a good idea to have one here too, since it is probably fairly likely that you will have the device hooked up to a computer when setting it up anyways.&lt;br /&gt;
&lt;br /&gt;
The serial console I made accepts commands one line at a time, but there is a timeout so that the system does not get stuck waiting forever for a character that never comes. This makes it excellent for programs that send data one line at a time, like the Arduino IDE, but it will not work that well for standard dumb terminal unless you can type very quickly.&lt;br /&gt;
&lt;br /&gt;
The console accepts commands in the form of a single letter, followed by a floating point number, and finally the newline character. For example:&lt;br /&gt;
&amp;lt;pre&amp;gt;y18.00\n&amp;lt;/pre&amp;gt;&lt;br /&gt;
The letter gets passed along as the command, and the number gets passed along as the parameter. It doesn't have to stay floating point either, it will just get converted into an integer or whatever you need. &lt;br /&gt;
&lt;br /&gt;
To use the serial console, set your terminal to 9600 baud and reset the board. Then enter '''e'''. I found that the baudrate of the board's normal 115200 is too high to reliably accept serial input, but works fine for outputing data at high speed during normal operation of the board. &lt;br /&gt;
&lt;br /&gt;
The following table documents all the commands and their functions:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Command !! Parameter !! Description !! Example&lt;br /&gt;
|-&lt;br /&gt;
| e || any || Enters the Serial control mode || &lt;br /&gt;
|-&lt;br /&gt;
| t || 1 - start listing&amp;lt;br&amp;gt;0 - stop listing || Lists continuously the current time. This is helpful when setting it exactly to another source. || t1 - lists time&lt;br /&gt;
|-&lt;br /&gt;
| a || 1 - start listing&amp;lt;br&amp;gt;0 - stop listing || Lists continuously the flash data. This is much slower than using the switch #3 because it is only at 9600 baud. || a1&lt;br /&gt;
|-&lt;br /&gt;
| r || any || Displays the DS3232M RTC registers ||&lt;br /&gt;
|-&lt;br /&gt;
| y || 2 digit year || Sets the year of the RTC || y18 - sets to year 2018&lt;br /&gt;
|-&lt;br /&gt;
| m || 2 digit month || Sets the month of the RTC || m12 - sets to month December&lt;br /&gt;
|-&lt;br /&gt;
| d || 2 digit day || Sets the day of month of the RTC || d8 - sets to day 8&lt;br /&gt;
|-&lt;br /&gt;
| H || 2 digit hour || Sets the hour of the RTC || H32 - sets to hour 1PM&lt;br /&gt;
|-&lt;br /&gt;
| M || 2 digit minute || Sets the minute of the RTC || M32 - sets to minute 32&lt;br /&gt;
|-&lt;br /&gt;
| S || 2 digit second || Sets the seconds of the RTC || S0 - sets to second 0&lt;br /&gt;
|-&lt;br /&gt;
| F || value 12345 || Erases the flash memory || F12345&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Octave Plotter==&lt;br /&gt;
If the output table is changed from tab formatting to CSV (you can do this with Notepad++ or some other text editor), you can use this script in GNU Octave (an open source MatLab alternative) to make a handy plot of the data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
clear;&lt;br /&gt;
clc;&lt;br /&gt;
clf;&lt;br /&gt;
&lt;br /&gt;
raw = load(&amp;quot;j.csv&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
%frame format:&lt;br /&gt;
%[ time, ds3231T, temp, pressure, humidity ]&lt;br /&gt;
&lt;br /&gt;
i = 0;&lt;br /&gt;
while(i&amp;lt;length(raw(:,1)))&lt;br /&gt;
  i++;&lt;br /&gt;
  frame(i,:)=[datenum([2000+raw(i,3),raw(i,4),raw(i,5),raw(i,6),raw(i,7),raw(i,8)]),raw(i,9),raw(i,10),raw(i,11),raw(i,12)];&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
figure(1)&lt;br /&gt;
ha(1)=subplot(3,1,1);&lt;br /&gt;
hold on;&lt;br /&gt;
title(strcat(&amp;quot;Enviroment data: &amp;quot;,datestr(frame(1,1),&amp;quot; mmm 'yy &amp;quot;),&amp;quot; to&amp;quot;,datestr(frame(i,1),&amp;quot; mmm 'yy &amp;quot;)));&lt;br /&gt;
plot(frame(:,1),frame(:,2)*(9/5)+32);&lt;br /&gt;
plot(frame(:,1),frame(:,3)*(9/5)+32);&lt;br /&gt;
datetick(&amp;quot;x&amp;quot;, &amp;quot;mmm/yy&amp;quot;);&lt;br /&gt;
grid on;&lt;br /&gt;
xlabel(&amp;quot;Time&amp;quot;);&lt;br /&gt;
ylabel(&amp;quot;Temperature (F)&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
ha(2)=subplot(3,1,2);&lt;br /&gt;
plot(frame(:,1),frame(:,4));&lt;br /&gt;
datetick(&amp;quot;x&amp;quot;, &amp;quot;mmm/yy&amp;quot;);&lt;br /&gt;
grid on;&lt;br /&gt;
xlabel(&amp;quot;Time&amp;quot;);&lt;br /&gt;
ylabel(&amp;quot;Pressure (Pa)&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
ha(3)=subplot(3,1,3);&lt;br /&gt;
plot(frame(:,1),frame(:,5));&lt;br /&gt;
datetick(&amp;quot;x&amp;quot;, &amp;quot;mmm/yy&amp;quot;);&lt;br /&gt;
grid on;&lt;br /&gt;
xlabel(&amp;quot;Time&amp;quot;);&lt;br /&gt;
ylabel(&amp;quot;Humidity (%)&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
linkaxes(ha, &amp;quot;x&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Details (hackaday.io)==&lt;br /&gt;
This project has been ongoing for a few months now, but I, like many people, don't document projects as well / as often as I should. This project represents a lot of firsts for me, such as the first time working with low power, first time ever soldering SMD components, and only the second project where I have designed a PCB. Most of my projects consist of either breadboards, prototyping boards, or some bizarre freeform/deadbug. I am not an electrical engineer or programmer, just a self taught hobbyist, which does sometimes mean that I have no idea if I am doing something &amp;quot;the right way.&amp;quot; I want to keep my designs simple, using components that are well known, and programming no more complex than Arduino.  With that in mind, the following ramblings explain my thoughts in building this project.&lt;br /&gt;
&lt;br /&gt;
Since this was designed to be buried in a time capsule, the first order of business is how to get power. I had never worked with any low power sleep modes on microcontrollers, but I have plenty of devices that last for years with just batteries. Plenty of projects here on HAD that use a coin cell, but i'd be hard pressed to get 5 years out of one of those. Sounds like a good solution is normal AA batteries. I will use enough so that even when the battery is near dead, the voltage will still be high enough to power the device. It also gives me a bit of extra room to not worry so much about squeezing every last drop of juice out the batteries.&lt;br /&gt;
&lt;br /&gt;
===The Microcontroller===&lt;br /&gt;
It's an ATmega328, nothing special. I thought about using an ATtiny, but since both have the same sleep power requirements, I kept the 328 for some extra pins and more ram, in case I wanted to expand the system a little or do some kind of heavier processing. Yes, this is essentially the same thing as building your own Arduino, but it's simple and it works.&lt;br /&gt;
&lt;br /&gt;
The important thing is to be able to get the 328 into and out of sleep mode. Having never experimented with this mode before, Nick Gammon's tutorial taught me most of what I needed to know. I want this board to run for a long time, so everything must go, like the ADCs, and brownout detection. This brings the sleep current down to an impressive 0.1 uA. I'll re-enable any periperials I need upon waking up.&lt;br /&gt;
&lt;br /&gt;
To wake up the chip, I'll use one of the interrupt pins.&lt;br /&gt;
&lt;br /&gt;
===The Clock===&lt;br /&gt;
I'm using a real-time clock chip to wake up the 328. It's much more accurate than an internal watchdog timer, and lets me add a timestamp to my measurements. I've used the DS3232 rtc before in the form of some inexpensive modules, so I decided to use them for this project as well. This chip contains a real time clock with temperature compensation. The clock accuracy is supposed to be quite good, on the order of a few PPM.&lt;br /&gt;
&lt;br /&gt;
The DS3231 can be powered exclusively through the Vbatt pin instead of the normal Vcc pin. This saves some power according to the datasheet, but also increases the temperature compensation time to once a minute rather than every few seconds. For my purposes, this is fine. &lt;br /&gt;
For interfacing to the 328, 3 pins are used. Two are the i2c pins, and the third is the INT pin. The DS3231 has an alarm function, which can trigger this pin to go LOW when tripped. Connected to an interrupt pin on the 328, it is used to wake up the microcontroller from deep sleep. The 328 then resets the alarm, and reads out a timestamp over i2c. Alarm functions are not inplemented in most of the Arduino libraries I have found, so I wrote a simple one to make working directly with the RTC easier (but it only uses one of the two alarms).&lt;br /&gt;
&lt;br /&gt;
My initial testing with RTC and microcontroller seemed promising, with the circuit waking up and blinking an LED once per minute as I had set it to do. However, when placing the setup in the freezer, the clock was more than a minute off after only a few hours. This is probably because the DS3231s I got from eBay were counterfeit. They work fine for room temperature operation (I have a counterfeit one in a nixie clock I made and it keeps accurate time) but my board needs to survive in freezing temperatures. A few genuine DS3232M from Maxim solved the problem, and my boards now keep accurate time even in the freezer. The M version is a smaller package, and is very slightly less accurate but is supposed to be more durable, using a MEMS oscillator instead of a crystal. The 3232 part number includes some extra battery backed general purpose memory, which I thought might be useful but is still not used yet in my design. The new chip takes the same commands as the old one, so no code changes were needed.&lt;br /&gt;
&lt;br /&gt;
===The Memory===&lt;br /&gt;
Initially, I tried using a 128kb 24lc1025 EEprom as the main storage space, but quickly found it would run out of space in less than the 5 year design life, so I would need to do something else.&lt;br /&gt;
&lt;br /&gt;
While working on this project, I came across the Pearl cave project, where the same idea is used to make environmental sensors for monitoring caves. Their solution was to use an SD card, but it was found that different SD cards used different amounts of power, and that the cards did not like being turned on and off. Rather than an SD card, I thought about using a flash memory chip, maybe in the same way SPIFFS is used in my esp8266 projects. It would also be soldered to the board, with no spring connectors that could corrode or come loose. I settled on the Winbond 25q32fvsig, a 4mbyte flash chip. This would have plenty of room for years of measurements, and also has a sleep mode which takes the power down to a sub-microamp level. I used Marzogh's SPIMemory library for this, which takes care of most of the work in interfacing with the flash. When working with these chips, I found I could not write my 17 byte long measurement frame sequentially like I could with the EEPROM. Increasing the frame size to 32 bytes aligned them with the flash pages and I was able to read and write without errors, as well as fit a few extra measurements in. Now I can store 128k samples, instead of just 128k bytes. At one sampling per hour, this is almost 15 years of storage.&lt;br /&gt;
&lt;br /&gt;
On the board in my picture, you can spot my mistake in the board design. I accidentally connected the reset and write protect pins to GND instead of VCC. This means that the chip is in a constant reset state, and cannot be read or written. With a Dremel, I carefully cut the traces. The reset pin is right next to the VCC pin, so a little excess solder is enough to bridge them. A jumper of thin wire across the top of the chip brings the write protect pin positive as well. This error is fixed in the released version provided in Files. &lt;br /&gt;
&lt;br /&gt;
Initially, all I wanted to measure was temperature. However, that's not a lot of payout for 5 years work. Since the time capsule supposed to be a sealed PVC tube, I thought that it would be interesting to see what the internal pressure was like.  The BME280 can do both temperature and pressure, and also humidity. It also has a low power sleep mode, and communicates over i2c. Since I've never soldered a part that small, I cheated and used a premade breakout board module, which fits on to the i2c header behind the main header.  The header allows for more expansion also, in addition to or if another i2c sensor is to be used.&lt;br /&gt;
&lt;br /&gt;
===Power===&lt;br /&gt;
With the selection of a 3.3v memory chip and sensor, I was limited to running the board at 3.3v instead of the 5v I had originally planned. I didn't think that two AA batteries or a coin cell could last long enough and still be enough voltage to power the board after it lost some charge. Since I care more about reliability with no maintenance, and ton't have too much of a size constraint on the size of the batteries. I thought it would be better to run the components at some stable voltage that did not drop with the batteries (though I recognize that this will increase idle current draw). I could have run the 328 at 4.5v with 3 AAs, but I would still need an additional 3.3v supply and some kind of logic level  conversion. The pearl cave project had the same problem, and solved it by using a simple linear regulator. However, the regular has to be a low quiesent current type, which drew little current on it's own, unlike an lm1117 found on most of the cheap Arduino boards. It took me a while to find a version that had an input voltage greater than 5.5v (in case I decided to use more than 3 cells), and still had good performance. Both the Mcp1703 and HT7773 are good candidates, and come in a sot89 package with the same pinout. I happened to have some of the through hole HT regulators on hand, and used them while I waited for some eBay mcp1703s to arrive. Unfortunately, the eBay mcp1703s turned out to be counterfeit also, and did not meet the claimed quiescent current listed in the datasheet. The current was instead around 70 microamps, more than 10x what I had planned. Again, some genuine samples from Microchip fixed the problem. Interestingly, the HT chips which are produced by a Chinese company and were bought overseas from an unknown seller performed perfectly.&lt;br /&gt;
&lt;br /&gt;
===Operator interface===&lt;br /&gt;
Only seven IO pins on the 328 were being used thus far. Two for i2c, one for the clock wake up pin, and 4 for the SPI flash memory. I added an eighth, a voltage divider of high value resistors on an ADC pin to measure the battery voltage, which has not been fitted yet. But that still leaves lots of other pins open on the 328. I connected four of them to a small 4 position dip switch that I had on hand, three more to colored LEDs, and a button switch to the other interrupt pin. In this way, the board can still be verified working after disconnecting from the programmer. Otherwise, I can't tell if the board is working fine or a brick. The button enables waking up the 328 without waiting for the clock alarm to trip, which could take an hour on the slow setting. The dip switches allow for selecting different models, like setting the clock accurately to the second, or erasing the flash to start over, or dumping the flash contents over serial.&lt;br /&gt;
&lt;br /&gt;
I decided to include a serial interface header on one side, so it will be easier to get the data off of the board when the time comes. I'll use the Arduino bootloader so I can program the chip over serial, since I have many more usb serial adapters than AVR programmers, and the cheap AVR programmers I do have only work 5% of the time anyways. VCC, GND, Tx, Rx, and the 328 /RST pins are brought out on the 5 pin header.&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=WC-80&amp;diff=586</id>
		<title>WC-80</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=WC-80&amp;diff=586"/>
		<updated>2020-07-03T18:09:51Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: Created page with &amp;quot;&amp;lt;insert title and stuff here&amp;gt;  ==Breadboard Z80 - 2020/07/03== File:WC-80-v002.jpg.jpg|thumb|The breaboarded Z80 system connected to the Wilson Expansion interface and runni...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;insert title and stuff here&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Breadboard Z80 - 2020/07/03==&lt;br /&gt;
[[File:WC-80-v002.jpg.jpg|thumb|The breaboarded Z80 system connected to the Wilson Expansion interface and running ORCH85]]&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=File:WC-80-v002.jpg.jpg&amp;diff=585</id>
		<title>File:WC-80-v002.jpg.jpg</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=File:WC-80-v002.jpg.jpg&amp;diff=585"/>
		<updated>2020-07-03T18:09:16Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;A breadboarded Z80 computer attached to the Wilson TRS-80 expansion interface.&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=Main_Page&amp;diff=584</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=Main_Page&amp;diff=584"/>
		<updated>2020-07-03T18:06:44Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: /* TRS-80 Stuff */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''This site''' serves documentation for my various projects, mostly for my own reference, but also as a way to share them. Using a wiki for this purpose is certainly overkill, but it is easy to edit and format, and I am familiar with it. Whenever I do a fairly involved project, there's a decent chance I will write up the things I learned on this site. As it stands, this site is still relatively sparse, because I like working on projects more than writing about them, like most people.&lt;br /&gt;
&lt;br /&gt;
This web server and the content it provides is also a project in and of itself. This is running on server hardware and software that I set up myself, so as such even the content you are reading now is experimental. I guarantee no stability or reliable access, although I will do my best to keep this available online for as long as possible (though as of 2020 I have had a server like this running continuously for over 5 years).&lt;br /&gt;
&lt;br /&gt;
==Project List==&lt;br /&gt;
More major projects are listed below, in no particular order. See the list of [[Special:AllPages|all pages here.]]&lt;br /&gt;
&lt;br /&gt;
* [[AWOL Box]] - My take on the infamous Mikal Hart Reverse Geocache™ puzzle&lt;br /&gt;
* [[Nixie Clock 1]] - First completed Nixie clock/display unit&lt;br /&gt;
* [[Nixie Clock 2]] - Second Nixie display&lt;br /&gt;
* [[RC Wheelchair 1]] - Hacking the joystick of an electric wheelchair for remote control&lt;br /&gt;
* [[RC Wheelchair 2]] - A complete high-power h-bridge controller, controls, and radio system.&lt;br /&gt;
* [[Wilson X carriage]] - Improved x-carriage for the Prusa i3 style 3d printer.&lt;br /&gt;
* [[COW arm]] - a robot arm for the wheelchair robot base&lt;br /&gt;
* [[Lora module]] - module that makes using LoRa radio communications easier to work with.&lt;br /&gt;
* [[Environmental logger]] - A time capsule experiment&lt;br /&gt;
* [[Model T speedometer]] - General purpose GPS based speedometer for cars that don't have one. &lt;br /&gt;
* [[Geiger counter]] - my homemade Geiger counter using a SBM20 tube&lt;br /&gt;
* [[Raspberry Pi DVR]] - my main way of recording TV off the air&lt;br /&gt;
* [[Wilson Tetris]] - my version of a Tetris clone (in C)&lt;br /&gt;
* [[Door monitor]] - putting an unused security system to &amp;quot;good&amp;quot; use.&lt;br /&gt;
* [[uScreen]] - general purpose module with esp8266 and Nokia LCD&lt;br /&gt;
* [[6888 Transmitter]] - 6888 tube-based AM transmitter&lt;br /&gt;
* [[Sky Pointer]] - a thingy for pointing out objects in the sky.&lt;br /&gt;
* [[Atlas Gears]] - Replacement lathe/mill changegears&lt;br /&gt;
* [[Smart Response XE]] - Hacking of a school response system&lt;br /&gt;
* [[Floppy disk drive hacking]] - Poking around floppy drives with a logic analyzer&lt;br /&gt;
* [[Coronameter]] - Dedicated screen for pandemic panicking.&lt;br /&gt;
* [[Miscellaneous]] - short things that aren't long enough for their own page&lt;br /&gt;
* [[Unpublished Projects]]&lt;br /&gt;
&lt;br /&gt;
==TRS-80 Stuff==&lt;br /&gt;
* [[TRS-80 Model 1 - Character ROM]]&lt;br /&gt;
* [[TRS-80 Model 1 - Wilson Expansion Interface]] - an Expansion Interface (needed to connect floppy drives and other peripherals to the 1977 TRS-80 microcomputer) &lt;br /&gt;
* [[TRS-80 Plug 'n Power Controller]] - reverse engineering an extinct trs-80 accessory&lt;br /&gt;
* [[WC-80]] - A work in progress to build TRS-80 compatible Z80 computer&lt;br /&gt;
&lt;br /&gt;
==Other Services==&lt;br /&gt;
This server has hosted a number of different things since it's original incarnation in high school. Some of the old services are still up (but others are unlisted or taken down due to spam or security risks)&lt;br /&gt;
&lt;br /&gt;
* [http://alnwlsn.ax.lt/lists/ Public Lists] - experimental spam collection&lt;br /&gt;
* [http://alnwlsn.ax.lt/wintercamp/notifier/ Winter Camp Notifier Script] - service that emails you when new Winter Camp discussion takes place&lt;br /&gt;
* [http://alnwlsn.ax.lt/wicm/index.php/Main_Page WICM] - A copy of the Winter Camp encyclopedia translated into MediaWiki&lt;br /&gt;
&lt;br /&gt;
===What is the fish?===&lt;br /&gt;
The green fish is from a game I made when I first learned how to program. Over time, it turned into the logo that I use for my projects.&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=TRS-80_Model_1_-_Wilson_Expansion_Interface&amp;diff=583</id>
		<title>TRS-80 Model 1 - Wilson Expansion Interface</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=TRS-80_Model_1_-_Wilson_Expansion_Interface&amp;diff=583"/>
		<updated>2020-05-21T03:48:33Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: /* Hardware/Construction */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:Trs80ei2-proto.jpg|thumb|Completed prototype featuring extra RAM, SD and DD floppy controllers, RTC heartbeat, SD Card and shared 0x3000 RAM]]&lt;br /&gt;
The '''TRS-80 Model 1''' originally comprised just the keyboard unit (containing the cpu, video diver, keyboard, cassette reader circuitry and 16k or ram) and monitor. Later, Radio Shack introduced the '''Expansion Interface (EI)''', which was another unit that sat behind the keyboard and added up to 32K more ram, a floppy disk controller, and some extra logic to control a printer or other accessories. Other features could be added to the expansion interface, including a doubler that added extra capacity to floppy disks or an rs232 board.&lt;br /&gt;
&lt;br /&gt;
When I got my first TRS-80 in 2018, all I got was the keyboard. I had to make my own adapters to connect a standard NTSC monitor, cassette recorder, and I even rewound a transformer to provide the combination of different voltages that the TRS-80 needs for power. However, since I eventually wanted to add more memory and run some of the DOS operating systems; this meant I would have to come up with an Expansion Interface. Unfortunately, most of the original EIs online, even ones that could not be confirmed working, were priced higher than I originally bought the keyboard. The schematics for the EI are in the Radio Shack Service manuals, which are easily found online, and the EI is made using almost entirely using 74 series logic. Since I happened to have a large stock of 74 logic chips and some prototyping boards, I decided to make my own EI. Doing this would also allow the freedom to change the wiring around for experiments while not destroying a piece of irreplaceable vintage hardware.&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
[[File:Ei2-block-diagran.png|thumb|Block diagram of my EI so far]]&lt;br /&gt;
My EI contains:&lt;br /&gt;
&lt;br /&gt;
* Blinkenlights - Connected to address bus, data bus, and control signals.&lt;br /&gt;
* 32K RAM (using a 128K SRAM chip because that's what I happened to have; the rest is not yet used)&lt;br /&gt;
* INS1771N floppy disk controller for FM encoded disks (fully compatible with the original FD1771 in a real EI, can therefore read the nonstandard DAM records used by TRSDOS)&lt;br /&gt;
* WD2793 floppy controller for MFM encoded disks, set up like a Percom Doubler&amp;lt;ref&amp;gt;[http://www.trs-80.org/percom-doubler/ Percom Doubler]&amp;lt;/ref&amp;gt; but this particular chip also has good internal data separation too, saving a few chips.&lt;br /&gt;
* 1K dual port SRAM, placed at TRS-80 memory address 0x3000-0x33ff, which is unused address space in a normal Model 1 system. This ram is shared between the trs80 and the atmega microcontoller.&lt;br /&gt;
* ATMEGA1284p microcontroller, running the [https://github.com/MCUdude/MightyCore Mightycore] bootloader; this makes it compatible with and able to be programmed by Arduino software. &lt;br /&gt;
* SD card, connected to Arduino&lt;br /&gt;
&lt;br /&gt;
Things not covered yet are:&lt;br /&gt;
* Printer port&lt;br /&gt;
* TRS-80 RS232 port&lt;br /&gt;
* Screen printer port&lt;br /&gt;
* Extra cassette port / switch&lt;br /&gt;
&lt;br /&gt;
==Theory of operation / Features==&lt;br /&gt;
[[File:Trs80ei2-address-decoder.PNG|thumb|Simulation of prototype address decoder / glue logic]]&lt;br /&gt;
Most of my system works like a normal EI, at least as far as the floppy controllers and extra system memory goes. I mainly drew inspiration for the wiring from the TRS-80 technical manuals and schematics. In this version, some sections are taken verbatim from the original schematics, like the six chips used for the clock divider. I found a design for a double density disk adapter in an old copy of Northern Bytes&amp;lt;ref&amp;gt;Northern Bytes, Volume 7, Number 5[https://archive.org/details/Northern_Bytes_Volume_7_Number_5_1985_Alternate_Source_Information_Outlet_The_US]&amp;lt;/ref&amp;gt;. However, the address decoding and buffering logic is my own design, done with 74 series logic. Programmable logic might make things more compact, but there is a steep learning curve (I have not found anything like Arduino for programmable logic) and the PAL devices I have are no longer in manufacturing. Besides, traditional 74 series logic seems to fit better with the spirit of the project.&lt;br /&gt;
&lt;br /&gt;
I have not implemented any power supply for the board, just some filtering; because I needed a standard ATX power supply to drive floppy drives, I decided to use the +5, -5, and +12 volt supplies from that instead.&lt;br /&gt;
&lt;br /&gt;
===ATMEGA Section===&lt;br /&gt;
The special new features come from the combination of the dual port RAM and ATMEGA microcontroller. A few TRS-80 projects I have looked at place some extra memory at TRS-80 address 3000H (which is otherwise completely unused in a stock machine), for extra code or other features. I have placed a Dual port RAM at this location, which means the TRS-80 can read, write, and execute code at this section, adding an extra 1K of general purpose RAM to the system. However, this is dual port RAM, meaning that it can be accessed by two devices '''at the same time'''. I connected the other side of the dual port RAM to an ATMEGA1284P, which is a modern microcontroller which runs at 16MHz and has 16K ram, an impressive system in it's own right when compared to the TRS-80. The microcontroller has an attached SD card for tons of space for storage, and its own operating system, with the express goal of monitoring and managing the shared memory section. &lt;br /&gt;
&lt;br /&gt;
My additions to the TRS-80 are broken down into two programs:&lt;br /&gt;
*'''SH'''ared '''R'''am '''I'''nterface '''P'''rogram - a short z80 program that the TRS-80 runs from the shared dual port RAM&lt;br /&gt;
*'''A'''tmega '''C'''ontrol '''P'''rogram '''T'''erminal - the Arduino sketch that runs on the Atmega, and provides a whole host of functions to read, write and modify data and access hardware on the TRS-80 (via SHRIP).&lt;br /&gt;
&lt;br /&gt;
ACPT command-line functions include:&lt;br /&gt;
* Local (shared dual port RAM) memory functions, which directly change data on the shared RAM. Addresses for these commands range from 000-3ff.&lt;br /&gt;
** &amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt; - display entire contends of RAM (in hex editor-like grid)&lt;br /&gt;
** &amp;lt;code&amp;gt;lr haddress&amp;lt;/code&amp;gt; - reads single byte&lt;br /&gt;
** &amp;lt;code&amp;gt;ll file.bin hstartaddress&amp;lt;/code&amp;gt; - load a dump file to RAM, starting at the given address&lt;br /&gt;
** &amp;lt;code&amp;gt;ld file.bin hstartaddress hendaddress&amp;lt;/code&amp;gt; - dumps a section of memory to a file on the SD card&lt;br /&gt;
* TRS-80 memory functions, working through the monitor program on the shared memory. It must be running for these to work.&lt;br /&gt;
** &amp;lt;code&amp;gt;rs hstartaddress hendaddress&amp;lt;/code&amp;gt; - works like ls, but you can spec sections of Z80 memory instead of the whole thing.&lt;br /&gt;
** &amp;lt;code&amp;gt;lr / ll / ld&amp;lt;/code&amp;gt; - work like their local ram equivalents, but now the address range is the whole Z80 address space: 0000-ffff&lt;br /&gt;
** &amp;lt;code&amp;gt;j address&amp;lt;/code&amp;gt; - jump to code at ''address''&lt;br /&gt;
** &amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; - exit the monitor program and Return to normal TRS-80 functioning.&lt;br /&gt;
** &amp;lt;code&amp;gt;p&amp;lt;/code&amp;gt; - Pause the TRS-80 and run the monitor code.&lt;br /&gt;
** &amp;lt;code&amp;gt;rv file.bin hstartaddress&amp;lt;/code&amp;gt; - verify a file matches a section of RAM&lt;br /&gt;
* SD file commands, special for managing files on the SD card.&lt;br /&gt;
** &amp;lt;code&amp;gt;dir&amp;lt;/code&amp;gt; - shows list of files and their sizes&lt;br /&gt;
** &amp;lt;code&amp;gt;del file.bin&amp;lt;/code&amp;gt; - delete a file&lt;br /&gt;
* File send/receive. Using a special terminal program like teraterm, you can transfer files to and from the SD card without turning off the system and taking it out. There is no special protocol (like XMODEM); files are just sent as raw binary streams.&lt;br /&gt;
** &amp;lt;code&amp;gt;xs file.bin&amp;lt;/code&amp;gt; - send raw file over serial port. If you log these bytes, you can reconstruct the file. &lt;br /&gt;
** &amp;lt;code&amp;gt;xr file.bin&amp;lt;/code&amp;gt; - receive raw file over serial port. Using a terminal program with xon/xoff, send the binary file. &lt;br /&gt;
&lt;br /&gt;
This is all well and good, but SHRIP can only work when the Z80 cpu is running the code, stored inside the shared RAM. One way to get to it is to type SYSTEM ↵ / 12288 ↵ from BASIC to start executing code from address 3000H. But, it sure would be nice to be able to use the monitor program whenever I want, without typing in anything. If I am in DOS or inside a program, there's no way to make the Z80 jump to address 3000H without exiting it. To this end, I decided to modify the TRS-80 ROM. I had already placed an EPROM inside the TRS-80 case to update to the R/S ROM version. I replaced the routine at 002BH with a jump to 3000H. The replaced routine is the part that detects which key is pressed on the TRS-80 keyboard, meaning that it gets run all the time. We can place the rest of the 002BH code at the end of SHRIP, so it can get back to normal operation if we aren't using any SHRIP functions. &lt;br /&gt;
&lt;br /&gt;
Being in the middle of the keyboard routine, the monitor program also has the opportunity to return alternate data than what the keyboard is actually doing. The monitor program checks a byte of shared ram at address 3306H, and if it is nonzero, it will return that value as if the keyboard typed it. This means we can use the microcontroller to type things on the TRS-80 without ever touching the keyboard. I implemented two commands on the microcontroller command line interface to do this:&lt;br /&gt;
* &amp;lt;code&amp;gt;k sometext-to-type&amp;lt;/code&amp;gt; - everything after the &amp;quot;k &amp;quot; is typed on the TRS-80 keyboard.&lt;br /&gt;
**It also works with escape characters, like &amp;lt;code&amp;gt;\n&amp;lt;/code&amp;gt; for eNter and &amp;lt;code&amp;gt;\k&amp;lt;/code&amp;gt; for breaK, &amp;lt;code&amp;gt;\u \d \l \r&amp;lt;/code&amp;gt; for the direction keys, and &amp;lt;code&amp;gt;\c&amp;lt;/code&amp;gt; for the clear key.&lt;br /&gt;
* &amp;lt;code&amp;gt;ks file.txt&amp;lt;/code&amp;gt; - an entire file on the SD card is typed out over the keyboard. Escape characters aren't used here, it just sends the file as if it were a sequence of keyboard scan codes (most of the keyboard is ASCII).&lt;br /&gt;
&lt;br /&gt;
====Files====&lt;br /&gt;
Revision 6 - [[File:Trs80ei2-v6.zip]]&lt;br /&gt;
&lt;br /&gt;
Revision 7 - [[File:Trs80ei2-v7.zip]]&lt;br /&gt;
&lt;br /&gt;
==Hardware/Construction==&lt;br /&gt;
I constructed this on protoboard. Many of these types of projects use wire wrapping, but I don't have any wire wrapping sockets or any tools for doing this. Instead, I used the wire wrap-esque technique where wires jump to different points on the bottom of the board, and are soldered in place. This looks incredibly messy, and it is extremely easy to burn existing wires with the iron (if you look closely, you can see where I have fixed burnt wires with liquid electrical tape.) However, this technique does allow for a comparatively high chip density on the board, because none of the PCB area is used to carry signals. &lt;br /&gt;
&lt;br /&gt;
Most of my chips are socketed, as pretty much all of them are either very old and fragile, or new and very cheap and of questionable quality. The sockets make it very easy to fix bad chips, especially since the bottom of these sockets usually have a separate, free wire on most of the pins. &lt;br /&gt;
&lt;br /&gt;
On most of my chips, I have applied a label, making it easy to see the function on every pin. I make my labels using a thermal printer that was originally used to print price stickers in a store. The type of thermal labels used will turn black when they get hot. I have used this property more than once to detect a bad chip on my board (the chips often heat up when they go bad).&lt;br /&gt;
&lt;br /&gt;
The first board I started with was actually the light board, and the layout for that was decided based on how I could line up the components for less soldering. I made this board removable so I could install chips underneath. When it came time to make a floppy driver board and later a RS-232 board, I used the connector for the light board, since it had the whole Z80 bus exposed. The boards were stacked on top of each other with those stackable header things that Arduino shields use. In hindsight, this was a bad idea and I have had a lot of contact problems with the ones I used - there is an opportunity for a bad connection for each connector you add to the stack. Oh well, the whole thing took tens of hours to construct and now I'm stuck with it until I do a PCB.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
trs80ei2-base-bottom.jpg|base-bottom&lt;br /&gt;
Trs80ei2-base-top.jpg|base-top&lt;br /&gt;
Trs80ei2-doubler-bottom.jpg|doubler-bottom&lt;br /&gt;
Trs80ei2-doubler-top.jpg|doubler-top&lt;br /&gt;
Trs80ei2-floppy-bottom.jpg|floppy-bottom&lt;br /&gt;
Trs80ei2-floppy-top.jpg|floppy-top&lt;br /&gt;
Trs80ei2-lights-top.jpg|lights-top&lt;br /&gt;
Trs80ei2-lights-bottom.jpg|lights-bottom&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Log==&lt;br /&gt;
===Debugging a bad RAM chip - May 31, 2019===&lt;br /&gt;
While running the TRS-80 today, I noticed that I was not able to read files reliably under LDOS, but DoubleDos worked just as well as ever. I know that I had LDOS working when I first completed the expansion interface, so something was clearly wrong. Upon further investigation, I noticed that the errors only occurred when the double density driver (FDUBL) was loaded in LDOS. When the driver was loaded, I experienced complete instability when trying to read disks, and I could not format double density disks at all; the system would simply hang instead of stepping the disk drive. Initially, I thought there might be something wrong with my doubler board, after all, it does sit on top of the stack of boards and is slightly crooked. But, double density still worked fine under DoubleDos, so this was probably not the issue. &lt;br /&gt;
&lt;br /&gt;
My next thought was some kind of ram problem. I did notice that when loading data directly to ram with my Atmega monitor program, I noticed some verify errors at system address 0xb300. At first, I thought that this might be a stack pointer that was getting overwritten, but then I remembered that my monitor places it's stack within the shared 1k RAM. I tried directly writing to 0xb300 and could not get a consistent write, though the bytes before and after it worked fine. Could this verify error be the problem? This section of system memory would be within the 32K of SRAM provided by my expansion board. I am using a 128K chip here because that's what I had, but the chips I have are not credibly sourced, and I have had problems in this board with clone chips of 74 logic. &lt;br /&gt;
&lt;br /&gt;
I tried replacing the RAM and the problem did indeed go away, and I was finally able to fully load entire (48K) ram snapshots using my monitor program with no verify errors. At the same time, I found LDOS once again able to read and write disks reliably. So it seems that the ram was bad, although I find it strange that only a single byte was affected.&lt;br /&gt;
&lt;br /&gt;
===FreHD clone experiment - June 3, 2019===&lt;br /&gt;
Before I had completed the floppy controllers, I had began work on a frehd clone that would fit on my weird stacked EI, which I could never get to work properly. Developed by Frederic Vecoven, The [http://www.vecoven.com/trs80/trs80.html frehd project is an open source project] that emulates a hard drive in a manner consistent with the original mfm disk controllers you could get back in the day, though there is [http://members.iinet.net.au/~ianmav/trs80/emulator.htm now a closed source version assembled and sold] by Ian Mavric of Australia. After getting all my floppy drives working, having two floppy drives simulated with Goteks (more on this later), I really don't need a frehd too, but I had already made the board (following the schematic in the original frehd open source release), and so it was worth another try.&lt;br /&gt;
&lt;br /&gt;
My first experiment was to try a program called [http://48k.ca/trsvid.html TRSVID] (developed by George Phillips) which can display full motion video on the trs-80 using a frehd, although you need to change the firmware on the frehd PIC microcontroller first. Having constructed the entire board myself, I had a programmer too in order to program the PIC in the first place, and after loading the Phillips firmware, I too was able to watch grainy videos on my TRS-80, with equally crummy sound. Although the quality isn't great by modern standards, this is an extremely impressive demo considering the TRS-80's age. You only need to look at [[Wilson Tetris|my Tetris program]] to see the normal limitations of the TRS-80 graphics.&lt;br /&gt;
&lt;br /&gt;
However, I ran into trouble when trying to actually use my clone frehd for its intended purpose: as a hard drive. The freHD comes with some utilities to help mount and create hard disks, and &amp;lt;code&amp;gt;VHDUTL&amp;lt;/code&amp;gt; seems to work ok. However, trying to actually format the disks using &amp;lt;code&amp;gt;RSHARD1&amp;lt;/code&amp;gt; (using 840 tracks, 6 heads and 140 sectors), gave mixed results. Sometimes, the system would lock up after I finished filling out the prompts to mount the disk using &amp;lt;code&amp;gt;SYSTEM (DRIVE=4,DISABLE,DRIVER=&amp;quot;RSHARD1&amp;quot;)&amp;lt;/code&amp;gt; Other times, it would lock up after trying the &amp;lt;code&amp;gt;RSFORM1&amp;lt;/code&amp;gt; program which is supposed to format the disk. Most of the time though, these commands would work successfully, even verifying the format correctly. But, I found that afterwards, the system might lock up when trying to display the directory. &lt;br /&gt;
&lt;br /&gt;
In the event that all of the above setup works properly, I was still left with one final problem, which is that when I copy a file to the emulated hard drive, it doen't always seem to be a complete copy. I could copy my Tetris program and get it to load at least once or twice, but my other version (Metris, with more pieces) would not run no matter what I tried. Soon afterwards, my hard disk got corrupted and I would have to start all over.&lt;br /&gt;
&lt;br /&gt;
Incidentally, I also noticed that the &amp;lt;code&amp;gt;IMPORT2&amp;lt;/code&amp;gt; command which is used to transfer files from the freHD SD card to TRS-80 devices like floppy disks would also only make corrupt files. Perhaps if I examine the files, or make some test payloads, I may be able to determine what's going on, but that will need to wait for another day. I know that Ian made some changes the software, either to the frehd, trs-80 or both, to make the frehd work on a Model 1, but as of now I am not sure what those changes are. &lt;br /&gt;
&lt;br /&gt;
It may be better to continue my ATMEGA/dual port ram solution anyway, as this has the advantage of modifying any memory location.&lt;br /&gt;
&lt;br /&gt;
===Version 7 of software - June 19, 2019===&lt;br /&gt;
It seems like there is some kind of problem with the 32K ram on the board, most likely a bad connection or something, which is near impossible to find. I sometimes find that reseating the SRAM chip or swapping it for a diffrent one will usually work, even though all of my ram chips test good using my EPROM programmer as a tester. &lt;br /&gt;
&lt;br /&gt;
Anyways, I decided I needed to add a RAM test feature to the atmega operator interface. Before this, I created two 48k files of all 1s (0xFF) and all 0s (0x00), which I would load each with the '''rl''' command, which verifies each byte as it is written, allowing me to find RAM errors. Since this is a rather long command to type out, I decided to include a command &amp;lt;code&amp;gt;tstram&amp;lt;/code&amp;gt; that would write all 1s, then all 0s, for each ram address over the entire 48K ram. An optional number after the command will specify the number of loops to do, from once to a whole bunch.&lt;br /&gt;
&lt;br /&gt;
Additionally, I discovered a bug that would not allow the 3rd noun to be counted, making all commands think that there were only 2 nouns maximum (nouns=parameters after the command), making commands like &amp;lt;code&amp;gt;rd&amp;lt;/code&amp;gt; inoperable. As a workaround for v7, I increased the allowed number of nouns to 4, which means that we still have the same error, but now the parser thinks that we can't have more than 3 nouns, which is the most that any command has. I will fix this properly in the next releases.&lt;br /&gt;
&lt;br /&gt;
===v9 - Aug 2019===&lt;br /&gt;
Since the last log entry, I have indeed found the cause of the RAM issue - an address pin (one of the high address pins that isn't used) was left floating! This means that the ram would randomly switch to a different bank; this would be as if the ram randomly erases everything sometimes.&lt;br /&gt;
&lt;br /&gt;
The reason I discovered this was because I wired these extra address pins to some atmega pins, for an experiment. Now, through the Atmega, I can switch the 32k of system ram between 4 banks. &lt;br /&gt;
&lt;br /&gt;
I also am experimenting with an on-board terminal for the Atmega terminal control interface. From the little z80 program running in the dual port RAM, we already have access to the TRS-80 keyboard. To get access to the screen, we can call address 0x033a, which writes the character in A onto the screen, then advances the cursor. There are a few other routines &amp;lt;ref&amp;gt;http://www.trs-80.org/trs-80-rom-routines-documented/&amp;lt;/ref&amp;gt; that can help manage the screen, so that makes things like advancing the cursor and scrolling down something that I don't need to implement myself in my own program. I added calls to these routines in SHRIP, meaning that SHRIP-ACPT can now write stuff directly to the screen. I even added extra functions to the ACPT sketch, to make the Arduino stream functions like Serial.print also write to the TRS-80 screen.&lt;br /&gt;
&lt;br /&gt;
Ok, so we now have access to the TRS-80 screen and keyboard. The last step is to make ACPT available on the TRS-80 directly, without needing to use an extra serial terminal on the Atmega. I added a keyboard shortcut to SHRIP, which will enter into an on-screen ACP terminal. All I need to do is first copy the contents of the screen and save the cursor position so I can get back exactly where I left off. I store the image data in one of the extra banks of SRAM, since it is literally wasted space until now. When I'm done with the terminal, I issue the exit command, and the screen data is copied back onto the screen.&lt;br /&gt;
&lt;br /&gt;
This means it is now possible to load machine code directly from the Atmega SD card into trs-80 memory, and execute it, without ever hooking up the trsei2 board to another computer. You can't really load full ram images yet, because the onscreen terminal itself uses some of the system RAM, but overall, the system is much more accessible now.&lt;br /&gt;
&lt;br /&gt;
===v12 - December 2019===&lt;br /&gt;
With 128K of SRAM on the expansion board, it would really be nice if that extra ram could be accessed from the Z80 side of things, so that I don't have to go through the Atmega. This should also speed up assembly programs that use the extra RAM. &lt;br /&gt;
In the memory mapped IO section that breaks out address 37E0-37EC, I used 74LS138 octal decoders, so I also have lines for 37F0-37FC that are not used in a standard TRS-80. Therefore, I connected a 74LS175 latch to data pins D0-D1, and latch on writes to memory address 37FC. This makes the extra RAM (4 blocks of 32K, placed in the upper half of Z80 memory map) accessible from the Z80 directly.&lt;br /&gt;
&lt;br /&gt;
Hardware also has a parallel port added, which is nothing more than a couple buffers and latches. I needed this to make the winter camp latrine monitor program, since I discovered that it needs a printer to work properly.&lt;br /&gt;
&lt;br /&gt;
===RS-232 board - 3-24-2020===&lt;br /&gt;
The real expansion interfaces had an option for a RS-232 serial port, which enabled communication to the outside world; most modems required an RS232 port. Since I want to hook this up to a raspberry pi eventually, I'll need one.&lt;br /&gt;
&lt;br /&gt;
The original board used a tr1602 UART and BR1941 as the baud rate generator. I couldn't find either of these chips, but I did find a TR1865 and COM8136, which are chips used in the Model 4, and are compatible. They also do not require a -5 or 12V supply, not that it's a big deal as I have an ATX supply as the power source. There's also a few supporting 74 series components, and some buffers to convert the 5V signals to the RS-232 voltages. In place of the buffers, I will try to use MAX232s instead, since I have a bunch of them for some reason. &lt;br /&gt;
&lt;br /&gt;
For address decoding, I am trying a GAL22v10. Normally I don't like using GAL programmable logic, because these chips are no longer made (there's still a lot though), and they sort of obscure how everything works. In this case, I found myself short of 74 logic chips in the middle of a global pandemic, so why not try something else and replace 4-5 74 chips instead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
edit 4/12/2020 - It works! I implemented everything in a real Model I RS232 board, except:&lt;br /&gt;
* I didn't implement the DIP switches. This is just some dip switches hooked to an IO port input. You can set everything from software. Model 3 removed these.&lt;br /&gt;
** This left me with an extra unused output pin on the GAL22V10. I found some TIL-311 hexadecimal LED displays / decoders, so I wired them up to IO port 0xEC. Now, I can have a debug display, simply OUT (0ech),a to write to the display.&lt;br /&gt;
* Did not implement the original TTL-RS232 voltage converters (I didn't have any of these ICs) What I did have was a bunch of MAX232 converter ICs, which do the same thing but have a capacitor charge pump to get the RS232 voltage. Also, per chip, they have just 2 TTL--&amp;gt;232 and 2 232--&amp;gt;TTL paths. So, I implemented tx/rx, rts/cts, dtr/dsr and DCD. RI is not connected to any 232 pin, and the TRS-80 thinks it is always OFF. &lt;br /&gt;
&lt;br /&gt;
I wrote my own terminal emulator after finding the TRS-80 offerings lacking. See [[TRS-80 - WTERM]].&lt;br /&gt;
&lt;br /&gt;
===Orchestra 80/85 - April-May 2020===&lt;br /&gt;
The next thing I wanted to try out was ways to produce sounds and music on the TRS-80. Back in the day, the king of TRS-80 music was apparently a hardware add on called the Orchestra-80. According to trs-80.org, this added &amp;quot;four simultaneous voices over a six octave range.&amp;quot; However, the sound did not come from a hardware sound chip, like the specialized ones Commodore or Atari had. Instead, the audio hardware was basically just a latch and some resistors, wired into an IO port. It's an R-2R DAC - basically the same thing as the much later Covox Speech Thing or Disney Sound Source for PCs. The real genius of the Orchestra-80 system was the excellent software, which synthesizes all the audio on the fly, and spits it out the IO port. Plus, the people who made it actually knew something about music theory, so it is possible to properly write music in it. Later, the Orchestra-80 was superseded by the Orchestra-85 and 90, which use slightly different hardware but worked with the model 3 and 4 too, and add stereo sound.&lt;br /&gt;
&lt;br /&gt;
I found very little information on the technical details - anyone who has the boards seem unwilling to share the chips that are on them, and there were no schematics in the original manuals. However, we have emulators and the original software.&lt;br /&gt;
&lt;br /&gt;
I started with the Orchestra-80. This writes data to just 1 IO port - 0xBD. Audio is unsigned 8-bit PCM, so it can be hooked up directly to the resistors. I used my extra GAL pin from the RS-232 board that is hooked to the LED hex displays, and redefined it to act on writes port 0xBD instead. From there, it's just a 74x374 latch, and some 10K and 20K resistors. I followed a schematic for a Covox. You can also look up R-2R DAC and get the same thing. D7 is the most significant bit. Later, I added a low pass filter - the sampling frequency is about 4Khz, and the resistors aren't perfect, so the audio can be quite noisy in the high frequencies.&lt;br /&gt;
&lt;br /&gt;
So that's the 80 done. But later, I found out that most of the song files out there were written for the Orchestra-85/90, (85 is the Model 1 version) which has different hardware. First of all, it uses 2 IO ports (0xD5 and 0xD9) for 2 DACs and 2 stereo channels. Also, there is something different about the way the audio PCM is formatted - it's not PCM. If you take a dump of the data written to the ports and dump it into Audacity, you will find it sounds correct when imported as signed 8 bit PCM. But for signed, you need to take the 2's complement - invert the MSB and subtract 1. But, I found evidence to support the Orchestra-85/90 was still using R-2R resistors. How is the hardware doing the subtraction?&lt;br /&gt;
&lt;br /&gt;
Before trying the hardware, I did the much more difficult thing of disassembling the Orchestra-85 software, and patched it to do the D7 inversion, average the result, and spit it out port 0xBD - my existing Orchestra-80 DAC. This did work, but the averaged audio was of even less quality (we are throwing away 1 bit), and the additional CPU overhead also slowed down the audio.&lt;br /&gt;
&lt;br /&gt;
Back to hardware - I did some research, but was unable to find any DAC IC that took an 8-channel signed 2s complement inputs. It turns out that the audio data is probably not unsigned after all; it's probably &amp;quot;offset binary&amp;quot; which is the same process as 2's complement but you don't subtract 1, you just invert the MSB. That's easy.&lt;br /&gt;
&lt;br /&gt;
Since I didn't feel like wiring up a bunch of resistors, I looked for a parallel DAC chip instead, which should be more accurate anyway. I settled on the AD7302, which is a 2 channel, 8 bit DAC. After a sample order from Analog Devices, I had one. Basically, the whole ADC is now on this one chip instead of a bunch of resistor packs and latches. For address decoding, I decided to use a GAL16v8 chip, since I had good luck using another one on the RS232 build. The GAL can also do the needed inversion of D7. So, this boils down the entire Orchestra-85 down to 2 chips, and 2 RC low pass filters. The solution fits on a tiny proto-pcb, neatly between the floppy controller and top boards. Because of the GAL, I also can decode the Orchestra-80 port, and not invert D7 for that one.&lt;br /&gt;
&lt;br /&gt;
So, how does it sound? Actually, pretty good! Much better than anything you would expect is possible on a 1978 machine. It does use 100% of the CPU's processing power though, which is completely expected.&lt;br /&gt;
&lt;br /&gt;
Another experiment I did was to fill the TRS-80's 48K of RAM with 8-bit PCM samples, then write a short program in my secret 0x3000 ram area to dump it out through the Orchestra-80 port. Realistically, 49152 samples only holds a couple samples of audio at a low bitrate, but it's enough for short sound effects, or a 5 second loop of a song that repeats. This is true digital audio on the TRS-80!&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=TRS-80_Model_1_-_Wilson_Expansion_Interface&amp;diff=582</id>
		<title>TRS-80 Model 1 - Wilson Expansion Interface</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=TRS-80_Model_1_-_Wilson_Expansion_Interface&amp;diff=582"/>
		<updated>2020-05-21T03:36:21Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: /* Orchestra 80/85 - April-May 2020 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:Trs80ei2-proto.jpg|thumb|Completed prototype featuring extra RAM, SD and DD floppy controllers, RTC heartbeat, SD Card and shared 0x3000 RAM]]&lt;br /&gt;
The '''TRS-80 Model 1''' originally comprised just the keyboard unit (containing the cpu, video diver, keyboard, cassette reader circuitry and 16k or ram) and monitor. Later, Radio Shack introduced the '''Expansion Interface (EI)''', which was another unit that sat behind the keyboard and added up to 32K more ram, a floppy disk controller, and some extra logic to control a printer or other accessories. Other features could be added to the expansion interface, including a doubler that added extra capacity to floppy disks or an rs232 board.&lt;br /&gt;
&lt;br /&gt;
When I got my first TRS-80 in 2018, all I got was the keyboard. I had to make my own adapters to connect a standard NTSC monitor, cassette recorder, and I even rewound a transformer to provide the combination of different voltages that the TRS-80 needs for power. However, since I eventually wanted to add more memory and run some of the DOS operating systems; this meant I would have to come up with an Expansion Interface. Unfortunately, most of the original EIs online, even ones that could not be confirmed working, were priced higher than I originally bought the keyboard. The schematics for the EI are in the Radio Shack Service manuals, which are easily found online, and the EI is made using almost entirely using 74 series logic. Since I happened to have a large stock of 74 logic chips and some prototyping boards, I decided to make my own EI. Doing this would also allow the freedom to change the wiring around for experiments while not destroying a piece of irreplaceable vintage hardware.&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
[[File:Ei2-block-diagran.png|thumb|Block diagram of my EI so far]]&lt;br /&gt;
My EI contains:&lt;br /&gt;
&lt;br /&gt;
* Blinkenlights - Connected to address bus, data bus, and control signals.&lt;br /&gt;
* 32K RAM (using a 128K SRAM chip because that's what I happened to have; the rest is not yet used)&lt;br /&gt;
* INS1771N floppy disk controller for FM encoded disks (fully compatible with the original FD1771 in a real EI, can therefore read the nonstandard DAM records used by TRSDOS)&lt;br /&gt;
* WD2793 floppy controller for MFM encoded disks, set up like a Percom Doubler&amp;lt;ref&amp;gt;[http://www.trs-80.org/percom-doubler/ Percom Doubler]&amp;lt;/ref&amp;gt; but this particular chip also has good internal data separation too, saving a few chips.&lt;br /&gt;
* 1K dual port SRAM, placed at TRS-80 memory address 0x3000-0x33ff, which is unused address space in a normal Model 1 system. This ram is shared between the trs80 and the atmega microcontoller.&lt;br /&gt;
* ATMEGA1284p microcontroller, running the [https://github.com/MCUdude/MightyCore Mightycore] bootloader; this makes it compatible with and able to be programmed by Arduino software. &lt;br /&gt;
* SD card, connected to Arduino&lt;br /&gt;
&lt;br /&gt;
Things not covered yet are:&lt;br /&gt;
* Printer port&lt;br /&gt;
* TRS-80 RS232 port&lt;br /&gt;
* Screen printer port&lt;br /&gt;
* Extra cassette port / switch&lt;br /&gt;
&lt;br /&gt;
==Theory of operation / Features==&lt;br /&gt;
[[File:Trs80ei2-address-decoder.PNG|thumb|Simulation of prototype address decoder / glue logic]]&lt;br /&gt;
Most of my system works like a normal EI, at least as far as the floppy controllers and extra system memory goes. I mainly drew inspiration for the wiring from the TRS-80 technical manuals and schematics. In this version, some sections are taken verbatim from the original schematics, like the six chips used for the clock divider. I found a design for a double density disk adapter in an old copy of Northern Bytes&amp;lt;ref&amp;gt;Northern Bytes, Volume 7, Number 5[https://archive.org/details/Northern_Bytes_Volume_7_Number_5_1985_Alternate_Source_Information_Outlet_The_US]&amp;lt;/ref&amp;gt;. However, the address decoding and buffering logic is my own design, done with 74 series logic. Programmable logic might make things more compact, but there is a steep learning curve (I have not found anything like Arduino for programmable logic) and the PAL devices I have are no longer in manufacturing. Besides, traditional 74 series logic seems to fit better with the spirit of the project.&lt;br /&gt;
&lt;br /&gt;
I have not implemented any power supply for the board, just some filtering; because I needed a standard ATX power supply to drive floppy drives, I decided to use the +5, -5, and +12 volt supplies from that instead.&lt;br /&gt;
&lt;br /&gt;
===ATMEGA Section===&lt;br /&gt;
The special new features come from the combination of the dual port RAM and ATMEGA microcontroller. A few TRS-80 projects I have looked at place some extra memory at TRS-80 address 3000H (which is otherwise completely unused in a stock machine), for extra code or other features. I have placed a Dual port RAM at this location, which means the TRS-80 can read, write, and execute code at this section, adding an extra 1K of general purpose RAM to the system. However, this is dual port RAM, meaning that it can be accessed by two devices '''at the same time'''. I connected the other side of the dual port RAM to an ATMEGA1284P, which is a modern microcontroller which runs at 16MHz and has 16K ram, an impressive system in it's own right when compared to the TRS-80. The microcontroller has an attached SD card for tons of space for storage, and its own operating system, with the express goal of monitoring and managing the shared memory section. &lt;br /&gt;
&lt;br /&gt;
My additions to the TRS-80 are broken down into two programs:&lt;br /&gt;
*'''SH'''ared '''R'''am '''I'''nterface '''P'''rogram - a short z80 program that the TRS-80 runs from the shared dual port RAM&lt;br /&gt;
*'''A'''tmega '''C'''ontrol '''P'''rogram '''T'''erminal - the Arduino sketch that runs on the Atmega, and provides a whole host of functions to read, write and modify data and access hardware on the TRS-80 (via SHRIP).&lt;br /&gt;
&lt;br /&gt;
ACPT command-line functions include:&lt;br /&gt;
* Local (shared dual port RAM) memory functions, which directly change data on the shared RAM. Addresses for these commands range from 000-3ff.&lt;br /&gt;
** &amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt; - display entire contends of RAM (in hex editor-like grid)&lt;br /&gt;
** &amp;lt;code&amp;gt;lr haddress&amp;lt;/code&amp;gt; - reads single byte&lt;br /&gt;
** &amp;lt;code&amp;gt;ll file.bin hstartaddress&amp;lt;/code&amp;gt; - load a dump file to RAM, starting at the given address&lt;br /&gt;
** &amp;lt;code&amp;gt;ld file.bin hstartaddress hendaddress&amp;lt;/code&amp;gt; - dumps a section of memory to a file on the SD card&lt;br /&gt;
* TRS-80 memory functions, working through the monitor program on the shared memory. It must be running for these to work.&lt;br /&gt;
** &amp;lt;code&amp;gt;rs hstartaddress hendaddress&amp;lt;/code&amp;gt; - works like ls, but you can spec sections of Z80 memory instead of the whole thing.&lt;br /&gt;
** &amp;lt;code&amp;gt;lr / ll / ld&amp;lt;/code&amp;gt; - work like their local ram equivalents, but now the address range is the whole Z80 address space: 0000-ffff&lt;br /&gt;
** &amp;lt;code&amp;gt;j address&amp;lt;/code&amp;gt; - jump to code at ''address''&lt;br /&gt;
** &amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; - exit the monitor program and Return to normal TRS-80 functioning.&lt;br /&gt;
** &amp;lt;code&amp;gt;p&amp;lt;/code&amp;gt; - Pause the TRS-80 and run the monitor code.&lt;br /&gt;
** &amp;lt;code&amp;gt;rv file.bin hstartaddress&amp;lt;/code&amp;gt; - verify a file matches a section of RAM&lt;br /&gt;
* SD file commands, special for managing files on the SD card.&lt;br /&gt;
** &amp;lt;code&amp;gt;dir&amp;lt;/code&amp;gt; - shows list of files and their sizes&lt;br /&gt;
** &amp;lt;code&amp;gt;del file.bin&amp;lt;/code&amp;gt; - delete a file&lt;br /&gt;
* File send/receive. Using a special terminal program like teraterm, you can transfer files to and from the SD card without turning off the system and taking it out. There is no special protocol (like XMODEM); files are just sent as raw binary streams.&lt;br /&gt;
** &amp;lt;code&amp;gt;xs file.bin&amp;lt;/code&amp;gt; - send raw file over serial port. If you log these bytes, you can reconstruct the file. &lt;br /&gt;
** &amp;lt;code&amp;gt;xr file.bin&amp;lt;/code&amp;gt; - receive raw file over serial port. Using a terminal program with xon/xoff, send the binary file. &lt;br /&gt;
&lt;br /&gt;
This is all well and good, but SHRIP can only work when the Z80 cpu is running the code, stored inside the shared RAM. One way to get to it is to type SYSTEM ↵ / 12288 ↵ from BASIC to start executing code from address 3000H. But, it sure would be nice to be able to use the monitor program whenever I want, without typing in anything. If I am in DOS or inside a program, there's no way to make the Z80 jump to address 3000H without exiting it. To this end, I decided to modify the TRS-80 ROM. I had already placed an EPROM inside the TRS-80 case to update to the R/S ROM version. I replaced the routine at 002BH with a jump to 3000H. The replaced routine is the part that detects which key is pressed on the TRS-80 keyboard, meaning that it gets run all the time. We can place the rest of the 002BH code at the end of SHRIP, so it can get back to normal operation if we aren't using any SHRIP functions. &lt;br /&gt;
&lt;br /&gt;
Being in the middle of the keyboard routine, the monitor program also has the opportunity to return alternate data than what the keyboard is actually doing. The monitor program checks a byte of shared ram at address 3306H, and if it is nonzero, it will return that value as if the keyboard typed it. This means we can use the microcontroller to type things on the TRS-80 without ever touching the keyboard. I implemented two commands on the microcontroller command line interface to do this:&lt;br /&gt;
* &amp;lt;code&amp;gt;k sometext-to-type&amp;lt;/code&amp;gt; - everything after the &amp;quot;k &amp;quot; is typed on the TRS-80 keyboard.&lt;br /&gt;
**It also works with escape characters, like &amp;lt;code&amp;gt;\n&amp;lt;/code&amp;gt; for eNter and &amp;lt;code&amp;gt;\k&amp;lt;/code&amp;gt; for breaK, &amp;lt;code&amp;gt;\u \d \l \r&amp;lt;/code&amp;gt; for the direction keys, and &amp;lt;code&amp;gt;\c&amp;lt;/code&amp;gt; for the clear key.&lt;br /&gt;
* &amp;lt;code&amp;gt;ks file.txt&amp;lt;/code&amp;gt; - an entire file on the SD card is typed out over the keyboard. Escape characters aren't used here, it just sends the file as if it were a sequence of keyboard scan codes (most of the keyboard is ASCII).&lt;br /&gt;
&lt;br /&gt;
====Files====&lt;br /&gt;
Revision 6 - [[File:Trs80ei2-v6.zip]]&lt;br /&gt;
&lt;br /&gt;
Revision 7 - [[File:Trs80ei2-v7.zip]]&lt;br /&gt;
&lt;br /&gt;
==Hardware/Construction==&lt;br /&gt;
I constructed this on protoboard. Many of these types of projects use wire wrapping, but I don't have any wire wrapping sockets or any tools for doing this. Instead, I used the wire wrap-esque technique where wires jump to different points on the bottom of the board, and are soldered in place. This looks incredibly messy, and it is extremely easy to burn existing wires with the iron (if you look closely, you can see where I have fixed burnt wires with liquid electrical tape.) However, this technique does allow for a comparatively high chip density on the board, because none of the PCB area is used to carry signals. &lt;br /&gt;
&lt;br /&gt;
Most of my chips are socketed, as pretty much all of them are either very old and fragile, or new and very cheap and of questionable quality. The sockets make it very easy to fix bad chips, especially since the bottom of these sockets usually have a separate, free wire on most of the pins. &lt;br /&gt;
&lt;br /&gt;
On most of my chips, I have applied a label, making it easy to see the function on every pin. I make my labels using a thermal printer that was originally used to print price stickers in a store. The type of thermal labels used will turn black when they get hot. I have used this property more than once to detect a bad chip on my board (the chips often heat up when they go bad).&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
trs80ei2-base-bottom.jpg|base-bottom&lt;br /&gt;
Trs80ei2-base-top.jpg|base-top&lt;br /&gt;
Trs80ei2-doubler-bottom.jpg|doubler-bottom&lt;br /&gt;
Trs80ei2-doubler-top.jpg|doubler-top&lt;br /&gt;
Trs80ei2-floppy-bottom.jpg|floppy-bottom&lt;br /&gt;
Trs80ei2-floppy-top.jpg|floppy-top&lt;br /&gt;
Trs80ei2-lights-top.jpg|lights-top&lt;br /&gt;
Trs80ei2-lights-bottom.jpg|lights-bottom&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Log==&lt;br /&gt;
===Debugging a bad RAM chip - May 31, 2019===&lt;br /&gt;
While running the TRS-80 today, I noticed that I was not able to read files reliably under LDOS, but DoubleDos worked just as well as ever. I know that I had LDOS working when I first completed the expansion interface, so something was clearly wrong. Upon further investigation, I noticed that the errors only occurred when the double density driver (FDUBL) was loaded in LDOS. When the driver was loaded, I experienced complete instability when trying to read disks, and I could not format double density disks at all; the system would simply hang instead of stepping the disk drive. Initially, I thought there might be something wrong with my doubler board, after all, it does sit on top of the stack of boards and is slightly crooked. But, double density still worked fine under DoubleDos, so this was probably not the issue. &lt;br /&gt;
&lt;br /&gt;
My next thought was some kind of ram problem. I did notice that when loading data directly to ram with my Atmega monitor program, I noticed some verify errors at system address 0xb300. At first, I thought that this might be a stack pointer that was getting overwritten, but then I remembered that my monitor places it's stack within the shared 1k RAM. I tried directly writing to 0xb300 and could not get a consistent write, though the bytes before and after it worked fine. Could this verify error be the problem? This section of system memory would be within the 32K of SRAM provided by my expansion board. I am using a 128K chip here because that's what I had, but the chips I have are not credibly sourced, and I have had problems in this board with clone chips of 74 logic. &lt;br /&gt;
&lt;br /&gt;
I tried replacing the RAM and the problem did indeed go away, and I was finally able to fully load entire (48K) ram snapshots using my monitor program with no verify errors. At the same time, I found LDOS once again able to read and write disks reliably. So it seems that the ram was bad, although I find it strange that only a single byte was affected.&lt;br /&gt;
&lt;br /&gt;
===FreHD clone experiment - June 3, 2019===&lt;br /&gt;
Before I had completed the floppy controllers, I had began work on a frehd clone that would fit on my weird stacked EI, which I could never get to work properly. Developed by Frederic Vecoven, The [http://www.vecoven.com/trs80/trs80.html frehd project is an open source project] that emulates a hard drive in a manner consistent with the original mfm disk controllers you could get back in the day, though there is [http://members.iinet.net.au/~ianmav/trs80/emulator.htm now a closed source version assembled and sold] by Ian Mavric of Australia. After getting all my floppy drives working, having two floppy drives simulated with Goteks (more on this later), I really don't need a frehd too, but I had already made the board (following the schematic in the original frehd open source release), and so it was worth another try.&lt;br /&gt;
&lt;br /&gt;
My first experiment was to try a program called [http://48k.ca/trsvid.html TRSVID] (developed by George Phillips) which can display full motion video on the trs-80 using a frehd, although you need to change the firmware on the frehd PIC microcontroller first. Having constructed the entire board myself, I had a programmer too in order to program the PIC in the first place, and after loading the Phillips firmware, I too was able to watch grainy videos on my TRS-80, with equally crummy sound. Although the quality isn't great by modern standards, this is an extremely impressive demo considering the TRS-80's age. You only need to look at [[Wilson Tetris|my Tetris program]] to see the normal limitations of the TRS-80 graphics.&lt;br /&gt;
&lt;br /&gt;
However, I ran into trouble when trying to actually use my clone frehd for its intended purpose: as a hard drive. The freHD comes with some utilities to help mount and create hard disks, and &amp;lt;code&amp;gt;VHDUTL&amp;lt;/code&amp;gt; seems to work ok. However, trying to actually format the disks using &amp;lt;code&amp;gt;RSHARD1&amp;lt;/code&amp;gt; (using 840 tracks, 6 heads and 140 sectors), gave mixed results. Sometimes, the system would lock up after I finished filling out the prompts to mount the disk using &amp;lt;code&amp;gt;SYSTEM (DRIVE=4,DISABLE,DRIVER=&amp;quot;RSHARD1&amp;quot;)&amp;lt;/code&amp;gt; Other times, it would lock up after trying the &amp;lt;code&amp;gt;RSFORM1&amp;lt;/code&amp;gt; program which is supposed to format the disk. Most of the time though, these commands would work successfully, even verifying the format correctly. But, I found that afterwards, the system might lock up when trying to display the directory. &lt;br /&gt;
&lt;br /&gt;
In the event that all of the above setup works properly, I was still left with one final problem, which is that when I copy a file to the emulated hard drive, it doen't always seem to be a complete copy. I could copy my Tetris program and get it to load at least once or twice, but my other version (Metris, with more pieces) would not run no matter what I tried. Soon afterwards, my hard disk got corrupted and I would have to start all over.&lt;br /&gt;
&lt;br /&gt;
Incidentally, I also noticed that the &amp;lt;code&amp;gt;IMPORT2&amp;lt;/code&amp;gt; command which is used to transfer files from the freHD SD card to TRS-80 devices like floppy disks would also only make corrupt files. Perhaps if I examine the files, or make some test payloads, I may be able to determine what's going on, but that will need to wait for another day. I know that Ian made some changes the software, either to the frehd, trs-80 or both, to make the frehd work on a Model 1, but as of now I am not sure what those changes are. &lt;br /&gt;
&lt;br /&gt;
It may be better to continue my ATMEGA/dual port ram solution anyway, as this has the advantage of modifying any memory location.&lt;br /&gt;
&lt;br /&gt;
===Version 7 of software - June 19, 2019===&lt;br /&gt;
It seems like there is some kind of problem with the 32K ram on the board, most likely a bad connection or something, which is near impossible to find. I sometimes find that reseating the SRAM chip or swapping it for a diffrent one will usually work, even though all of my ram chips test good using my EPROM programmer as a tester. &lt;br /&gt;
&lt;br /&gt;
Anyways, I decided I needed to add a RAM test feature to the atmega operator interface. Before this, I created two 48k files of all 1s (0xFF) and all 0s (0x00), which I would load each with the '''rl''' command, which verifies each byte as it is written, allowing me to find RAM errors. Since this is a rather long command to type out, I decided to include a command &amp;lt;code&amp;gt;tstram&amp;lt;/code&amp;gt; that would write all 1s, then all 0s, for each ram address over the entire 48K ram. An optional number after the command will specify the number of loops to do, from once to a whole bunch.&lt;br /&gt;
&lt;br /&gt;
Additionally, I discovered a bug that would not allow the 3rd noun to be counted, making all commands think that there were only 2 nouns maximum (nouns=parameters after the command), making commands like &amp;lt;code&amp;gt;rd&amp;lt;/code&amp;gt; inoperable. As a workaround for v7, I increased the allowed number of nouns to 4, which means that we still have the same error, but now the parser thinks that we can't have more than 3 nouns, which is the most that any command has. I will fix this properly in the next releases.&lt;br /&gt;
&lt;br /&gt;
===v9 - Aug 2019===&lt;br /&gt;
Since the last log entry, I have indeed found the cause of the RAM issue - an address pin (one of the high address pins that isn't used) was left floating! This means that the ram would randomly switch to a different bank; this would be as if the ram randomly erases everything sometimes.&lt;br /&gt;
&lt;br /&gt;
The reason I discovered this was because I wired these extra address pins to some atmega pins, for an experiment. Now, through the Atmega, I can switch the 32k of system ram between 4 banks. &lt;br /&gt;
&lt;br /&gt;
I also am experimenting with an on-board terminal for the Atmega terminal control interface. From the little z80 program running in the dual port RAM, we already have access to the TRS-80 keyboard. To get access to the screen, we can call address 0x033a, which writes the character in A onto the screen, then advances the cursor. There are a few other routines &amp;lt;ref&amp;gt;http://www.trs-80.org/trs-80-rom-routines-documented/&amp;lt;/ref&amp;gt; that can help manage the screen, so that makes things like advancing the cursor and scrolling down something that I don't need to implement myself in my own program. I added calls to these routines in SHRIP, meaning that SHRIP-ACPT can now write stuff directly to the screen. I even added extra functions to the ACPT sketch, to make the Arduino stream functions like Serial.print also write to the TRS-80 screen.&lt;br /&gt;
&lt;br /&gt;
Ok, so we now have access to the TRS-80 screen and keyboard. The last step is to make ACPT available on the TRS-80 directly, without needing to use an extra serial terminal on the Atmega. I added a keyboard shortcut to SHRIP, which will enter into an on-screen ACP terminal. All I need to do is first copy the contents of the screen and save the cursor position so I can get back exactly where I left off. I store the image data in one of the extra banks of SRAM, since it is literally wasted space until now. When I'm done with the terminal, I issue the exit command, and the screen data is copied back onto the screen.&lt;br /&gt;
&lt;br /&gt;
This means it is now possible to load machine code directly from the Atmega SD card into trs-80 memory, and execute it, without ever hooking up the trsei2 board to another computer. You can't really load full ram images yet, because the onscreen terminal itself uses some of the system RAM, but overall, the system is much more accessible now.&lt;br /&gt;
&lt;br /&gt;
===v12 - December 2019===&lt;br /&gt;
With 128K of SRAM on the expansion board, it would really be nice if that extra ram could be accessed from the Z80 side of things, so that I don't have to go through the Atmega. This should also speed up assembly programs that use the extra RAM. &lt;br /&gt;
In the memory mapped IO section that breaks out address 37E0-37EC, I used 74LS138 octal decoders, so I also have lines for 37F0-37FC that are not used in a standard TRS-80. Therefore, I connected a 74LS175 latch to data pins D0-D1, and latch on writes to memory address 37FC. This makes the extra RAM (4 blocks of 32K, placed in the upper half of Z80 memory map) accessible from the Z80 directly.&lt;br /&gt;
&lt;br /&gt;
Hardware also has a parallel port added, which is nothing more than a couple buffers and latches. I needed this to make the winter camp latrine monitor program, since I discovered that it needs a printer to work properly.&lt;br /&gt;
&lt;br /&gt;
===RS-232 board - 3-24-2020===&lt;br /&gt;
The real expansion interfaces had an option for a RS-232 serial port, which enabled communication to the outside world; most modems required an RS232 port. Since I want to hook this up to a raspberry pi eventually, I'll need one.&lt;br /&gt;
&lt;br /&gt;
The original board used a tr1602 UART and BR1941 as the baud rate generator. I couldn't find either of these chips, but I did find a TR1865 and COM8136, which are chips used in the Model 4, and are compatible. They also do not require a -5 or 12V supply, not that it's a big deal as I have an ATX supply as the power source. There's also a few supporting 74 series components, and some buffers to convert the 5V signals to the RS-232 voltages. In place of the buffers, I will try to use MAX232s instead, since I have a bunch of them for some reason. &lt;br /&gt;
&lt;br /&gt;
For address decoding, I am trying a GAL22v10. Normally I don't like using GAL programmable logic, because these chips are no longer made (there's still a lot though), and they sort of obscure how everything works. In this case, I found myself short of 74 logic chips in the middle of a global pandemic, so why not try something else and replace 4-5 74 chips instead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
edit 4/12/2020 - It works! I implemented everything in a real Model I RS232 board, except:&lt;br /&gt;
* I didn't implement the DIP switches. This is just some dip switches hooked to an IO port input. You can set everything from software. Model 3 removed these.&lt;br /&gt;
** This left me with an extra unused output pin on the GAL22V10. I found some TIL-311 hexadecimal LED displays / decoders, so I wired them up to IO port 0xEC. Now, I can have a debug display, simply OUT (0ech),a to write to the display.&lt;br /&gt;
* Did not implement the original TTL-RS232 voltage converters (I didn't have any of these ICs) What I did have was a bunch of MAX232 converter ICs, which do the same thing but have a capacitor charge pump to get the RS232 voltage. Also, per chip, they have just 2 TTL--&amp;gt;232 and 2 232--&amp;gt;TTL paths. So, I implemented tx/rx, rts/cts, dtr/dsr and DCD. RI is not connected to any 232 pin, and the TRS-80 thinks it is always OFF. &lt;br /&gt;
&lt;br /&gt;
I wrote my own terminal emulator after finding the TRS-80 offerings lacking. See [[TRS-80 - WTERM]].&lt;br /&gt;
&lt;br /&gt;
===Orchestra 80/85 - April-May 2020===&lt;br /&gt;
The next thing I wanted to try out was ways to produce sounds and music on the TRS-80. Back in the day, the king of TRS-80 music was apparently a hardware add on called the Orchestra-80. According to trs-80.org, this added &amp;quot;four simultaneous voices over a six octave range.&amp;quot; However, the sound did not come from a hardware sound chip, like the specialized ones Commodore or Atari had. Instead, the audio hardware was basically just a latch and some resistors, wired into an IO port. It's an R-2R DAC - basically the same thing as the much later Covox Speech Thing or Disney Sound Source for PCs. The real genius of the Orchestra-80 system was the excellent software, which synthesizes all the audio on the fly, and spits it out the IO port. Plus, the people who made it actually knew something about music theory, so it is possible to properly write music in it. Later, the Orchestra-80 was superseded by the Orchestra-85 and 90, which use slightly different hardware but worked with the model 3 and 4 too, and add stereo sound.&lt;br /&gt;
&lt;br /&gt;
I found very little information on the technical details - anyone who has the boards seem unwilling to share the chips that are on them, and there were no schematics in the original manuals. However, we have emulators and the original software.&lt;br /&gt;
&lt;br /&gt;
I started with the Orchestra-80. This writes data to just 1 IO port - 0xBD. Audio is unsigned 8-bit PCM, so it can be hooked up directly to the resistors. I used my extra GAL pin from the RS-232 board that is hooked to the LED hex displays, and redefined it to act on writes port 0xBD instead. From there, it's just a 74x374 latch, and some 10K and 20K resistors. I followed a schematic for a Covox. You can also look up R-2R DAC and get the same thing. D7 is the most significant bit. Later, I added a low pass filter - the sampling frequency is about 4Khz, and the resistors aren't perfect, so the audio can be quite noisy in the high frequencies.&lt;br /&gt;
&lt;br /&gt;
So that's the 80 done. But later, I found out that most of the song files out there were written for the Orchestra-85/90, (85 is the Model 1 version) which has different hardware. First of all, it uses 2 IO ports (0xD5 and 0xD9) for 2 DACs and 2 stereo channels. Also, there is something different about the way the audio PCM is formatted - it's not PCM. If you take a dump of the data written to the ports and dump it into Audacity, you will find it sounds correct when imported as signed 8 bit PCM. But for signed, you need to take the 2's complement - invert the MSB and subtract 1. But, I found evidence to support the Orchestra-85/90 was still using R-2R resistors. How is the hardware doing the subtraction?&lt;br /&gt;
&lt;br /&gt;
Before trying the hardware, I did the much more difficult thing of disassembling the Orchestra-85 software, and patched it to do the D7 inversion, average the result, and spit it out port 0xBD - my existing Orchestra-80 DAC. This did work, but the averaged audio was of even less quality (we are throwing away 1 bit), and the additional CPU overhead also slowed down the audio.&lt;br /&gt;
&lt;br /&gt;
Back to hardware - I did some research, but was unable to find any DAC IC that took an 8-channel signed 2s complement inputs. It turns out that the audio data is probably not unsigned after all; it's probably &amp;quot;offset binary&amp;quot; which is the same process as 2's complement but you don't subtract 1, you just invert the MSB. That's easy.&lt;br /&gt;
&lt;br /&gt;
Since I didn't feel like wiring up a bunch of resistors, I looked for a parallel DAC chip instead, which should be more accurate anyway. I settled on the AD7302, which is a 2 channel, 8 bit DAC. After a sample order from Analog Devices, I had one. Basically, the whole ADC is now on this one chip instead of a bunch of resistor packs and latches. For address decoding, I decided to use a GAL16v8 chip, since I had good luck using another one on the RS232 build. The GAL can also do the needed inversion of D7. So, this boils down the entire Orchestra-85 down to 2 chips, and 2 RC low pass filters. The solution fits on a tiny proto-pcb, neatly between the floppy controller and top boards. Because of the GAL, I also can decode the Orchestra-80 port, and not invert D7 for that one.&lt;br /&gt;
&lt;br /&gt;
So, how does it sound? Actually, pretty good! Much better than anything you would expect is possible on a 1978 machine. It does use 100% of the CPU's processing power though, which is completely expected.&lt;br /&gt;
&lt;br /&gt;
Another experiment I did was to fill the TRS-80's 48K of RAM with 8-bit PCM samples, then write a short program in my secret 0x3000 ram area to dump it out through the Orchestra-80 port. Realistically, 49152 samples only holds a couple samples of audio at a low bitrate, but it's enough for short sound effects, or a 5 second loop of a song that repeats. This is true digital audio on the TRS-80!&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=TRS-80_Model_1_-_Wilson_Expansion_Interface&amp;diff=581</id>
		<title>TRS-80 Model 1 - Wilson Expansion Interface</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=TRS-80_Model_1_-_Wilson_Expansion_Interface&amp;diff=581"/>
		<updated>2020-05-21T03:31:38Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: /* Orchestra 80/85 - April-May 2020 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:Trs80ei2-proto.jpg|thumb|Completed prototype featuring extra RAM, SD and DD floppy controllers, RTC heartbeat, SD Card and shared 0x3000 RAM]]&lt;br /&gt;
The '''TRS-80 Model 1''' originally comprised just the keyboard unit (containing the cpu, video diver, keyboard, cassette reader circuitry and 16k or ram) and monitor. Later, Radio Shack introduced the '''Expansion Interface (EI)''', which was another unit that sat behind the keyboard and added up to 32K more ram, a floppy disk controller, and some extra logic to control a printer or other accessories. Other features could be added to the expansion interface, including a doubler that added extra capacity to floppy disks or an rs232 board.&lt;br /&gt;
&lt;br /&gt;
When I got my first TRS-80 in 2018, all I got was the keyboard. I had to make my own adapters to connect a standard NTSC monitor, cassette recorder, and I even rewound a transformer to provide the combination of different voltages that the TRS-80 needs for power. However, since I eventually wanted to add more memory and run some of the DOS operating systems; this meant I would have to come up with an Expansion Interface. Unfortunately, most of the original EIs online, even ones that could not be confirmed working, were priced higher than I originally bought the keyboard. The schematics for the EI are in the Radio Shack Service manuals, which are easily found online, and the EI is made using almost entirely using 74 series logic. Since I happened to have a large stock of 74 logic chips and some prototyping boards, I decided to make my own EI. Doing this would also allow the freedom to change the wiring around for experiments while not destroying a piece of irreplaceable vintage hardware.&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
[[File:Ei2-block-diagran.png|thumb|Block diagram of my EI so far]]&lt;br /&gt;
My EI contains:&lt;br /&gt;
&lt;br /&gt;
* Blinkenlights - Connected to address bus, data bus, and control signals.&lt;br /&gt;
* 32K RAM (using a 128K SRAM chip because that's what I happened to have; the rest is not yet used)&lt;br /&gt;
* INS1771N floppy disk controller for FM encoded disks (fully compatible with the original FD1771 in a real EI, can therefore read the nonstandard DAM records used by TRSDOS)&lt;br /&gt;
* WD2793 floppy controller for MFM encoded disks, set up like a Percom Doubler&amp;lt;ref&amp;gt;[http://www.trs-80.org/percom-doubler/ Percom Doubler]&amp;lt;/ref&amp;gt; but this particular chip also has good internal data separation too, saving a few chips.&lt;br /&gt;
* 1K dual port SRAM, placed at TRS-80 memory address 0x3000-0x33ff, which is unused address space in a normal Model 1 system. This ram is shared between the trs80 and the atmega microcontoller.&lt;br /&gt;
* ATMEGA1284p microcontroller, running the [https://github.com/MCUdude/MightyCore Mightycore] bootloader; this makes it compatible with and able to be programmed by Arduino software. &lt;br /&gt;
* SD card, connected to Arduino&lt;br /&gt;
&lt;br /&gt;
Things not covered yet are:&lt;br /&gt;
* Printer port&lt;br /&gt;
* TRS-80 RS232 port&lt;br /&gt;
* Screen printer port&lt;br /&gt;
* Extra cassette port / switch&lt;br /&gt;
&lt;br /&gt;
==Theory of operation / Features==&lt;br /&gt;
[[File:Trs80ei2-address-decoder.PNG|thumb|Simulation of prototype address decoder / glue logic]]&lt;br /&gt;
Most of my system works like a normal EI, at least as far as the floppy controllers and extra system memory goes. I mainly drew inspiration for the wiring from the TRS-80 technical manuals and schematics. In this version, some sections are taken verbatim from the original schematics, like the six chips used for the clock divider. I found a design for a double density disk adapter in an old copy of Northern Bytes&amp;lt;ref&amp;gt;Northern Bytes, Volume 7, Number 5[https://archive.org/details/Northern_Bytes_Volume_7_Number_5_1985_Alternate_Source_Information_Outlet_The_US]&amp;lt;/ref&amp;gt;. However, the address decoding and buffering logic is my own design, done with 74 series logic. Programmable logic might make things more compact, but there is a steep learning curve (I have not found anything like Arduino for programmable logic) and the PAL devices I have are no longer in manufacturing. Besides, traditional 74 series logic seems to fit better with the spirit of the project.&lt;br /&gt;
&lt;br /&gt;
I have not implemented any power supply for the board, just some filtering; because I needed a standard ATX power supply to drive floppy drives, I decided to use the +5, -5, and +12 volt supplies from that instead.&lt;br /&gt;
&lt;br /&gt;
===ATMEGA Section===&lt;br /&gt;
The special new features come from the combination of the dual port RAM and ATMEGA microcontroller. A few TRS-80 projects I have looked at place some extra memory at TRS-80 address 3000H (which is otherwise completely unused in a stock machine), for extra code or other features. I have placed a Dual port RAM at this location, which means the TRS-80 can read, write, and execute code at this section, adding an extra 1K of general purpose RAM to the system. However, this is dual port RAM, meaning that it can be accessed by two devices '''at the same time'''. I connected the other side of the dual port RAM to an ATMEGA1284P, which is a modern microcontroller which runs at 16MHz and has 16K ram, an impressive system in it's own right when compared to the TRS-80. The microcontroller has an attached SD card for tons of space for storage, and its own operating system, with the express goal of monitoring and managing the shared memory section. &lt;br /&gt;
&lt;br /&gt;
My additions to the TRS-80 are broken down into two programs:&lt;br /&gt;
*'''SH'''ared '''R'''am '''I'''nterface '''P'''rogram - a short z80 program that the TRS-80 runs from the shared dual port RAM&lt;br /&gt;
*'''A'''tmega '''C'''ontrol '''P'''rogram '''T'''erminal - the Arduino sketch that runs on the Atmega, and provides a whole host of functions to read, write and modify data and access hardware on the TRS-80 (via SHRIP).&lt;br /&gt;
&lt;br /&gt;
ACPT command-line functions include:&lt;br /&gt;
* Local (shared dual port RAM) memory functions, which directly change data on the shared RAM. Addresses for these commands range from 000-3ff.&lt;br /&gt;
** &amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt; - display entire contends of RAM (in hex editor-like grid)&lt;br /&gt;
** &amp;lt;code&amp;gt;lr haddress&amp;lt;/code&amp;gt; - reads single byte&lt;br /&gt;
** &amp;lt;code&amp;gt;ll file.bin hstartaddress&amp;lt;/code&amp;gt; - load a dump file to RAM, starting at the given address&lt;br /&gt;
** &amp;lt;code&amp;gt;ld file.bin hstartaddress hendaddress&amp;lt;/code&amp;gt; - dumps a section of memory to a file on the SD card&lt;br /&gt;
* TRS-80 memory functions, working through the monitor program on the shared memory. It must be running for these to work.&lt;br /&gt;
** &amp;lt;code&amp;gt;rs hstartaddress hendaddress&amp;lt;/code&amp;gt; - works like ls, but you can spec sections of Z80 memory instead of the whole thing.&lt;br /&gt;
** &amp;lt;code&amp;gt;lr / ll / ld&amp;lt;/code&amp;gt; - work like their local ram equivalents, but now the address range is the whole Z80 address space: 0000-ffff&lt;br /&gt;
** &amp;lt;code&amp;gt;j address&amp;lt;/code&amp;gt; - jump to code at ''address''&lt;br /&gt;
** &amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; - exit the monitor program and Return to normal TRS-80 functioning.&lt;br /&gt;
** &amp;lt;code&amp;gt;p&amp;lt;/code&amp;gt; - Pause the TRS-80 and run the monitor code.&lt;br /&gt;
** &amp;lt;code&amp;gt;rv file.bin hstartaddress&amp;lt;/code&amp;gt; - verify a file matches a section of RAM&lt;br /&gt;
* SD file commands, special for managing files on the SD card.&lt;br /&gt;
** &amp;lt;code&amp;gt;dir&amp;lt;/code&amp;gt; - shows list of files and their sizes&lt;br /&gt;
** &amp;lt;code&amp;gt;del file.bin&amp;lt;/code&amp;gt; - delete a file&lt;br /&gt;
* File send/receive. Using a special terminal program like teraterm, you can transfer files to and from the SD card without turning off the system and taking it out. There is no special protocol (like XMODEM); files are just sent as raw binary streams.&lt;br /&gt;
** &amp;lt;code&amp;gt;xs file.bin&amp;lt;/code&amp;gt; - send raw file over serial port. If you log these bytes, you can reconstruct the file. &lt;br /&gt;
** &amp;lt;code&amp;gt;xr file.bin&amp;lt;/code&amp;gt; - receive raw file over serial port. Using a terminal program with xon/xoff, send the binary file. &lt;br /&gt;
&lt;br /&gt;
This is all well and good, but SHRIP can only work when the Z80 cpu is running the code, stored inside the shared RAM. One way to get to it is to type SYSTEM ↵ / 12288 ↵ from BASIC to start executing code from address 3000H. But, it sure would be nice to be able to use the monitor program whenever I want, without typing in anything. If I am in DOS or inside a program, there's no way to make the Z80 jump to address 3000H without exiting it. To this end, I decided to modify the TRS-80 ROM. I had already placed an EPROM inside the TRS-80 case to update to the R/S ROM version. I replaced the routine at 002BH with a jump to 3000H. The replaced routine is the part that detects which key is pressed on the TRS-80 keyboard, meaning that it gets run all the time. We can place the rest of the 002BH code at the end of SHRIP, so it can get back to normal operation if we aren't using any SHRIP functions. &lt;br /&gt;
&lt;br /&gt;
Being in the middle of the keyboard routine, the monitor program also has the opportunity to return alternate data than what the keyboard is actually doing. The monitor program checks a byte of shared ram at address 3306H, and if it is nonzero, it will return that value as if the keyboard typed it. This means we can use the microcontroller to type things on the TRS-80 without ever touching the keyboard. I implemented two commands on the microcontroller command line interface to do this:&lt;br /&gt;
* &amp;lt;code&amp;gt;k sometext-to-type&amp;lt;/code&amp;gt; - everything after the &amp;quot;k &amp;quot; is typed on the TRS-80 keyboard.&lt;br /&gt;
**It also works with escape characters, like &amp;lt;code&amp;gt;\n&amp;lt;/code&amp;gt; for eNter and &amp;lt;code&amp;gt;\k&amp;lt;/code&amp;gt; for breaK, &amp;lt;code&amp;gt;\u \d \l \r&amp;lt;/code&amp;gt; for the direction keys, and &amp;lt;code&amp;gt;\c&amp;lt;/code&amp;gt; for the clear key.&lt;br /&gt;
* &amp;lt;code&amp;gt;ks file.txt&amp;lt;/code&amp;gt; - an entire file on the SD card is typed out over the keyboard. Escape characters aren't used here, it just sends the file as if it were a sequence of keyboard scan codes (most of the keyboard is ASCII).&lt;br /&gt;
&lt;br /&gt;
====Files====&lt;br /&gt;
Revision 6 - [[File:Trs80ei2-v6.zip]]&lt;br /&gt;
&lt;br /&gt;
Revision 7 - [[File:Trs80ei2-v7.zip]]&lt;br /&gt;
&lt;br /&gt;
==Hardware/Construction==&lt;br /&gt;
I constructed this on protoboard. Many of these types of projects use wire wrapping, but I don't have any wire wrapping sockets or any tools for doing this. Instead, I used the wire wrap-esque technique where wires jump to different points on the bottom of the board, and are soldered in place. This looks incredibly messy, and it is extremely easy to burn existing wires with the iron (if you look closely, you can see where I have fixed burnt wires with liquid electrical tape.) However, this technique does allow for a comparatively high chip density on the board, because none of the PCB area is used to carry signals. &lt;br /&gt;
&lt;br /&gt;
Most of my chips are socketed, as pretty much all of them are either very old and fragile, or new and very cheap and of questionable quality. The sockets make it very easy to fix bad chips, especially since the bottom of these sockets usually have a separate, free wire on most of the pins. &lt;br /&gt;
&lt;br /&gt;
On most of my chips, I have applied a label, making it easy to see the function on every pin. I make my labels using a thermal printer that was originally used to print price stickers in a store. The type of thermal labels used will turn black when they get hot. I have used this property more than once to detect a bad chip on my board (the chips often heat up when they go bad).&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
trs80ei2-base-bottom.jpg|base-bottom&lt;br /&gt;
Trs80ei2-base-top.jpg|base-top&lt;br /&gt;
Trs80ei2-doubler-bottom.jpg|doubler-bottom&lt;br /&gt;
Trs80ei2-doubler-top.jpg|doubler-top&lt;br /&gt;
Trs80ei2-floppy-bottom.jpg|floppy-bottom&lt;br /&gt;
Trs80ei2-floppy-top.jpg|floppy-top&lt;br /&gt;
Trs80ei2-lights-top.jpg|lights-top&lt;br /&gt;
Trs80ei2-lights-bottom.jpg|lights-bottom&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Log==&lt;br /&gt;
===Debugging a bad RAM chip - May 31, 2019===&lt;br /&gt;
While running the TRS-80 today, I noticed that I was not able to read files reliably under LDOS, but DoubleDos worked just as well as ever. I know that I had LDOS working when I first completed the expansion interface, so something was clearly wrong. Upon further investigation, I noticed that the errors only occurred when the double density driver (FDUBL) was loaded in LDOS. When the driver was loaded, I experienced complete instability when trying to read disks, and I could not format double density disks at all; the system would simply hang instead of stepping the disk drive. Initially, I thought there might be something wrong with my doubler board, after all, it does sit on top of the stack of boards and is slightly crooked. But, double density still worked fine under DoubleDos, so this was probably not the issue. &lt;br /&gt;
&lt;br /&gt;
My next thought was some kind of ram problem. I did notice that when loading data directly to ram with my Atmega monitor program, I noticed some verify errors at system address 0xb300. At first, I thought that this might be a stack pointer that was getting overwritten, but then I remembered that my monitor places it's stack within the shared 1k RAM. I tried directly writing to 0xb300 and could not get a consistent write, though the bytes before and after it worked fine. Could this verify error be the problem? This section of system memory would be within the 32K of SRAM provided by my expansion board. I am using a 128K chip here because that's what I had, but the chips I have are not credibly sourced, and I have had problems in this board with clone chips of 74 logic. &lt;br /&gt;
&lt;br /&gt;
I tried replacing the RAM and the problem did indeed go away, and I was finally able to fully load entire (48K) ram snapshots using my monitor program with no verify errors. At the same time, I found LDOS once again able to read and write disks reliably. So it seems that the ram was bad, although I find it strange that only a single byte was affected.&lt;br /&gt;
&lt;br /&gt;
===FreHD clone experiment - June 3, 2019===&lt;br /&gt;
Before I had completed the floppy controllers, I had began work on a frehd clone that would fit on my weird stacked EI, which I could never get to work properly. Developed by Frederic Vecoven, The [http://www.vecoven.com/trs80/trs80.html frehd project is an open source project] that emulates a hard drive in a manner consistent with the original mfm disk controllers you could get back in the day, though there is [http://members.iinet.net.au/~ianmav/trs80/emulator.htm now a closed source version assembled and sold] by Ian Mavric of Australia. After getting all my floppy drives working, having two floppy drives simulated with Goteks (more on this later), I really don't need a frehd too, but I had already made the board (following the schematic in the original frehd open source release), and so it was worth another try.&lt;br /&gt;
&lt;br /&gt;
My first experiment was to try a program called [http://48k.ca/trsvid.html TRSVID] (developed by George Phillips) which can display full motion video on the trs-80 using a frehd, although you need to change the firmware on the frehd PIC microcontroller first. Having constructed the entire board myself, I had a programmer too in order to program the PIC in the first place, and after loading the Phillips firmware, I too was able to watch grainy videos on my TRS-80, with equally crummy sound. Although the quality isn't great by modern standards, this is an extremely impressive demo considering the TRS-80's age. You only need to look at [[Wilson Tetris|my Tetris program]] to see the normal limitations of the TRS-80 graphics.&lt;br /&gt;
&lt;br /&gt;
However, I ran into trouble when trying to actually use my clone frehd for its intended purpose: as a hard drive. The freHD comes with some utilities to help mount and create hard disks, and &amp;lt;code&amp;gt;VHDUTL&amp;lt;/code&amp;gt; seems to work ok. However, trying to actually format the disks using &amp;lt;code&amp;gt;RSHARD1&amp;lt;/code&amp;gt; (using 840 tracks, 6 heads and 140 sectors), gave mixed results. Sometimes, the system would lock up after I finished filling out the prompts to mount the disk using &amp;lt;code&amp;gt;SYSTEM (DRIVE=4,DISABLE,DRIVER=&amp;quot;RSHARD1&amp;quot;)&amp;lt;/code&amp;gt; Other times, it would lock up after trying the &amp;lt;code&amp;gt;RSFORM1&amp;lt;/code&amp;gt; program which is supposed to format the disk. Most of the time though, these commands would work successfully, even verifying the format correctly. But, I found that afterwards, the system might lock up when trying to display the directory. &lt;br /&gt;
&lt;br /&gt;
In the event that all of the above setup works properly, I was still left with one final problem, which is that when I copy a file to the emulated hard drive, it doen't always seem to be a complete copy. I could copy my Tetris program and get it to load at least once or twice, but my other version (Metris, with more pieces) would not run no matter what I tried. Soon afterwards, my hard disk got corrupted and I would have to start all over.&lt;br /&gt;
&lt;br /&gt;
Incidentally, I also noticed that the &amp;lt;code&amp;gt;IMPORT2&amp;lt;/code&amp;gt; command which is used to transfer files from the freHD SD card to TRS-80 devices like floppy disks would also only make corrupt files. Perhaps if I examine the files, or make some test payloads, I may be able to determine what's going on, but that will need to wait for another day. I know that Ian made some changes the software, either to the frehd, trs-80 or both, to make the frehd work on a Model 1, but as of now I am not sure what those changes are. &lt;br /&gt;
&lt;br /&gt;
It may be better to continue my ATMEGA/dual port ram solution anyway, as this has the advantage of modifying any memory location.&lt;br /&gt;
&lt;br /&gt;
===Version 7 of software - June 19, 2019===&lt;br /&gt;
It seems like there is some kind of problem with the 32K ram on the board, most likely a bad connection or something, which is near impossible to find. I sometimes find that reseating the SRAM chip or swapping it for a diffrent one will usually work, even though all of my ram chips test good using my EPROM programmer as a tester. &lt;br /&gt;
&lt;br /&gt;
Anyways, I decided I needed to add a RAM test feature to the atmega operator interface. Before this, I created two 48k files of all 1s (0xFF) and all 0s (0x00), which I would load each with the '''rl''' command, which verifies each byte as it is written, allowing me to find RAM errors. Since this is a rather long command to type out, I decided to include a command &amp;lt;code&amp;gt;tstram&amp;lt;/code&amp;gt; that would write all 1s, then all 0s, for each ram address over the entire 48K ram. An optional number after the command will specify the number of loops to do, from once to a whole bunch.&lt;br /&gt;
&lt;br /&gt;
Additionally, I discovered a bug that would not allow the 3rd noun to be counted, making all commands think that there were only 2 nouns maximum (nouns=parameters after the command), making commands like &amp;lt;code&amp;gt;rd&amp;lt;/code&amp;gt; inoperable. As a workaround for v7, I increased the allowed number of nouns to 4, which means that we still have the same error, but now the parser thinks that we can't have more than 3 nouns, which is the most that any command has. I will fix this properly in the next releases.&lt;br /&gt;
&lt;br /&gt;
===v9 - Aug 2019===&lt;br /&gt;
Since the last log entry, I have indeed found the cause of the RAM issue - an address pin (one of the high address pins that isn't used) was left floating! This means that the ram would randomly switch to a different bank; this would be as if the ram randomly erases everything sometimes.&lt;br /&gt;
&lt;br /&gt;
The reason I discovered this was because I wired these extra address pins to some atmega pins, for an experiment. Now, through the Atmega, I can switch the 32k of system ram between 4 banks. &lt;br /&gt;
&lt;br /&gt;
I also am experimenting with an on-board terminal for the Atmega terminal control interface. From the little z80 program running in the dual port RAM, we already have access to the TRS-80 keyboard. To get access to the screen, we can call address 0x033a, which writes the character in A onto the screen, then advances the cursor. There are a few other routines &amp;lt;ref&amp;gt;http://www.trs-80.org/trs-80-rom-routines-documented/&amp;lt;/ref&amp;gt; that can help manage the screen, so that makes things like advancing the cursor and scrolling down something that I don't need to implement myself in my own program. I added calls to these routines in SHRIP, meaning that SHRIP-ACPT can now write stuff directly to the screen. I even added extra functions to the ACPT sketch, to make the Arduino stream functions like Serial.print also write to the TRS-80 screen.&lt;br /&gt;
&lt;br /&gt;
Ok, so we now have access to the TRS-80 screen and keyboard. The last step is to make ACPT available on the TRS-80 directly, without needing to use an extra serial terminal on the Atmega. I added a keyboard shortcut to SHRIP, which will enter into an on-screen ACP terminal. All I need to do is first copy the contents of the screen and save the cursor position so I can get back exactly where I left off. I store the image data in one of the extra banks of SRAM, since it is literally wasted space until now. When I'm done with the terminal, I issue the exit command, and the screen data is copied back onto the screen.&lt;br /&gt;
&lt;br /&gt;
This means it is now possible to load machine code directly from the Atmega SD card into trs-80 memory, and execute it, without ever hooking up the trsei2 board to another computer. You can't really load full ram images yet, because the onscreen terminal itself uses some of the system RAM, but overall, the system is much more accessible now.&lt;br /&gt;
&lt;br /&gt;
===v12 - December 2019===&lt;br /&gt;
With 128K of SRAM on the expansion board, it would really be nice if that extra ram could be accessed from the Z80 side of things, so that I don't have to go through the Atmega. This should also speed up assembly programs that use the extra RAM. &lt;br /&gt;
In the memory mapped IO section that breaks out address 37E0-37EC, I used 74LS138 octal decoders, so I also have lines for 37F0-37FC that are not used in a standard TRS-80. Therefore, I connected a 74LS175 latch to data pins D0-D1, and latch on writes to memory address 37FC. This makes the extra RAM (4 blocks of 32K, placed in the upper half of Z80 memory map) accessible from the Z80 directly.&lt;br /&gt;
&lt;br /&gt;
Hardware also has a parallel port added, which is nothing more than a couple buffers and latches. I needed this to make the winter camp latrine monitor program, since I discovered that it needs a printer to work properly.&lt;br /&gt;
&lt;br /&gt;
===RS-232 board - 3-24-2020===&lt;br /&gt;
The real expansion interfaces had an option for a RS-232 serial port, which enabled communication to the outside world; most modems required an RS232 port. Since I want to hook this up to a raspberry pi eventually, I'll need one.&lt;br /&gt;
&lt;br /&gt;
The original board used a tr1602 UART and BR1941 as the baud rate generator. I couldn't find either of these chips, but I did find a TR1865 and COM8136, which are chips used in the Model 4, and are compatible. They also do not require a -5 or 12V supply, not that it's a big deal as I have an ATX supply as the power source. There's also a few supporting 74 series components, and some buffers to convert the 5V signals to the RS-232 voltages. In place of the buffers, I will try to use MAX232s instead, since I have a bunch of them for some reason. &lt;br /&gt;
&lt;br /&gt;
For address decoding, I am trying a GAL22v10. Normally I don't like using GAL programmable logic, because these chips are no longer made (there's still a lot though), and they sort of obscure how everything works. In this case, I found myself short of 74 logic chips in the middle of a global pandemic, so why not try something else and replace 4-5 74 chips instead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
edit 4/12/2020 - It works! I implemented everything in a real Model I RS232 board, except:&lt;br /&gt;
* I didn't implement the DIP switches. This is just some dip switches hooked to an IO port input. You can set everything from software. Model 3 removed these.&lt;br /&gt;
** This left me with an extra unused output pin on the GAL22V10. I found some TIL-311 hexadecimal LED displays / decoders, so I wired them up to IO port 0xEC. Now, I can have a debug display, simply OUT (0ech),a to write to the display.&lt;br /&gt;
* Did not implement the original TTL-RS232 voltage converters (I didn't have any of these ICs) What I did have was a bunch of MAX232 converter ICs, which do the same thing but have a capacitor charge pump to get the RS232 voltage. Also, per chip, they have just 2 TTL--&amp;gt;232 and 2 232--&amp;gt;TTL paths. So, I implemented tx/rx, rts/cts, dtr/dsr and DCD. RI is not connected to any 232 pin, and the TRS-80 thinks it is always OFF. &lt;br /&gt;
&lt;br /&gt;
I wrote my own terminal emulator after finding the TRS-80 offerings lacking. See [[TRS-80 - WTERM]].&lt;br /&gt;
&lt;br /&gt;
===Orchestra 80/85 - April-May 2020===&lt;br /&gt;
The next thing I wanted to try out was ways to produce sounds and music on the TRS-80. Back in the day, the king of TRS-80 music was apparently a hardware add on called the Orchestra-80. According to trs-80.org, this added &amp;quot;four simultaneous voices over a six octave range.&amp;quot; However, the sound did not come from a hardware sound chip, like the specialized ones Commodore or Atari had. Instead, the audio hardware was basically just a latch and some resistors, wired into an IO port. It's an R-2R DAC - basically the same thing as the much later Covox Speech Thing or Disney Sound Source for PCs. The real genius of the Orchestra-80 system was the excellent software, which synthesizes all the audio on the fly, and spits it out the IO port. Plus, the people who made it actually knew something about music theory, so it is possible to properly write music in it. Later, the Orchestra-80 was superseded by the Orchestra-85 and 90, which use slightly different hardware but worked with the model 3 and 4, and add stereo sound.&lt;br /&gt;
&lt;br /&gt;
I found very little information on the technical details - anyone who has the boards seem unwilling to share the chips that are on them, and there were no schematics in the original manuals. However, we have emulators and the original software.&lt;br /&gt;
&lt;br /&gt;
I started with the Orchestra-80. This writes data to just 1 IO port - 0xBD. Audio is unsigned 8-bit PCM, so it can be hooked up directly to the resistors. I used my extra GAL pin from the RS-232 board that is hooked to the LED hex displays, and redefined it to act on writes port 0xBD instead. From there, it's just a 74x374 latch, and some 10K and 20K resistors. I followed a schematic for a Covox. You can also look up R-2R DAC and get the same thing. D7 is the most significant bit. Later, I added a low pass filter - the sampling frequency is about 4Khz, and the resistors aren't perfect, so the audio can be quite noisy in the high frequencies.&lt;br /&gt;
&lt;br /&gt;
So that's the 80 done. But later, I found out that most of the song files out there were written for the Orchestra-85/90, (85 is the Model 1 version) which has different hardware. First of all, it uses 2 IO ports (0xD5 and 0xD9) for 2 DACs and 2 stereo channels. Also, there is something different about the way the audio PCM is formatted - it's not PCM. If you take a dump of the data written to the ports and dump it into Audacity, you will find it sounds correct when imported as signed 8 bit PCM. But for signed, you need to take the 2's complement - invert the MSB and subtract 1. But, I found evidence to support the Orchestra-85/90 was still using R-2R resistors. How is the hardware doing the subtraction?&lt;br /&gt;
&lt;br /&gt;
Before trying the hardware, I did the much more difficult thing of disassembling the Orchestra-85 software, and patched it to do the D7 inversion, average the result, and spit it out port 0xBD - my existing Orchestra-80 DAC. This did work, but the averaged audio was of even less quality (we are throwing away 1 bit), and the additional CPU overhead also slowed down the audio.&lt;br /&gt;
&lt;br /&gt;
Back to hardware - I did some research, but was unable to find any DAC IC that took an 8-channel signed 2s complement inputs. It turns out that the audio data is probably not unsigned after all; it's probably &amp;quot;offset binary&amp;quot; which is the same process as 2's complement but you don't subtract 1, you just invert the MSB. That's easy.&lt;br /&gt;
&lt;br /&gt;
Since I didn't feel like wiring up a bunch of resistors, I looked for a parallel DAC chip instead, which should be more accurate anyway. I settled on the AD7302, which is a 2 channel, 8 bit DAC. After a sample order from Analog Devices, I had one. Basically, the whole ADC is now on this one chip instead of a bunch of resistor packs and latches. For address decoding, I decided to use a GAL16v8 chip, since I had good luck using another one on the RS232 build. The GAL can also do the needed inversion of D7. So, this boils down the entire Orchestra-85 down to 2 chips, and 2 RC low pass filters. The solution fits on a tiny proto-pcb, neatly between the floppy controller and top boards. Because of the GAL, I also can decode the Orchestra-80 port, and not invert D7 for that one.&lt;br /&gt;
&lt;br /&gt;
So, how does it sound? Actually, pretty good! Much better than anything you would expect is possible on a 1978 machine. It does use 100% of the CPU's processing power though, which is completely expected.&lt;br /&gt;
&lt;br /&gt;
Another experiment I did was to fill the TRS-80's 48K of RAM with 8-bit PCM samples, then write a short program in my secret 0x3000 ram area to dump it out through the Orchestra-80 port. Realistically, 49152 samples only holds a couple samples of audio at a low bitrate, but it's enough for short sound effects, or a 5 second loop of a song that repeats. This is true digital audio on the TRS-80!&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=TRS-80_Model_1_-_Wilson_Expansion_Interface&amp;diff=580</id>
		<title>TRS-80 Model 1 - Wilson Expansion Interface</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=TRS-80_Model_1_-_Wilson_Expansion_Interface&amp;diff=580"/>
		<updated>2020-05-21T03:20:46Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: /* Log */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:Trs80ei2-proto.jpg|thumb|Completed prototype featuring extra RAM, SD and DD floppy controllers, RTC heartbeat, SD Card and shared 0x3000 RAM]]&lt;br /&gt;
The '''TRS-80 Model 1''' originally comprised just the keyboard unit (containing the cpu, video diver, keyboard, cassette reader circuitry and 16k or ram) and monitor. Later, Radio Shack introduced the '''Expansion Interface (EI)''', which was another unit that sat behind the keyboard and added up to 32K more ram, a floppy disk controller, and some extra logic to control a printer or other accessories. Other features could be added to the expansion interface, including a doubler that added extra capacity to floppy disks or an rs232 board.&lt;br /&gt;
&lt;br /&gt;
When I got my first TRS-80 in 2018, all I got was the keyboard. I had to make my own adapters to connect a standard NTSC monitor, cassette recorder, and I even rewound a transformer to provide the combination of different voltages that the TRS-80 needs for power. However, since I eventually wanted to add more memory and run some of the DOS operating systems; this meant I would have to come up with an Expansion Interface. Unfortunately, most of the original EIs online, even ones that could not be confirmed working, were priced higher than I originally bought the keyboard. The schematics for the EI are in the Radio Shack Service manuals, which are easily found online, and the EI is made using almost entirely using 74 series logic. Since I happened to have a large stock of 74 logic chips and some prototyping boards, I decided to make my own EI. Doing this would also allow the freedom to change the wiring around for experiments while not destroying a piece of irreplaceable vintage hardware.&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
[[File:Ei2-block-diagran.png|thumb|Block diagram of my EI so far]]&lt;br /&gt;
My EI contains:&lt;br /&gt;
&lt;br /&gt;
* Blinkenlights - Connected to address bus, data bus, and control signals.&lt;br /&gt;
* 32K RAM (using a 128K SRAM chip because that's what I happened to have; the rest is not yet used)&lt;br /&gt;
* INS1771N floppy disk controller for FM encoded disks (fully compatible with the original FD1771 in a real EI, can therefore read the nonstandard DAM records used by TRSDOS)&lt;br /&gt;
* WD2793 floppy controller for MFM encoded disks, set up like a Percom Doubler&amp;lt;ref&amp;gt;[http://www.trs-80.org/percom-doubler/ Percom Doubler]&amp;lt;/ref&amp;gt; but this particular chip also has good internal data separation too, saving a few chips.&lt;br /&gt;
* 1K dual port SRAM, placed at TRS-80 memory address 0x3000-0x33ff, which is unused address space in a normal Model 1 system. This ram is shared between the trs80 and the atmega microcontoller.&lt;br /&gt;
* ATMEGA1284p microcontroller, running the [https://github.com/MCUdude/MightyCore Mightycore] bootloader; this makes it compatible with and able to be programmed by Arduino software. &lt;br /&gt;
* SD card, connected to Arduino&lt;br /&gt;
&lt;br /&gt;
Things not covered yet are:&lt;br /&gt;
* Printer port&lt;br /&gt;
* TRS-80 RS232 port&lt;br /&gt;
* Screen printer port&lt;br /&gt;
* Extra cassette port / switch&lt;br /&gt;
&lt;br /&gt;
==Theory of operation / Features==&lt;br /&gt;
[[File:Trs80ei2-address-decoder.PNG|thumb|Simulation of prototype address decoder / glue logic]]&lt;br /&gt;
Most of my system works like a normal EI, at least as far as the floppy controllers and extra system memory goes. I mainly drew inspiration for the wiring from the TRS-80 technical manuals and schematics. In this version, some sections are taken verbatim from the original schematics, like the six chips used for the clock divider. I found a design for a double density disk adapter in an old copy of Northern Bytes&amp;lt;ref&amp;gt;Northern Bytes, Volume 7, Number 5[https://archive.org/details/Northern_Bytes_Volume_7_Number_5_1985_Alternate_Source_Information_Outlet_The_US]&amp;lt;/ref&amp;gt;. However, the address decoding and buffering logic is my own design, done with 74 series logic. Programmable logic might make things more compact, but there is a steep learning curve (I have not found anything like Arduino for programmable logic) and the PAL devices I have are no longer in manufacturing. Besides, traditional 74 series logic seems to fit better with the spirit of the project.&lt;br /&gt;
&lt;br /&gt;
I have not implemented any power supply for the board, just some filtering; because I needed a standard ATX power supply to drive floppy drives, I decided to use the +5, -5, and +12 volt supplies from that instead.&lt;br /&gt;
&lt;br /&gt;
===ATMEGA Section===&lt;br /&gt;
The special new features come from the combination of the dual port RAM and ATMEGA microcontroller. A few TRS-80 projects I have looked at place some extra memory at TRS-80 address 3000H (which is otherwise completely unused in a stock machine), for extra code or other features. I have placed a Dual port RAM at this location, which means the TRS-80 can read, write, and execute code at this section, adding an extra 1K of general purpose RAM to the system. However, this is dual port RAM, meaning that it can be accessed by two devices '''at the same time'''. I connected the other side of the dual port RAM to an ATMEGA1284P, which is a modern microcontroller which runs at 16MHz and has 16K ram, an impressive system in it's own right when compared to the TRS-80. The microcontroller has an attached SD card for tons of space for storage, and its own operating system, with the express goal of monitoring and managing the shared memory section. &lt;br /&gt;
&lt;br /&gt;
My additions to the TRS-80 are broken down into two programs:&lt;br /&gt;
*'''SH'''ared '''R'''am '''I'''nterface '''P'''rogram - a short z80 program that the TRS-80 runs from the shared dual port RAM&lt;br /&gt;
*'''A'''tmega '''C'''ontrol '''P'''rogram '''T'''erminal - the Arduino sketch that runs on the Atmega, and provides a whole host of functions to read, write and modify data and access hardware on the TRS-80 (via SHRIP).&lt;br /&gt;
&lt;br /&gt;
ACPT command-line functions include:&lt;br /&gt;
* Local (shared dual port RAM) memory functions, which directly change data on the shared RAM. Addresses for these commands range from 000-3ff.&lt;br /&gt;
** &amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt; - display entire contends of RAM (in hex editor-like grid)&lt;br /&gt;
** &amp;lt;code&amp;gt;lr haddress&amp;lt;/code&amp;gt; - reads single byte&lt;br /&gt;
** &amp;lt;code&amp;gt;ll file.bin hstartaddress&amp;lt;/code&amp;gt; - load a dump file to RAM, starting at the given address&lt;br /&gt;
** &amp;lt;code&amp;gt;ld file.bin hstartaddress hendaddress&amp;lt;/code&amp;gt; - dumps a section of memory to a file on the SD card&lt;br /&gt;
* TRS-80 memory functions, working through the monitor program on the shared memory. It must be running for these to work.&lt;br /&gt;
** &amp;lt;code&amp;gt;rs hstartaddress hendaddress&amp;lt;/code&amp;gt; - works like ls, but you can spec sections of Z80 memory instead of the whole thing.&lt;br /&gt;
** &amp;lt;code&amp;gt;lr / ll / ld&amp;lt;/code&amp;gt; - work like their local ram equivalents, but now the address range is the whole Z80 address space: 0000-ffff&lt;br /&gt;
** &amp;lt;code&amp;gt;j address&amp;lt;/code&amp;gt; - jump to code at ''address''&lt;br /&gt;
** &amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; - exit the monitor program and Return to normal TRS-80 functioning.&lt;br /&gt;
** &amp;lt;code&amp;gt;p&amp;lt;/code&amp;gt; - Pause the TRS-80 and run the monitor code.&lt;br /&gt;
** &amp;lt;code&amp;gt;rv file.bin hstartaddress&amp;lt;/code&amp;gt; - verify a file matches a section of RAM&lt;br /&gt;
* SD file commands, special for managing files on the SD card.&lt;br /&gt;
** &amp;lt;code&amp;gt;dir&amp;lt;/code&amp;gt; - shows list of files and their sizes&lt;br /&gt;
** &amp;lt;code&amp;gt;del file.bin&amp;lt;/code&amp;gt; - delete a file&lt;br /&gt;
* File send/receive. Using a special terminal program like teraterm, you can transfer files to and from the SD card without turning off the system and taking it out. There is no special protocol (like XMODEM); files are just sent as raw binary streams.&lt;br /&gt;
** &amp;lt;code&amp;gt;xs file.bin&amp;lt;/code&amp;gt; - send raw file over serial port. If you log these bytes, you can reconstruct the file. &lt;br /&gt;
** &amp;lt;code&amp;gt;xr file.bin&amp;lt;/code&amp;gt; - receive raw file over serial port. Using a terminal program with xon/xoff, send the binary file. &lt;br /&gt;
&lt;br /&gt;
This is all well and good, but SHRIP can only work when the Z80 cpu is running the code, stored inside the shared RAM. One way to get to it is to type SYSTEM ↵ / 12288 ↵ from BASIC to start executing code from address 3000H. But, it sure would be nice to be able to use the monitor program whenever I want, without typing in anything. If I am in DOS or inside a program, there's no way to make the Z80 jump to address 3000H without exiting it. To this end, I decided to modify the TRS-80 ROM. I had already placed an EPROM inside the TRS-80 case to update to the R/S ROM version. I replaced the routine at 002BH with a jump to 3000H. The replaced routine is the part that detects which key is pressed on the TRS-80 keyboard, meaning that it gets run all the time. We can place the rest of the 002BH code at the end of SHRIP, so it can get back to normal operation if we aren't using any SHRIP functions. &lt;br /&gt;
&lt;br /&gt;
Being in the middle of the keyboard routine, the monitor program also has the opportunity to return alternate data than what the keyboard is actually doing. The monitor program checks a byte of shared ram at address 3306H, and if it is nonzero, it will return that value as if the keyboard typed it. This means we can use the microcontroller to type things on the TRS-80 without ever touching the keyboard. I implemented two commands on the microcontroller command line interface to do this:&lt;br /&gt;
* &amp;lt;code&amp;gt;k sometext-to-type&amp;lt;/code&amp;gt; - everything after the &amp;quot;k &amp;quot; is typed on the TRS-80 keyboard.&lt;br /&gt;
**It also works with escape characters, like &amp;lt;code&amp;gt;\n&amp;lt;/code&amp;gt; for eNter and &amp;lt;code&amp;gt;\k&amp;lt;/code&amp;gt; for breaK, &amp;lt;code&amp;gt;\u \d \l \r&amp;lt;/code&amp;gt; for the direction keys, and &amp;lt;code&amp;gt;\c&amp;lt;/code&amp;gt; for the clear key.&lt;br /&gt;
* &amp;lt;code&amp;gt;ks file.txt&amp;lt;/code&amp;gt; - an entire file on the SD card is typed out over the keyboard. Escape characters aren't used here, it just sends the file as if it were a sequence of keyboard scan codes (most of the keyboard is ASCII).&lt;br /&gt;
&lt;br /&gt;
====Files====&lt;br /&gt;
Revision 6 - [[File:Trs80ei2-v6.zip]]&lt;br /&gt;
&lt;br /&gt;
Revision 7 - [[File:Trs80ei2-v7.zip]]&lt;br /&gt;
&lt;br /&gt;
==Hardware/Construction==&lt;br /&gt;
I constructed this on protoboard. Many of these types of projects use wire wrapping, but I don't have any wire wrapping sockets or any tools for doing this. Instead, I used the wire wrap-esque technique where wires jump to different points on the bottom of the board, and are soldered in place. This looks incredibly messy, and it is extremely easy to burn existing wires with the iron (if you look closely, you can see where I have fixed burnt wires with liquid electrical tape.) However, this technique does allow for a comparatively high chip density on the board, because none of the PCB area is used to carry signals. &lt;br /&gt;
&lt;br /&gt;
Most of my chips are socketed, as pretty much all of them are either very old and fragile, or new and very cheap and of questionable quality. The sockets make it very easy to fix bad chips, especially since the bottom of these sockets usually have a separate, free wire on most of the pins. &lt;br /&gt;
&lt;br /&gt;
On most of my chips, I have applied a label, making it easy to see the function on every pin. I make my labels using a thermal printer that was originally used to print price stickers in a store. The type of thermal labels used will turn black when they get hot. I have used this property more than once to detect a bad chip on my board (the chips often heat up when they go bad).&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
trs80ei2-base-bottom.jpg|base-bottom&lt;br /&gt;
Trs80ei2-base-top.jpg|base-top&lt;br /&gt;
Trs80ei2-doubler-bottom.jpg|doubler-bottom&lt;br /&gt;
Trs80ei2-doubler-top.jpg|doubler-top&lt;br /&gt;
Trs80ei2-floppy-bottom.jpg|floppy-bottom&lt;br /&gt;
Trs80ei2-floppy-top.jpg|floppy-top&lt;br /&gt;
Trs80ei2-lights-top.jpg|lights-top&lt;br /&gt;
Trs80ei2-lights-bottom.jpg|lights-bottom&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Log==&lt;br /&gt;
===Debugging a bad RAM chip - May 31, 2019===&lt;br /&gt;
While running the TRS-80 today, I noticed that I was not able to read files reliably under LDOS, but DoubleDos worked just as well as ever. I know that I had LDOS working when I first completed the expansion interface, so something was clearly wrong. Upon further investigation, I noticed that the errors only occurred when the double density driver (FDUBL) was loaded in LDOS. When the driver was loaded, I experienced complete instability when trying to read disks, and I could not format double density disks at all; the system would simply hang instead of stepping the disk drive. Initially, I thought there might be something wrong with my doubler board, after all, it does sit on top of the stack of boards and is slightly crooked. But, double density still worked fine under DoubleDos, so this was probably not the issue. &lt;br /&gt;
&lt;br /&gt;
My next thought was some kind of ram problem. I did notice that when loading data directly to ram with my Atmega monitor program, I noticed some verify errors at system address 0xb300. At first, I thought that this might be a stack pointer that was getting overwritten, but then I remembered that my monitor places it's stack within the shared 1k RAM. I tried directly writing to 0xb300 and could not get a consistent write, though the bytes before and after it worked fine. Could this verify error be the problem? This section of system memory would be within the 32K of SRAM provided by my expansion board. I am using a 128K chip here because that's what I had, but the chips I have are not credibly sourced, and I have had problems in this board with clone chips of 74 logic. &lt;br /&gt;
&lt;br /&gt;
I tried replacing the RAM and the problem did indeed go away, and I was finally able to fully load entire (48K) ram snapshots using my monitor program with no verify errors. At the same time, I found LDOS once again able to read and write disks reliably. So it seems that the ram was bad, although I find it strange that only a single byte was affected.&lt;br /&gt;
&lt;br /&gt;
===FreHD clone experiment - June 3, 2019===&lt;br /&gt;
Before I had completed the floppy controllers, I had began work on a frehd clone that would fit on my weird stacked EI, which I could never get to work properly. Developed by Frederic Vecoven, The [http://www.vecoven.com/trs80/trs80.html frehd project is an open source project] that emulates a hard drive in a manner consistent with the original mfm disk controllers you could get back in the day, though there is [http://members.iinet.net.au/~ianmav/trs80/emulator.htm now a closed source version assembled and sold] by Ian Mavric of Australia. After getting all my floppy drives working, having two floppy drives simulated with Goteks (more on this later), I really don't need a frehd too, but I had already made the board (following the schematic in the original frehd open source release), and so it was worth another try.&lt;br /&gt;
&lt;br /&gt;
My first experiment was to try a program called [http://48k.ca/trsvid.html TRSVID] (developed by George Phillips) which can display full motion video on the trs-80 using a frehd, although you need to change the firmware on the frehd PIC microcontroller first. Having constructed the entire board myself, I had a programmer too in order to program the PIC in the first place, and after loading the Phillips firmware, I too was able to watch grainy videos on my TRS-80, with equally crummy sound. Although the quality isn't great by modern standards, this is an extremely impressive demo considering the TRS-80's age. You only need to look at [[Wilson Tetris|my Tetris program]] to see the normal limitations of the TRS-80 graphics.&lt;br /&gt;
&lt;br /&gt;
However, I ran into trouble when trying to actually use my clone frehd for its intended purpose: as a hard drive. The freHD comes with some utilities to help mount and create hard disks, and &amp;lt;code&amp;gt;VHDUTL&amp;lt;/code&amp;gt; seems to work ok. However, trying to actually format the disks using &amp;lt;code&amp;gt;RSHARD1&amp;lt;/code&amp;gt; (using 840 tracks, 6 heads and 140 sectors), gave mixed results. Sometimes, the system would lock up after I finished filling out the prompts to mount the disk using &amp;lt;code&amp;gt;SYSTEM (DRIVE=4,DISABLE,DRIVER=&amp;quot;RSHARD1&amp;quot;)&amp;lt;/code&amp;gt; Other times, it would lock up after trying the &amp;lt;code&amp;gt;RSFORM1&amp;lt;/code&amp;gt; program which is supposed to format the disk. Most of the time though, these commands would work successfully, even verifying the format correctly. But, I found that afterwards, the system might lock up when trying to display the directory. &lt;br /&gt;
&lt;br /&gt;
In the event that all of the above setup works properly, I was still left with one final problem, which is that when I copy a file to the emulated hard drive, it doen't always seem to be a complete copy. I could copy my Tetris program and get it to load at least once or twice, but my other version (Metris, with more pieces) would not run no matter what I tried. Soon afterwards, my hard disk got corrupted and I would have to start all over.&lt;br /&gt;
&lt;br /&gt;
Incidentally, I also noticed that the &amp;lt;code&amp;gt;IMPORT2&amp;lt;/code&amp;gt; command which is used to transfer files from the freHD SD card to TRS-80 devices like floppy disks would also only make corrupt files. Perhaps if I examine the files, or make some test payloads, I may be able to determine what's going on, but that will need to wait for another day. I know that Ian made some changes the software, either to the frehd, trs-80 or both, to make the frehd work on a Model 1, but as of now I am not sure what those changes are. &lt;br /&gt;
&lt;br /&gt;
It may be better to continue my ATMEGA/dual port ram solution anyway, as this has the advantage of modifying any memory location.&lt;br /&gt;
&lt;br /&gt;
===Version 7 of software - June 19, 2019===&lt;br /&gt;
It seems like there is some kind of problem with the 32K ram on the board, most likely a bad connection or something, which is near impossible to find. I sometimes find that reseating the SRAM chip or swapping it for a diffrent one will usually work, even though all of my ram chips test good using my EPROM programmer as a tester. &lt;br /&gt;
&lt;br /&gt;
Anyways, I decided I needed to add a RAM test feature to the atmega operator interface. Before this, I created two 48k files of all 1s (0xFF) and all 0s (0x00), which I would load each with the '''rl''' command, which verifies each byte as it is written, allowing me to find RAM errors. Since this is a rather long command to type out, I decided to include a command &amp;lt;code&amp;gt;tstram&amp;lt;/code&amp;gt; that would write all 1s, then all 0s, for each ram address over the entire 48K ram. An optional number after the command will specify the number of loops to do, from once to a whole bunch.&lt;br /&gt;
&lt;br /&gt;
Additionally, I discovered a bug that would not allow the 3rd noun to be counted, making all commands think that there were only 2 nouns maximum (nouns=parameters after the command), making commands like &amp;lt;code&amp;gt;rd&amp;lt;/code&amp;gt; inoperable. As a workaround for v7, I increased the allowed number of nouns to 4, which means that we still have the same error, but now the parser thinks that we can't have more than 3 nouns, which is the most that any command has. I will fix this properly in the next releases.&lt;br /&gt;
&lt;br /&gt;
===v9 - Aug 2019===&lt;br /&gt;
Since the last log entry, I have indeed found the cause of the RAM issue - an address pin (one of the high address pins that isn't used) was left floating! This means that the ram would randomly switch to a different bank; this would be as if the ram randomly erases everything sometimes.&lt;br /&gt;
&lt;br /&gt;
The reason I discovered this was because I wired these extra address pins to some atmega pins, for an experiment. Now, through the Atmega, I can switch the 32k of system ram between 4 banks. &lt;br /&gt;
&lt;br /&gt;
I also am experimenting with an on-board terminal for the Atmega terminal control interface. From the little z80 program running in the dual port RAM, we already have access to the TRS-80 keyboard. To get access to the screen, we can call address 0x033a, which writes the character in A onto the screen, then advances the cursor. There are a few other routines &amp;lt;ref&amp;gt;http://www.trs-80.org/trs-80-rom-routines-documented/&amp;lt;/ref&amp;gt; that can help manage the screen, so that makes things like advancing the cursor and scrolling down something that I don't need to implement myself in my own program. I added calls to these routines in SHRIP, meaning that SHRIP-ACPT can now write stuff directly to the screen. I even added extra functions to the ACPT sketch, to make the Arduino stream functions like Serial.print also write to the TRS-80 screen.&lt;br /&gt;
&lt;br /&gt;
Ok, so we now have access to the TRS-80 screen and keyboard. The last step is to make ACPT available on the TRS-80 directly, without needing to use an extra serial terminal on the Atmega. I added a keyboard shortcut to SHRIP, which will enter into an on-screen ACP terminal. All I need to do is first copy the contents of the screen and save the cursor position so I can get back exactly where I left off. I store the image data in one of the extra banks of SRAM, since it is literally wasted space until now. When I'm done with the terminal, I issue the exit command, and the screen data is copied back onto the screen.&lt;br /&gt;
&lt;br /&gt;
This means it is now possible to load machine code directly from the Atmega SD card into trs-80 memory, and execute it, without ever hooking up the trsei2 board to another computer. You can't really load full ram images yet, because the onscreen terminal itself uses some of the system RAM, but overall, the system is much more accessible now.&lt;br /&gt;
&lt;br /&gt;
===v12 - December 2019===&lt;br /&gt;
With 128K of SRAM on the expansion board, it would really be nice if that extra ram could be accessed from the Z80 side of things, so that I don't have to go through the Atmega. This should also speed up assembly programs that use the extra RAM. &lt;br /&gt;
In the memory mapped IO section that breaks out address 37E0-37EC, I used 74LS138 octal decoders, so I also have lines for 37F0-37FC that are not used in a standard TRS-80. Therefore, I connected a 74LS175 latch to data pins D0-D1, and latch on writes to memory address 37FC. This makes the extra RAM (4 blocks of 32K, placed in the upper half of Z80 memory map) accessible from the Z80 directly.&lt;br /&gt;
&lt;br /&gt;
Hardware also has a parallel port added, which is nothing more than a couple buffers and latches. I needed this to make the winter camp latrine monitor program, since I discovered that it needs a printer to work properly.&lt;br /&gt;
&lt;br /&gt;
===RS-232 board - 3-24-2020===&lt;br /&gt;
The real expansion interfaces had an option for a RS-232 serial port, which enabled communication to the outside world; most modems required an RS232 port. Since I want to hook this up to a raspberry pi eventually, I'll need one.&lt;br /&gt;
&lt;br /&gt;
The original board used a tr1602 UART and BR1941 as the baud rate generator. I couldn't find either of these chips, but I did find a TR1865 and COM8136, which are chips used in the Model 4, and are compatible. They also do not require a -5 or 12V supply, not that it's a big deal as I have an ATX supply as the power source. There's also a few supporting 74 series components, and some buffers to convert the 5V signals to the RS-232 voltages. In place of the buffers, I will try to use MAX232s instead, since I have a bunch of them for some reason. &lt;br /&gt;
&lt;br /&gt;
For address decoding, I am trying a GAL22v10. Normally I don't like using GAL programmable logic, because these chips are no longer made (there's still a lot though), and they sort of obscure how everything works. In this case, I found myself short of 74 logic chips in the middle of a global pandemic, so why not try something else and replace 4-5 74 chips instead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
edit 4/12/2020 - It works! I implemented everything in a real Model I RS232 board, except:&lt;br /&gt;
* I didn't implement the DIP switches. This is just some dip switches hooked to an IO port input. You can set everything from software. Model 3 removed these.&lt;br /&gt;
** This left me with an extra unused output pin on the GAL22V10. I found some TIL-311 hexadecimal LED displays / decoders, so I wired them up to IO port 0xEC. Now, I can have a debug display, simply OUT (0ech),a to write to the display.&lt;br /&gt;
* Did not implement the original TTL-RS232 voltage converters (I didn't have any of these ICs) What I did have was a bunch of MAX232 converter ICs, which do the same thing but have a capacitor charge pump to get the RS232 voltage. Also, per chip, they have just 2 TTL--&amp;gt;232 and 2 232--&amp;gt;TTL paths. So, I implemented tx/rx, rts/cts, dtr/dsr and DCD. RI is not connected to any 232 pin, and the TRS-80 thinks it is always OFF. &lt;br /&gt;
&lt;br /&gt;
I wrote my own terminal emulator after finding the TRS-80 offerings lacking. See [[TRS-80 - WTERM]].&lt;br /&gt;
&lt;br /&gt;
==Orchestra 80/85 - April-May 2020==&lt;br /&gt;
The next thing I wanted to try out was ways to produce sounds and music on the TRS-80. Back in the day, the king of TRS-80 music was apparently a hardware add on called the Orchestra-80. According to trs-80.org, this added &amp;quot;four simultaneous voices over a six octave range.&amp;quot; However, the sound did not come from a hardware sound chip, like the specialized ones Commodore or Atari had. Instead, the audio hardware was basically just a latch and some resistors, wired into an IO port. It's an R-2R DAC - basically the same thing as the much later Covox Speech Thing or Disney Sound Source for PCs. The real genius of the Orchestra-80 system was the excellent software, which synthesizes all the audio on the fly, and spits it out the IO port. Plus, the people who made it actually knew something about music theory, so it is possible to properly write music in it. Later, the Orchestra-80 was superseded by the Orchestra-85 and 90, which use slightly different hardware but worked with the model 3 and 4, and add stereo sound.&lt;br /&gt;
&lt;br /&gt;
I found very little information on the technical details - anyone who has the boards seem unwilling to share the chips that are on them, and there were no schematics in the original manuals. However, we have emulators and the original software.&lt;br /&gt;
&lt;br /&gt;
I started with the Orchestra-80. This writes data to just 1 IO port - 0xBD. Audio is unsigned 8-bit PCM, so it can be hooked up directly to the resistors. I used my extra GAL pin from the RS-232 board that is hooked to the LED hex displays, and redefined it to act on writes port 0xBD instead. From there, it's just a 74x374 latch, and some 10K and 20K resistors. I followed a schematic for a Covox. You can also look up R-2R DAC and get the same thing. D7 is the most significant bit. Later, I added a low pass filter - the sampling frequency is about 4Khz, and the resistors aren't perfect, so the audio can be quite noisy in the high frequencies.&lt;br /&gt;
&lt;br /&gt;
So that's the 80 done. But later, I found out that most of the song files out there were written for the Orchestra-85/90, (85 is the Model 1 version) which has different hardware. First of all, it uses 2 IO ports (0xD5 and 0xD9) for 2 DACs and 2 stereo channels. Also, there is something different about the way the audio PCM is formatted - it's not PCM. If you take a dump of the data written to the ports and dump it into Audacity, you will find it sounds correct when imported as signed 8 bit PCM. But for signed, you need to take the 2's complement - invert the MSB and subtract 1. But, I found evidence to support the Orchestra-85/90 was still using R-2R resistors. How is the hardware doing the subtraction?&lt;br /&gt;
&lt;br /&gt;
I did some research, but was unable to find any DAC IC that took an 8-channel signed 2s complement inputs. It turns out that the audio data is probably not unsigned after all; it's probably &amp;quot;offset binary&amp;quot; which is the same process as 2's complement but you don't subtract 1, you just invert the MSB. That's easy.&lt;br /&gt;
&lt;br /&gt;
Since I didn't feel like wiring up a bunch of resistors, I looked for a parallel DAC chip instead, which should be more accurate anyway. I settled on the AD7302, which is a 2 channel, 8 bit DAC. After a sample order from Analog Devices, I had one. Basically, the whole ADC is now on this one chip instead of a bunch of resistor packs and latches. For address decoding, I decided to use a GAL16v8 chip, since I had good luck using another one on the RS232 build. The GAL can also do the needed inversion of D7. So, this boils down the entire Orchestra-85 down to 2 chips, and 2 RC low pass filters. The solution fits on a tiny proto-pcb, neatly between the floppy controller and top boards.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=Electronic_Time_Capsule&amp;diff=579</id>
		<title>Electronic Time Capsule</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=Electronic_Time_Capsule&amp;diff=579"/>
		<updated>2020-05-20T23:08:48Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: /* envilog1 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:Eagle.png|thumb]]&lt;br /&gt;
This is a board with a bme280 pressure, humidity and temperature sensor, a ds3232m realtime clock, and 4MB of flash memory. The clock wakes up an atmega328p, it takes readings and stores them, and then goes back to sleep. A low dropout, low quiescent current voltage regulator provides 3.3v power to the board from an external battery. &lt;br /&gt;
&lt;br /&gt;
I built this to be buried inside a time capsule (though it has plenty of other uses), so it has to be able to survive freezing temperatures, and should last at least 5 years without maintenance. A quick calculation of current draw on my prototype says that it should be able to run for about 15 years on AA batteries, and there is plenty of room in the flash memory for hourly measurements (15ish years worth).&lt;br /&gt;
&lt;br /&gt;
Over 10 years, the board is calculated to use &amp;lt;pre&amp;gt;88000hr*((20mA+4)*(.5/3600)+.35mA*(57/3600)+.006mA) = 1309 mAh&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Unit Status==&lt;br /&gt;
===envilog2===&lt;br /&gt;
buried in 2018 time 5 year capsule (started/sealed WC 42,hourly), lithium batteries&lt;br /&gt;
===envilog1===&lt;br /&gt;
On deck in tupperware outside house, started Jan 2019 (hourly), lithium batteries&lt;br /&gt;
* Checked at July 2019 Winter Camp party, status ok.&lt;br /&gt;
* Checked Jan 26, 2020, status OK. Left inside for few days to dry after finding the tupperware cracked. Time was about 65 seconds behind EST.&lt;br /&gt;
* Checked May 20, 2020 - after a heavy rainstorm the past few weeks, I found the tupperware cracked and the unit submerged in about an inch of water. All batteries are dead, although I do not think water reacted quickly with the lithium as I see no melting of the plastic case. After a wash in acetone and brake cleaner, most of the corrosion was removed, and I was able to power up the unit and retrieve the data out the serial port. The water first shorted the trigger pin for the wakeup, before apparently draining the batteries and causing the unit to reboot several times. Interestingly, at these times the on-board humidity sensor recorded &amp;quot;100.00%&amp;quot;&lt;br /&gt;
&lt;br /&gt;
===envilog5===&lt;br /&gt;
At Jan trailer, started Jul 2019, hourly&lt;br /&gt;
&lt;br /&gt;
===envilog3===&lt;br /&gt;
buried in 2019 time 5 year capsule (started/sealed WC 43,hourly), alkaline batteries&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
* [[File:Envilog-pcb-files.zip]] - The eagle and gerber files needed to make the boards&lt;br /&gt;
* [[File:Envilog-arduino-code.zip]] - The arduino program that runs on the board. Additional board hardware and libraries listed below are needed also.&lt;br /&gt;
&lt;br /&gt;
==Parts List==&lt;br /&gt;
* BME280 board measuring temperature, humidity and pressure.&lt;br /&gt;
* DS3231M measuring time, and temperature&lt;br /&gt;
* Optional voltage divider to measure battery voltage&lt;br /&gt;
* MCP1703AT-3302E/MB - 3.3V voltage low dropout linear regulator&lt;br /&gt;
* ATMEGA328P-AU&lt;br /&gt;
* Winbond 25q32fvsig&lt;br /&gt;
&lt;br /&gt;
==Interface Description==&lt;br /&gt;
[[File:Envilog-boardoutline.png|thumb|The board layout]]&lt;br /&gt;
The board contains 4 dip switches, which pretty much work independently, and one button.&lt;br /&gt;
The switches should be labeled 1-4, with 1 being closest to the center of the board. The off position of the switch has the handle toward the 3 indicator LEDs, and the on position is towards the button. &lt;br /&gt;
&lt;br /&gt;
The functions of the switches are:&lt;br /&gt;
* Switch 1 (on) - Enters the clock setting procedure. The clock is set using only the single button, pressed a number of times to set the date and time. '''You can only set the clock when the board is first connected to power or reset'''. &lt;br /&gt;
* Switch 2 (on) - '''Erases the flash chip.''' The flash must be empty to write new data from the beginning. An extra button press is needed to confirm you really want to do this. If the clock set switch is also on, then you will set the clock first, then erase the flash. '''You can only erase the flash when the board is first connected to power or reset'''.  This helps avoid doing it accidentally. &lt;br /&gt;
* Switch 3 (on) - Dumps the flash. Over serial, the board will output a tab-formatted table of the measurements that have been recorded. The dump will continue until either the entire chip has been read (including blank areas), or Switch 3 has been turned off. To start a dump, turn switch 3 on and then either wait for the RTC to wake up the system, or wake it up yourself with a single press of the button.&lt;br /&gt;
&lt;br /&gt;
* Switch 4 (on) - Blinks a green light every time the 328 powers up. Turn off to save power.&lt;br /&gt;
&lt;br /&gt;
The LED functions are, in general:&lt;br /&gt;
* Green - Status - Blinks when something happens / has happened&lt;br /&gt;
* Red - Working - Turns on when user input is acknowledged &lt;br /&gt;
* Yellow - Waiting for user input&lt;br /&gt;
&lt;br /&gt;
==Software Libraries used==&lt;br /&gt;
The following software was used in the program for this device:&lt;br /&gt;
* Arduino MiniCore hardware package. The board is programmed like a normal arduino board with the arduino bootloader, but this package makes it easier to get a bare 328 chip working with many of the power-heavy features disabled.&lt;br /&gt;
* avr/power, avr/sleep, Wire, SPI arduino libraries, included with the arduino IDE.&lt;br /&gt;
* https://github.com/Marzogh/SPIMemory - for using the external flash memory chip that stores the measurements&lt;br /&gt;
* The SparkFun BME280 library - For configuring the BME280 sensor&lt;br /&gt;
&lt;br /&gt;
==Build / Operating Instructions==&lt;br /&gt;
* Make the board and assemble the components onto it.  Everything is surface mount except the sensor board, which is an Aliexpress special. &lt;br /&gt;
* Flash the 328 on the board with the Arduino MiniCore bootloader. Make sure that the BOD is disabled, and the 8 mhz clock speed is selected. I tried 1 mhz as well, but found that the flash chip did not work well at this speed. The 328 can be programmed with ICSP, just like flashing a normal Arduino. The reset line, VCC and ground pins are brought out on the outside 5 pin header. The remaining SPI pins needed for ICP can be connected to either some test points on the board, or by clipping onto the legs of the flash chip.&lt;br /&gt;
* The board can now be programmed with a usb-serial adapter like any other arduino board. The tx and rx pins on the board connect to the rx and tx pins on the usb-serial device. The reset pin on the board connects through a 0.1uf cap to the DTR pin on the usb-serial, because I didn't include one on the board. Upload the main program.&lt;br /&gt;
* Push switches 1 (clock set) , 2 (flash erase) and 4 (status) on, and connect the board to a serial monitor and power (between 4 and 15 volts).&lt;br /&gt;
* Use the single button to set the clock following the serial terminal instructions. For example, to set the first parameter, the year, to 2018, press the button repeatedly until 18 is shown, then wait 2 seconds to continue. &lt;br /&gt;
* Erase the flash by pressing the button once when prompted.&lt;br /&gt;
* Return all dip switches to off position. Switch 4 can be left on, which will blink the green led whenever the 328 is powered up, but can be turned off to save power.&lt;br /&gt;
* After data is collected, turn switch 3 on and press the button. The data will be output over serial in the form of a table.&lt;br /&gt;
&lt;br /&gt;
==Example Output==&lt;br /&gt;
This is what it looks like when running / dumping measurements in the serial terminal. To create a dump, move switch 3 to on position and press button once. Data is dumped out the TXD pin at 115200 baud.&lt;br /&gt;
&amp;lt;pre&amp;gt;TIME CAPSULE LONG DURATION ENVIROMENTAL LOGGER - Alan J. WIlson 2018&lt;br /&gt;
Current time: 18/09/05 23:29:49&lt;br /&gt;
Flash capacity: 4194304 bytes, 131072 frames&lt;br /&gt;
Frame size: 32&lt;br /&gt;
Next ram index: 457&lt;br /&gt;
&lt;br /&gt;
Current time: 18/09/05 23:29:49&lt;br /&gt;
Cycle: 0&lt;br /&gt;
Index: 457&lt;br /&gt;
Battery ADC: 0&lt;br /&gt;
DS3231 Temperature (C): 22.75&lt;br /&gt;
BME280 Temperature (C): 22.70&lt;br /&gt;
BME280 Pressure   (Pa): 99898.25&lt;br /&gt;
BME280 Humidity    (%): 51.34&lt;br /&gt;
Write OK&lt;br /&gt;
&lt;br /&gt;
FLASH MEMORY DATA DUMP FOLLOWS&lt;br /&gt;
Index	Cycle	Year	Month	Day	Hour	Minute	Second	DS3231 Temperature (C)	Temperature (C)	Pressure (Pa)	Humidity (%)	Battery (10-bit ADC)&lt;br /&gt;
0	0	18	9	5	14	44	45	22.50	22.22	99527.77	45.77	0	&lt;br /&gt;
1	1	18	9	5	14	44	45	22.50	22.19	99525.35	46.32	0	&lt;br /&gt;
2	2	18	9	5	14	44	58	22.75	22.21	99520.82	46.35	0	&lt;br /&gt;
3	3	18	9	5	14	45	10	22.75	22.19	99530.60	46.18	0	&lt;br /&gt;
4	4	18	9	5	14	46	10	22.50	22.23	99526.82	46.55	0	&lt;br /&gt;
5	5	18	9	5	14	47	10	22.25	22.29	99525.55	46.44	0	&lt;br /&gt;
6	6	18	9	5	14	48	10	22.25	22.02	99528.21	47.05	0	&lt;br /&gt;
7	7	18	9	5	14	49	10	22.00	21.90	99535.94	47.95	0	&lt;br /&gt;
8	8	18	9	5	14	50	10	22.00	21.79	99527.93	48.24	0	&lt;br /&gt;
9	9	18	9	5	14	51	10	22.00	21.75	99536.94	48.41	0	&lt;br /&gt;
10	10	18	9	5	14	52	10	22.00	21.74	99537.06	48.59	0	&lt;br /&gt;
11	11	18	9	5	14	53	10	22.00	21.74	99530.03	48.66	0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Serial Console (12/08/2018)==&lt;br /&gt;
In the original version of the software, I use the single button to do most of the setup. This can be very difficult, because the board and button are tiny, and pressing it is required many times, especially for setting the time. Since I implement a simple serial console tool into many of my other projects, I figured it would be a good idea to have one here too, since it is probably fairly likely that you will have the device hooked up to a computer when setting it up anyways.&lt;br /&gt;
&lt;br /&gt;
The serial console I made accepts commands one line at a time, but there is a timeout so that the system does not get stuck waiting forever for a character that never comes. This makes it excellent for programs that send data one line at a time, like the Arduino IDE, but it will not work that well for standard dumb terminal unless you can type very quickly.&lt;br /&gt;
&lt;br /&gt;
The console accepts commands in the form of a single letter, followed by a floating point number, and finally the newline character. For example:&lt;br /&gt;
&amp;lt;pre&amp;gt;y18.00\n&amp;lt;/pre&amp;gt;&lt;br /&gt;
The letter gets passed along as the command, and the number gets passed along as the parameter. It doesn't have to stay floating point either, it will just get converted into an integer or whatever you need. &lt;br /&gt;
&lt;br /&gt;
To use the serial console, set your terminal to 9600 baud and reset the board. Then enter '''e'''. I found that the baudrate of the board's normal 115200 is too high to reliably accept serial input, but works fine for outputing data at high speed during normal operation of the board. &lt;br /&gt;
&lt;br /&gt;
The following table documents all the commands and their functions:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Command !! Parameter !! Description !! Example&lt;br /&gt;
|-&lt;br /&gt;
| e || any || Enters the Serial control mode || &lt;br /&gt;
|-&lt;br /&gt;
| t || 1 - start listing&amp;lt;br&amp;gt;0 - stop listing || Lists continuously the current time. This is helpful when setting it exactly to another source. || t1 - lists time&lt;br /&gt;
|-&lt;br /&gt;
| a || 1 - start listing&amp;lt;br&amp;gt;0 - stop listing || Lists continuously the flash data. This is much slower than using the switch #3 because it is only at 9600 baud. || a1&lt;br /&gt;
|-&lt;br /&gt;
| r || any || Displays the DS3232M RTC registers ||&lt;br /&gt;
|-&lt;br /&gt;
| y || 2 digit year || Sets the year of the RTC || y18 - sets to year 2018&lt;br /&gt;
|-&lt;br /&gt;
| m || 2 digit month || Sets the month of the RTC || m12 - sets to month December&lt;br /&gt;
|-&lt;br /&gt;
| d || 2 digit day || Sets the day of month of the RTC || d8 - sets to day 8&lt;br /&gt;
|-&lt;br /&gt;
| H || 2 digit hour || Sets the hour of the RTC || H32 - sets to hour 1PM&lt;br /&gt;
|-&lt;br /&gt;
| M || 2 digit minute || Sets the minute of the RTC || M32 - sets to minute 32&lt;br /&gt;
|-&lt;br /&gt;
| S || 2 digit second || Sets the seconds of the RTC || S0 - sets to second 0&lt;br /&gt;
|-&lt;br /&gt;
| F || value 12345 || Erases the flash memory || F12345&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Octave Plotter==&lt;br /&gt;
If the output table is changed from tab formatting to CSV (you can do this with Notepad++ or some other text editor), you can use this script in GNU Octave (an open source MatLab alternative) to make a handy plot of the data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
clear;&lt;br /&gt;
clc;&lt;br /&gt;
clf;&lt;br /&gt;
&lt;br /&gt;
raw = load(&amp;quot;j.csv&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
%frame format:&lt;br /&gt;
%[ time, ds3231T, temp, pressure, humidity ]&lt;br /&gt;
&lt;br /&gt;
i = 0;&lt;br /&gt;
while(i&amp;lt;length(raw(:,1)))&lt;br /&gt;
  i++;&lt;br /&gt;
  frame(i,:)=[datenum([2000+raw(i,3),raw(i,4),raw(i,5),raw(i,6),raw(i,7),raw(i,8)]),raw(i,9),raw(i,10),raw(i,11),raw(i,12)];&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
figure(1)&lt;br /&gt;
ha(1)=subplot(3,1,1);&lt;br /&gt;
hold on;&lt;br /&gt;
title(strcat(&amp;quot;Enviroment data: &amp;quot;,datestr(frame(1,1),&amp;quot; mmm 'yy &amp;quot;),&amp;quot; to&amp;quot;,datestr(frame(i,1),&amp;quot; mmm 'yy &amp;quot;)));&lt;br /&gt;
plot(frame(:,1),frame(:,2)*(9/5)+32);&lt;br /&gt;
plot(frame(:,1),frame(:,3)*(9/5)+32);&lt;br /&gt;
datetick(&amp;quot;x&amp;quot;, &amp;quot;mmm/yy&amp;quot;);&lt;br /&gt;
grid on;&lt;br /&gt;
xlabel(&amp;quot;Time&amp;quot;);&lt;br /&gt;
ylabel(&amp;quot;Temperature (F)&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
ha(2)=subplot(3,1,2);&lt;br /&gt;
plot(frame(:,1),frame(:,4));&lt;br /&gt;
datetick(&amp;quot;x&amp;quot;, &amp;quot;mmm/yy&amp;quot;);&lt;br /&gt;
grid on;&lt;br /&gt;
xlabel(&amp;quot;Time&amp;quot;);&lt;br /&gt;
ylabel(&amp;quot;Pressure (Pa)&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
ha(3)=subplot(3,1,3);&lt;br /&gt;
plot(frame(:,1),frame(:,5));&lt;br /&gt;
datetick(&amp;quot;x&amp;quot;, &amp;quot;mmm/yy&amp;quot;);&lt;br /&gt;
grid on;&lt;br /&gt;
xlabel(&amp;quot;Time&amp;quot;);&lt;br /&gt;
ylabel(&amp;quot;Humidity (%)&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
linkaxes(ha, &amp;quot;x&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Details (hackaday.io)==&lt;br /&gt;
This project has been ongoing for a few months now, but I, like many people, don't document projects as well / as often as I should. This project represents a lot of firsts for me, such as the first time working with low power, first time ever soldering SMD components, and only the second project where I have designed a PCB. Most of my projects consist of either breadboards, prototyping boards, or some bizarre freeform/deadbug. I am not an electrical engineer or programmer, just a self taught hobbyist, which does sometimes mean that I have no idea if I am doing something &amp;quot;the right way.&amp;quot; I want to keep my designs simple, using components that are well known, and programming no more complex than Arduino.  With that in mind, the following ramblings explain my thoughts in building this project.&lt;br /&gt;
&lt;br /&gt;
Since this was designed to be buried in a time capsule, the first order of business is how to get power. I had never worked with any low power sleep modes on microcontrollers, but I have plenty of devices that last for years with just batteries. Plenty of projects here on HAD that use a coin cell, but i'd be hard pressed to get 5 years out of one of those. Sounds like a good solution is normal AA batteries. I will use enough so that even when the battery is near dead, the voltage will still be high enough to power the device. It also gives me a bit of extra room to not worry so much about squeezing every last drop of juice out the batteries.&lt;br /&gt;
&lt;br /&gt;
===The Microcontroller===&lt;br /&gt;
It's an ATmega328, nothing special. I thought about using an ATtiny, but since both have the same sleep power requirements, I kept the 328 for some extra pins and more ram, in case I wanted to expand the system a little or do some kind of heavier processing. Yes, this is essentially the same thing as building your own Arduino, but it's simple and it works.&lt;br /&gt;
&lt;br /&gt;
The important thing is to be able to get the 328 into and out of sleep mode. Having never experimented with this mode before, Nick Gammon's tutorial taught me most of what I needed to know. I want this board to run for a long time, so everything must go, like the ADCs, and brownout detection. This brings the sleep current down to an impressive 0.1 uA. I'll re-enable any periperials I need upon waking up.&lt;br /&gt;
&lt;br /&gt;
To wake up the chip, I'll use one of the interrupt pins.&lt;br /&gt;
&lt;br /&gt;
===The Clock===&lt;br /&gt;
I'm using a real-time clock chip to wake up the 328. It's much more accurate than an internal watchdog timer, and lets me add a timestamp to my measurements. I've used the DS3232 rtc before in the form of some inexpensive modules, so I decided to use them for this project as well. This chip contains a real time clock with temperature compensation. The clock accuracy is supposed to be quite good, on the order of a few PPM.&lt;br /&gt;
&lt;br /&gt;
The DS3231 can be powered exclusively through the Vbatt pin instead of the normal Vcc pin. This saves some power according to the datasheet, but also increases the temperature compensation time to once a minute rather than every few seconds. For my purposes, this is fine. &lt;br /&gt;
For interfacing to the 328, 3 pins are used. Two are the i2c pins, and the third is the INT pin. The DS3231 has an alarm function, which can trigger this pin to go LOW when tripped. Connected to an interrupt pin on the 328, it is used to wake up the microcontroller from deep sleep. The 328 then resets the alarm, and reads out a timestamp over i2c. Alarm functions are not inplemented in most of the Arduino libraries I have found, so I wrote a simple one to make working directly with the RTC easier (but it only uses one of the two alarms).&lt;br /&gt;
&lt;br /&gt;
My initial testing with RTC and microcontroller seemed promising, with the circuit waking up and blinking an LED once per minute as I had set it to do. However, when placing the setup in the freezer, the clock was more than a minute off after only a few hours. This is probably because the DS3231s I got from eBay were counterfeit. They work fine for room temperature operation (I have a counterfeit one in a nixie clock I made and it keeps accurate time) but my board needs to survive in freezing temperatures. A few genuine DS3232M from Maxim solved the problem, and my boards now keep accurate time even in the freezer. The M version is a smaller package, and is very slightly less accurate but is supposed to be more durable, using a MEMS oscillator instead of a crystal. The 3232 part number includes some extra battery backed general purpose memory, which I thought might be useful but is still not used yet in my design. The new chip takes the same commands as the old one, so no code changes were needed.&lt;br /&gt;
&lt;br /&gt;
===The Memory===&lt;br /&gt;
Initially, I tried using a 128kb 24lc1025 EEprom as the main storage space, but quickly found it would run out of space in less than the 5 year design life, so I would need to do something else.&lt;br /&gt;
&lt;br /&gt;
While working on this project, I came across the Pearl cave project, where the same idea is used to make environmental sensors for monitoring caves. Their solution was to use an SD card, but it was found that different SD cards used different amounts of power, and that the cards did not like being turned on and off. Rather than an SD card, I thought about using a flash memory chip, maybe in the same way SPIFFS is used in my esp8266 projects. It would also be soldered to the board, with no spring connectors that could corrode or come loose. I settled on the Winbond 25q32fvsig, a 4mbyte flash chip. This would have plenty of room for years of measurements, and also has a sleep mode which takes the power down to a sub-microamp level. I used Marzogh's SPIMemory library for this, which takes care of most of the work in interfacing with the flash. When working with these chips, I found I could not write my 17 byte long measurement frame sequentially like I could with the EEPROM. Increasing the frame size to 32 bytes aligned them with the flash pages and I was able to read and write without errors, as well as fit a few extra measurements in. Now I can store 128k samples, instead of just 128k bytes. At one sampling per hour, this is almost 15 years of storage.&lt;br /&gt;
&lt;br /&gt;
On the board in my picture, you can spot my mistake in the board design. I accidentally connected the reset and write protect pins to GND instead of VCC. This means that the chip is in a constant reset state, and cannot be read or written. With a Dremel, I carefully cut the traces. The reset pin is right next to the VCC pin, so a little excess solder is enough to bridge them. A jumper of thin wire across the top of the chip brings the write protect pin positive as well. This error is fixed in the released version provided in Files. &lt;br /&gt;
&lt;br /&gt;
Initially, all I wanted to measure was temperature. However, that's not a lot of payout for 5 years work. Since the time capsule supposed to be a sealed PVC tube, I thought that it would be interesting to see what the internal pressure was like.  The BME280 can do both temperature and pressure, and also humidity. It also has a low power sleep mode, and communicates over i2c. Since I've never soldered a part that small, I cheated and used a premade breakout board module, which fits on to the i2c header behind the main header.  The header allows for more expansion also, in addition to or if another i2c sensor is to be used.&lt;br /&gt;
&lt;br /&gt;
===Power===&lt;br /&gt;
With the selection of a 3.3v memory chip and sensor, I was limited to running the board at 3.3v instead of the 5v I had originally planned. I didn't think that two AA batteries or a coin cell could last long enough and still be enough voltage to power the board after it lost some charge. Since I care more about reliability with no maintenance, and ton't have too much of a size constraint on the size of the batteries. I thought it would be better to run the components at some stable voltage that did not drop with the batteries (though I recognize that this will increase idle current draw). I could have run the 328 at 4.5v with 3 AAs, but I would still need an additional 3.3v supply and some kind of logic level  conversion. The pearl cave project had the same problem, and solved it by using a simple linear regulator. However, the regular has to be a low quiesent current type, which drew little current on it's own, unlike an lm1117 found on most of the cheap Arduino boards. It took me a while to find a version that had an input voltage greater than 5.5v (in case I decided to use more than 3 cells), and still had good performance. Both the Mcp1703 and HT7773 are good candidates, and come in a sot89 package with the same pinout. I happened to have some of the through hole HT regulators on hand, and used them while I waited for some eBay mcp1703s to arrive. Unfortunately, the eBay mcp1703s turned out to be counterfeit also, and did not meet the claimed quiescent current listed in the datasheet. The current was instead around 70 microamps, more than 10x what I had planned. Again, some genuine samples from Microchip fixed the problem. Interestingly, the HT chips which are produced by a Chinese company and were bought overseas from an unknown seller performed perfectly.&lt;br /&gt;
&lt;br /&gt;
===Operator interface===&lt;br /&gt;
Only seven IO pins on the 328 were being used thus far. Two for i2c, one for the clock wake up pin, and 4 for the SPI flash memory. I added an eighth, a voltage divider of high value resistors on an ADC pin to measure the battery voltage, which has not been fitted yet. But that still leaves lots of other pins open on the 328. I connected four of them to a small 4 position dip switch that I had on hand, three more to colored LEDs, and a button switch to the other interrupt pin. In this way, the board can still be verified working after disconnecting from the programmer. Otherwise, I can't tell if the board is working fine or a brick. The button enables waking up the 328 without waiting for the clock alarm to trip, which could take an hour on the slow setting. The dip switches allow for selecting different models, like setting the clock accurately to the second, or erasing the flash to start over, or dumping the flash contents over serial.&lt;br /&gt;
&lt;br /&gt;
I decided to include a serial interface header on one side, so it will be easier to get the data off of the board when the time comes. I'll use the Arduino bootloader so I can program the chip over serial, since I have many more usb serial adapters than AVR programmers, and the cheap AVR programmers I do have only work 5% of the time anyways. VCC, GND, Tx, Rx, and the 328 /RST pins are brought out on the 5 pin header.&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=Electronic_Time_Capsule&amp;diff=578</id>
		<title>Electronic Time Capsule</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=Electronic_Time_Capsule&amp;diff=578"/>
		<updated>2020-05-20T23:07:59Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: /* envilog1 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:Eagle.png|thumb]]&lt;br /&gt;
This is a board with a bme280 pressure, humidity and temperature sensor, a ds3232m realtime clock, and 4MB of flash memory. The clock wakes up an atmega328p, it takes readings and stores them, and then goes back to sleep. A low dropout, low quiescent current voltage regulator provides 3.3v power to the board from an external battery. &lt;br /&gt;
&lt;br /&gt;
I built this to be buried inside a time capsule (though it has plenty of other uses), so it has to be able to survive freezing temperatures, and should last at least 5 years without maintenance. A quick calculation of current draw on my prototype says that it should be able to run for about 15 years on AA batteries, and there is plenty of room in the flash memory for hourly measurements (15ish years worth).&lt;br /&gt;
&lt;br /&gt;
Over 10 years, the board is calculated to use &amp;lt;pre&amp;gt;88000hr*((20mA+4)*(.5/3600)+.35mA*(57/3600)+.006mA) = 1309 mAh&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Unit Status==&lt;br /&gt;
===envilog2===&lt;br /&gt;
buried in 2018 time 5 year capsule (started/sealed WC 42,hourly), lithium batteries&lt;br /&gt;
===envilog1===&lt;br /&gt;
On deck in tupperware outside house, started Jan 2019 (hourly), lithium batteries&lt;br /&gt;
* Checked at July 2019 Winter Camp party, status ok.&lt;br /&gt;
* Checked Jan 26, 2020, status OK. Left inside for few days to dry after finding the tupperware cracked. Time was about 65 seconds behind EST.&lt;br /&gt;
* Checked May 20, 2020 - after a heavy rainstorm the past few weeks, I found the tupperware cracked and the unit submerged in about an inch of water. All batteries are dead, although I do not think water reacted quickly with the lithium as I see no melting of the plastic case.&lt;br /&gt;
After a wash in acetone and brake cleaner, most of the corrosion was removed, and I was able to power up the unit and retrieve the data out the serial port. The water first shorted the trigger pin for the wakeup, before apparently draining the batteries and causing the unit to reboot several times.&lt;br /&gt;
&lt;br /&gt;
===envilog5===&lt;br /&gt;
At Jan trailer, started Jul 2019, hourly&lt;br /&gt;
&lt;br /&gt;
===envilog3===&lt;br /&gt;
buried in 2019 time 5 year capsule (started/sealed WC 43,hourly), alkaline batteries&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
* [[File:Envilog-pcb-files.zip]] - The eagle and gerber files needed to make the boards&lt;br /&gt;
* [[File:Envilog-arduino-code.zip]] - The arduino program that runs on the board. Additional board hardware and libraries listed below are needed also.&lt;br /&gt;
&lt;br /&gt;
==Parts List==&lt;br /&gt;
* BME280 board measuring temperature, humidity and pressure.&lt;br /&gt;
* DS3231M measuring time, and temperature&lt;br /&gt;
* Optional voltage divider to measure battery voltage&lt;br /&gt;
* MCP1703AT-3302E/MB - 3.3V voltage low dropout linear regulator&lt;br /&gt;
* ATMEGA328P-AU&lt;br /&gt;
* Winbond 25q32fvsig&lt;br /&gt;
&lt;br /&gt;
==Interface Description==&lt;br /&gt;
[[File:Envilog-boardoutline.png|thumb|The board layout]]&lt;br /&gt;
The board contains 4 dip switches, which pretty much work independently, and one button.&lt;br /&gt;
The switches should be labeled 1-4, with 1 being closest to the center of the board. The off position of the switch has the handle toward the 3 indicator LEDs, and the on position is towards the button. &lt;br /&gt;
&lt;br /&gt;
The functions of the switches are:&lt;br /&gt;
* Switch 1 (on) - Enters the clock setting procedure. The clock is set using only the single button, pressed a number of times to set the date and time. '''You can only set the clock when the board is first connected to power or reset'''. &lt;br /&gt;
* Switch 2 (on) - '''Erases the flash chip.''' The flash must be empty to write new data from the beginning. An extra button press is needed to confirm you really want to do this. If the clock set switch is also on, then you will set the clock first, then erase the flash. '''You can only erase the flash when the board is first connected to power or reset'''.  This helps avoid doing it accidentally. &lt;br /&gt;
* Switch 3 (on) - Dumps the flash. Over serial, the board will output a tab-formatted table of the measurements that have been recorded. The dump will continue until either the entire chip has been read (including blank areas), or Switch 3 has been turned off. To start a dump, turn switch 3 on and then either wait for the RTC to wake up the system, or wake it up yourself with a single press of the button.&lt;br /&gt;
&lt;br /&gt;
* Switch 4 (on) - Blinks a green light every time the 328 powers up. Turn off to save power.&lt;br /&gt;
&lt;br /&gt;
The LED functions are, in general:&lt;br /&gt;
* Green - Status - Blinks when something happens / has happened&lt;br /&gt;
* Red - Working - Turns on when user input is acknowledged &lt;br /&gt;
* Yellow - Waiting for user input&lt;br /&gt;
&lt;br /&gt;
==Software Libraries used==&lt;br /&gt;
The following software was used in the program for this device:&lt;br /&gt;
* Arduino MiniCore hardware package. The board is programmed like a normal arduino board with the arduino bootloader, but this package makes it easier to get a bare 328 chip working with many of the power-heavy features disabled.&lt;br /&gt;
* avr/power, avr/sleep, Wire, SPI arduino libraries, included with the arduino IDE.&lt;br /&gt;
* https://github.com/Marzogh/SPIMemory - for using the external flash memory chip that stores the measurements&lt;br /&gt;
* The SparkFun BME280 library - For configuring the BME280 sensor&lt;br /&gt;
&lt;br /&gt;
==Build / Operating Instructions==&lt;br /&gt;
* Make the board and assemble the components onto it.  Everything is surface mount except the sensor board, which is an Aliexpress special. &lt;br /&gt;
* Flash the 328 on the board with the Arduino MiniCore bootloader. Make sure that the BOD is disabled, and the 8 mhz clock speed is selected. I tried 1 mhz as well, but found that the flash chip did not work well at this speed. The 328 can be programmed with ICSP, just like flashing a normal Arduino. The reset line, VCC and ground pins are brought out on the outside 5 pin header. The remaining SPI pins needed for ICP can be connected to either some test points on the board, or by clipping onto the legs of the flash chip.&lt;br /&gt;
* The board can now be programmed with a usb-serial adapter like any other arduino board. The tx and rx pins on the board connect to the rx and tx pins on the usb-serial device. The reset pin on the board connects through a 0.1uf cap to the DTR pin on the usb-serial, because I didn't include one on the board. Upload the main program.&lt;br /&gt;
* Push switches 1 (clock set) , 2 (flash erase) and 4 (status) on, and connect the board to a serial monitor and power (between 4 and 15 volts).&lt;br /&gt;
* Use the single button to set the clock following the serial terminal instructions. For example, to set the first parameter, the year, to 2018, press the button repeatedly until 18 is shown, then wait 2 seconds to continue. &lt;br /&gt;
* Erase the flash by pressing the button once when prompted.&lt;br /&gt;
* Return all dip switches to off position. Switch 4 can be left on, which will blink the green led whenever the 328 is powered up, but can be turned off to save power.&lt;br /&gt;
* After data is collected, turn switch 3 on and press the button. The data will be output over serial in the form of a table.&lt;br /&gt;
&lt;br /&gt;
==Example Output==&lt;br /&gt;
This is what it looks like when running / dumping measurements in the serial terminal. To create a dump, move switch 3 to on position and press button once. Data is dumped out the TXD pin at 115200 baud.&lt;br /&gt;
&amp;lt;pre&amp;gt;TIME CAPSULE LONG DURATION ENVIROMENTAL LOGGER - Alan J. WIlson 2018&lt;br /&gt;
Current time: 18/09/05 23:29:49&lt;br /&gt;
Flash capacity: 4194304 bytes, 131072 frames&lt;br /&gt;
Frame size: 32&lt;br /&gt;
Next ram index: 457&lt;br /&gt;
&lt;br /&gt;
Current time: 18/09/05 23:29:49&lt;br /&gt;
Cycle: 0&lt;br /&gt;
Index: 457&lt;br /&gt;
Battery ADC: 0&lt;br /&gt;
DS3231 Temperature (C): 22.75&lt;br /&gt;
BME280 Temperature (C): 22.70&lt;br /&gt;
BME280 Pressure   (Pa): 99898.25&lt;br /&gt;
BME280 Humidity    (%): 51.34&lt;br /&gt;
Write OK&lt;br /&gt;
&lt;br /&gt;
FLASH MEMORY DATA DUMP FOLLOWS&lt;br /&gt;
Index	Cycle	Year	Month	Day	Hour	Minute	Second	DS3231 Temperature (C)	Temperature (C)	Pressure (Pa)	Humidity (%)	Battery (10-bit ADC)&lt;br /&gt;
0	0	18	9	5	14	44	45	22.50	22.22	99527.77	45.77	0	&lt;br /&gt;
1	1	18	9	5	14	44	45	22.50	22.19	99525.35	46.32	0	&lt;br /&gt;
2	2	18	9	5	14	44	58	22.75	22.21	99520.82	46.35	0	&lt;br /&gt;
3	3	18	9	5	14	45	10	22.75	22.19	99530.60	46.18	0	&lt;br /&gt;
4	4	18	9	5	14	46	10	22.50	22.23	99526.82	46.55	0	&lt;br /&gt;
5	5	18	9	5	14	47	10	22.25	22.29	99525.55	46.44	0	&lt;br /&gt;
6	6	18	9	5	14	48	10	22.25	22.02	99528.21	47.05	0	&lt;br /&gt;
7	7	18	9	5	14	49	10	22.00	21.90	99535.94	47.95	0	&lt;br /&gt;
8	8	18	9	5	14	50	10	22.00	21.79	99527.93	48.24	0	&lt;br /&gt;
9	9	18	9	5	14	51	10	22.00	21.75	99536.94	48.41	0	&lt;br /&gt;
10	10	18	9	5	14	52	10	22.00	21.74	99537.06	48.59	0	&lt;br /&gt;
11	11	18	9	5	14	53	10	22.00	21.74	99530.03	48.66	0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Serial Console (12/08/2018)==&lt;br /&gt;
In the original version of the software, I use the single button to do most of the setup. This can be very difficult, because the board and button are tiny, and pressing it is required many times, especially for setting the time. Since I implement a simple serial console tool into many of my other projects, I figured it would be a good idea to have one here too, since it is probably fairly likely that you will have the device hooked up to a computer when setting it up anyways.&lt;br /&gt;
&lt;br /&gt;
The serial console I made accepts commands one line at a time, but there is a timeout so that the system does not get stuck waiting forever for a character that never comes. This makes it excellent for programs that send data one line at a time, like the Arduino IDE, but it will not work that well for standard dumb terminal unless you can type very quickly.&lt;br /&gt;
&lt;br /&gt;
The console accepts commands in the form of a single letter, followed by a floating point number, and finally the newline character. For example:&lt;br /&gt;
&amp;lt;pre&amp;gt;y18.00\n&amp;lt;/pre&amp;gt;&lt;br /&gt;
The letter gets passed along as the command, and the number gets passed along as the parameter. It doesn't have to stay floating point either, it will just get converted into an integer or whatever you need. &lt;br /&gt;
&lt;br /&gt;
To use the serial console, set your terminal to 9600 baud and reset the board. Then enter '''e'''. I found that the baudrate of the board's normal 115200 is too high to reliably accept serial input, but works fine for outputing data at high speed during normal operation of the board. &lt;br /&gt;
&lt;br /&gt;
The following table documents all the commands and their functions:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Command !! Parameter !! Description !! Example&lt;br /&gt;
|-&lt;br /&gt;
| e || any || Enters the Serial control mode || &lt;br /&gt;
|-&lt;br /&gt;
| t || 1 - start listing&amp;lt;br&amp;gt;0 - stop listing || Lists continuously the current time. This is helpful when setting it exactly to another source. || t1 - lists time&lt;br /&gt;
|-&lt;br /&gt;
| a || 1 - start listing&amp;lt;br&amp;gt;0 - stop listing || Lists continuously the flash data. This is much slower than using the switch #3 because it is only at 9600 baud. || a1&lt;br /&gt;
|-&lt;br /&gt;
| r || any || Displays the DS3232M RTC registers ||&lt;br /&gt;
|-&lt;br /&gt;
| y || 2 digit year || Sets the year of the RTC || y18 - sets to year 2018&lt;br /&gt;
|-&lt;br /&gt;
| m || 2 digit month || Sets the month of the RTC || m12 - sets to month December&lt;br /&gt;
|-&lt;br /&gt;
| d || 2 digit day || Sets the day of month of the RTC || d8 - sets to day 8&lt;br /&gt;
|-&lt;br /&gt;
| H || 2 digit hour || Sets the hour of the RTC || H32 - sets to hour 1PM&lt;br /&gt;
|-&lt;br /&gt;
| M || 2 digit minute || Sets the minute of the RTC || M32 - sets to minute 32&lt;br /&gt;
|-&lt;br /&gt;
| S || 2 digit second || Sets the seconds of the RTC || S0 - sets to second 0&lt;br /&gt;
|-&lt;br /&gt;
| F || value 12345 || Erases the flash memory || F12345&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Octave Plotter==&lt;br /&gt;
If the output table is changed from tab formatting to CSV (you can do this with Notepad++ or some other text editor), you can use this script in GNU Octave (an open source MatLab alternative) to make a handy plot of the data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
clear;&lt;br /&gt;
clc;&lt;br /&gt;
clf;&lt;br /&gt;
&lt;br /&gt;
raw = load(&amp;quot;j.csv&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
%frame format:&lt;br /&gt;
%[ time, ds3231T, temp, pressure, humidity ]&lt;br /&gt;
&lt;br /&gt;
i = 0;&lt;br /&gt;
while(i&amp;lt;length(raw(:,1)))&lt;br /&gt;
  i++;&lt;br /&gt;
  frame(i,:)=[datenum([2000+raw(i,3),raw(i,4),raw(i,5),raw(i,6),raw(i,7),raw(i,8)]),raw(i,9),raw(i,10),raw(i,11),raw(i,12)];&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
figure(1)&lt;br /&gt;
ha(1)=subplot(3,1,1);&lt;br /&gt;
hold on;&lt;br /&gt;
title(strcat(&amp;quot;Enviroment data: &amp;quot;,datestr(frame(1,1),&amp;quot; mmm 'yy &amp;quot;),&amp;quot; to&amp;quot;,datestr(frame(i,1),&amp;quot; mmm 'yy &amp;quot;)));&lt;br /&gt;
plot(frame(:,1),frame(:,2)*(9/5)+32);&lt;br /&gt;
plot(frame(:,1),frame(:,3)*(9/5)+32);&lt;br /&gt;
datetick(&amp;quot;x&amp;quot;, &amp;quot;mmm/yy&amp;quot;);&lt;br /&gt;
grid on;&lt;br /&gt;
xlabel(&amp;quot;Time&amp;quot;);&lt;br /&gt;
ylabel(&amp;quot;Temperature (F)&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
ha(2)=subplot(3,1,2);&lt;br /&gt;
plot(frame(:,1),frame(:,4));&lt;br /&gt;
datetick(&amp;quot;x&amp;quot;, &amp;quot;mmm/yy&amp;quot;);&lt;br /&gt;
grid on;&lt;br /&gt;
xlabel(&amp;quot;Time&amp;quot;);&lt;br /&gt;
ylabel(&amp;quot;Pressure (Pa)&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
ha(3)=subplot(3,1,3);&lt;br /&gt;
plot(frame(:,1),frame(:,5));&lt;br /&gt;
datetick(&amp;quot;x&amp;quot;, &amp;quot;mmm/yy&amp;quot;);&lt;br /&gt;
grid on;&lt;br /&gt;
xlabel(&amp;quot;Time&amp;quot;);&lt;br /&gt;
ylabel(&amp;quot;Humidity (%)&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
linkaxes(ha, &amp;quot;x&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Details (hackaday.io)==&lt;br /&gt;
This project has been ongoing for a few months now, but I, like many people, don't document projects as well / as often as I should. This project represents a lot of firsts for me, such as the first time working with low power, first time ever soldering SMD components, and only the second project where I have designed a PCB. Most of my projects consist of either breadboards, prototyping boards, or some bizarre freeform/deadbug. I am not an electrical engineer or programmer, just a self taught hobbyist, which does sometimes mean that I have no idea if I am doing something &amp;quot;the right way.&amp;quot; I want to keep my designs simple, using components that are well known, and programming no more complex than Arduino.  With that in mind, the following ramblings explain my thoughts in building this project.&lt;br /&gt;
&lt;br /&gt;
Since this was designed to be buried in a time capsule, the first order of business is how to get power. I had never worked with any low power sleep modes on microcontrollers, but I have plenty of devices that last for years with just batteries. Plenty of projects here on HAD that use a coin cell, but i'd be hard pressed to get 5 years out of one of those. Sounds like a good solution is normal AA batteries. I will use enough so that even when the battery is near dead, the voltage will still be high enough to power the device. It also gives me a bit of extra room to not worry so much about squeezing every last drop of juice out the batteries.&lt;br /&gt;
&lt;br /&gt;
===The Microcontroller===&lt;br /&gt;
It's an ATmega328, nothing special. I thought about using an ATtiny, but since both have the same sleep power requirements, I kept the 328 for some extra pins and more ram, in case I wanted to expand the system a little or do some kind of heavier processing. Yes, this is essentially the same thing as building your own Arduino, but it's simple and it works.&lt;br /&gt;
&lt;br /&gt;
The important thing is to be able to get the 328 into and out of sleep mode. Having never experimented with this mode before, Nick Gammon's tutorial taught me most of what I needed to know. I want this board to run for a long time, so everything must go, like the ADCs, and brownout detection. This brings the sleep current down to an impressive 0.1 uA. I'll re-enable any periperials I need upon waking up.&lt;br /&gt;
&lt;br /&gt;
To wake up the chip, I'll use one of the interrupt pins.&lt;br /&gt;
&lt;br /&gt;
===The Clock===&lt;br /&gt;
I'm using a real-time clock chip to wake up the 328. It's much more accurate than an internal watchdog timer, and lets me add a timestamp to my measurements. I've used the DS3232 rtc before in the form of some inexpensive modules, so I decided to use them for this project as well. This chip contains a real time clock with temperature compensation. The clock accuracy is supposed to be quite good, on the order of a few PPM.&lt;br /&gt;
&lt;br /&gt;
The DS3231 can be powered exclusively through the Vbatt pin instead of the normal Vcc pin. This saves some power according to the datasheet, but also increases the temperature compensation time to once a minute rather than every few seconds. For my purposes, this is fine. &lt;br /&gt;
For interfacing to the 328, 3 pins are used. Two are the i2c pins, and the third is the INT pin. The DS3231 has an alarm function, which can trigger this pin to go LOW when tripped. Connected to an interrupt pin on the 328, it is used to wake up the microcontroller from deep sleep. The 328 then resets the alarm, and reads out a timestamp over i2c. Alarm functions are not inplemented in most of the Arduino libraries I have found, so I wrote a simple one to make working directly with the RTC easier (but it only uses one of the two alarms).&lt;br /&gt;
&lt;br /&gt;
My initial testing with RTC and microcontroller seemed promising, with the circuit waking up and blinking an LED once per minute as I had set it to do. However, when placing the setup in the freezer, the clock was more than a minute off after only a few hours. This is probably because the DS3231s I got from eBay were counterfeit. They work fine for room temperature operation (I have a counterfeit one in a nixie clock I made and it keeps accurate time) but my board needs to survive in freezing temperatures. A few genuine DS3232M from Maxim solved the problem, and my boards now keep accurate time even in the freezer. The M version is a smaller package, and is very slightly less accurate but is supposed to be more durable, using a MEMS oscillator instead of a crystal. The 3232 part number includes some extra battery backed general purpose memory, which I thought might be useful but is still not used yet in my design. The new chip takes the same commands as the old one, so no code changes were needed.&lt;br /&gt;
&lt;br /&gt;
===The Memory===&lt;br /&gt;
Initially, I tried using a 128kb 24lc1025 EEprom as the main storage space, but quickly found it would run out of space in less than the 5 year design life, so I would need to do something else.&lt;br /&gt;
&lt;br /&gt;
While working on this project, I came across the Pearl cave project, where the same idea is used to make environmental sensors for monitoring caves. Their solution was to use an SD card, but it was found that different SD cards used different amounts of power, and that the cards did not like being turned on and off. Rather than an SD card, I thought about using a flash memory chip, maybe in the same way SPIFFS is used in my esp8266 projects. It would also be soldered to the board, with no spring connectors that could corrode or come loose. I settled on the Winbond 25q32fvsig, a 4mbyte flash chip. This would have plenty of room for years of measurements, and also has a sleep mode which takes the power down to a sub-microamp level. I used Marzogh's SPIMemory library for this, which takes care of most of the work in interfacing with the flash. When working with these chips, I found I could not write my 17 byte long measurement frame sequentially like I could with the EEPROM. Increasing the frame size to 32 bytes aligned them with the flash pages and I was able to read and write without errors, as well as fit a few extra measurements in. Now I can store 128k samples, instead of just 128k bytes. At one sampling per hour, this is almost 15 years of storage.&lt;br /&gt;
&lt;br /&gt;
On the board in my picture, you can spot my mistake in the board design. I accidentally connected the reset and write protect pins to GND instead of VCC. This means that the chip is in a constant reset state, and cannot be read or written. With a Dremel, I carefully cut the traces. The reset pin is right next to the VCC pin, so a little excess solder is enough to bridge them. A jumper of thin wire across the top of the chip brings the write protect pin positive as well. This error is fixed in the released version provided in Files. &lt;br /&gt;
&lt;br /&gt;
Initially, all I wanted to measure was temperature. However, that's not a lot of payout for 5 years work. Since the time capsule supposed to be a sealed PVC tube, I thought that it would be interesting to see what the internal pressure was like.  The BME280 can do both temperature and pressure, and also humidity. It also has a low power sleep mode, and communicates over i2c. Since I've never soldered a part that small, I cheated and used a premade breakout board module, which fits on to the i2c header behind the main header.  The header allows for more expansion also, in addition to or if another i2c sensor is to be used.&lt;br /&gt;
&lt;br /&gt;
===Power===&lt;br /&gt;
With the selection of a 3.3v memory chip and sensor, I was limited to running the board at 3.3v instead of the 5v I had originally planned. I didn't think that two AA batteries or a coin cell could last long enough and still be enough voltage to power the board after it lost some charge. Since I care more about reliability with no maintenance, and ton't have too much of a size constraint on the size of the batteries. I thought it would be better to run the components at some stable voltage that did not drop with the batteries (though I recognize that this will increase idle current draw). I could have run the 328 at 4.5v with 3 AAs, but I would still need an additional 3.3v supply and some kind of logic level  conversion. The pearl cave project had the same problem, and solved it by using a simple linear regulator. However, the regular has to be a low quiesent current type, which drew little current on it's own, unlike an lm1117 found on most of the cheap Arduino boards. It took me a while to find a version that had an input voltage greater than 5.5v (in case I decided to use more than 3 cells), and still had good performance. Both the Mcp1703 and HT7773 are good candidates, and come in a sot89 package with the same pinout. I happened to have some of the through hole HT regulators on hand, and used them while I waited for some eBay mcp1703s to arrive. Unfortunately, the eBay mcp1703s turned out to be counterfeit also, and did not meet the claimed quiescent current listed in the datasheet. The current was instead around 70 microamps, more than 10x what I had planned. Again, some genuine samples from Microchip fixed the problem. Interestingly, the HT chips which are produced by a Chinese company and were bought overseas from an unknown seller performed perfectly.&lt;br /&gt;
&lt;br /&gt;
===Operator interface===&lt;br /&gt;
Only seven IO pins on the 328 were being used thus far. Two for i2c, one for the clock wake up pin, and 4 for the SPI flash memory. I added an eighth, a voltage divider of high value resistors on an ADC pin to measure the battery voltage, which has not been fitted yet. But that still leaves lots of other pins open on the 328. I connected four of them to a small 4 position dip switch that I had on hand, three more to colored LEDs, and a button switch to the other interrupt pin. In this way, the board can still be verified working after disconnecting from the programmer. Otherwise, I can't tell if the board is working fine or a brick. The button enables waking up the 328 without waiting for the clock alarm to trip, which could take an hour on the slow setting. The dip switches allow for selecting different models, like setting the clock accurately to the second, or erasing the flash to start over, or dumping the flash contents over serial.&lt;br /&gt;
&lt;br /&gt;
I decided to include a serial interface header on one side, so it will be easier to get the data off of the board when the time comes. I'll use the Arduino bootloader so I can program the chip over serial, since I have many more usb serial adapters than AVR programmers, and the cheap AVR programmers I do have only work 5% of the time anyways. VCC, GND, Tx, Rx, and the 328 /RST pins are brought out on the 5 pin header.&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=Electronic_Time_Capsule&amp;diff=577</id>
		<title>Electronic Time Capsule</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=Electronic_Time_Capsule&amp;diff=577"/>
		<updated>2020-05-20T21:25:43Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: /* envilog3 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:Eagle.png|thumb]]&lt;br /&gt;
This is a board with a bme280 pressure, humidity and temperature sensor, a ds3232m realtime clock, and 4MB of flash memory. The clock wakes up an atmega328p, it takes readings and stores them, and then goes back to sleep. A low dropout, low quiescent current voltage regulator provides 3.3v power to the board from an external battery. &lt;br /&gt;
&lt;br /&gt;
I built this to be buried inside a time capsule (though it has plenty of other uses), so it has to be able to survive freezing temperatures, and should last at least 5 years without maintenance. A quick calculation of current draw on my prototype says that it should be able to run for about 15 years on AA batteries, and there is plenty of room in the flash memory for hourly measurements (15ish years worth).&lt;br /&gt;
&lt;br /&gt;
Over 10 years, the board is calculated to use &amp;lt;pre&amp;gt;88000hr*((20mA+4)*(.5/3600)+.35mA*(57/3600)+.006mA) = 1309 mAh&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Unit Status==&lt;br /&gt;
===envilog2===&lt;br /&gt;
buried in 2018 time 5 year capsule (started/sealed WC 42,hourly), lithium batteries&lt;br /&gt;
===envilog1===&lt;br /&gt;
On deck in tupperware outside house, started Jan 2019 (hourly), lithium batteries&lt;br /&gt;
* Checked at July 2019 Winter Camp party, status ok.&lt;br /&gt;
* Checked Jan 26, 2020, status OK. Left inside for few days to dry after finding the tupperware cracked. Time was about 65 seconds behind EST.&lt;br /&gt;
* Checked May 20, 2020 - after a heavy rainstorm the past few weeks, I found the tupperware cracked and the unit submerged in about an inch of water. All batteries are dead, although I do not think water reacted quickly with the lithium as I see no melting of the plastic case. Board is severely corroded, but looks cleanable.&lt;br /&gt;
&lt;br /&gt;
===envilog5===&lt;br /&gt;
At Jan trailer, started Jul 2019, hourly&lt;br /&gt;
&lt;br /&gt;
===envilog3===&lt;br /&gt;
buried in 2019 time 5 year capsule (started/sealed WC 43,hourly), alkaline batteries&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
* [[File:Envilog-pcb-files.zip]] - The eagle and gerber files needed to make the boards&lt;br /&gt;
* [[File:Envilog-arduino-code.zip]] - The arduino program that runs on the board. Additional board hardware and libraries listed below are needed also.&lt;br /&gt;
&lt;br /&gt;
==Parts List==&lt;br /&gt;
* BME280 board measuring temperature, humidity and pressure.&lt;br /&gt;
* DS3231M measuring time, and temperature&lt;br /&gt;
* Optional voltage divider to measure battery voltage&lt;br /&gt;
* MCP1703AT-3302E/MB - 3.3V voltage low dropout linear regulator&lt;br /&gt;
* ATMEGA328P-AU&lt;br /&gt;
* Winbond 25q32fvsig&lt;br /&gt;
&lt;br /&gt;
==Interface Description==&lt;br /&gt;
[[File:Envilog-boardoutline.png|thumb|The board layout]]&lt;br /&gt;
The board contains 4 dip switches, which pretty much work independently, and one button.&lt;br /&gt;
The switches should be labeled 1-4, with 1 being closest to the center of the board. The off position of the switch has the handle toward the 3 indicator LEDs, and the on position is towards the button. &lt;br /&gt;
&lt;br /&gt;
The functions of the switches are:&lt;br /&gt;
* Switch 1 (on) - Enters the clock setting procedure. The clock is set using only the single button, pressed a number of times to set the date and time. '''You can only set the clock when the board is first connected to power or reset'''. &lt;br /&gt;
* Switch 2 (on) - '''Erases the flash chip.''' The flash must be empty to write new data from the beginning. An extra button press is needed to confirm you really want to do this. If the clock set switch is also on, then you will set the clock first, then erase the flash. '''You can only erase the flash when the board is first connected to power or reset'''.  This helps avoid doing it accidentally. &lt;br /&gt;
* Switch 3 (on) - Dumps the flash. Over serial, the board will output a tab-formatted table of the measurements that have been recorded. The dump will continue until either the entire chip has been read (including blank areas), or Switch 3 has been turned off. To start a dump, turn switch 3 on and then either wait for the RTC to wake up the system, or wake it up yourself with a single press of the button.&lt;br /&gt;
&lt;br /&gt;
* Switch 4 (on) - Blinks a green light every time the 328 powers up. Turn off to save power.&lt;br /&gt;
&lt;br /&gt;
The LED functions are, in general:&lt;br /&gt;
* Green - Status - Blinks when something happens / has happened&lt;br /&gt;
* Red - Working - Turns on when user input is acknowledged &lt;br /&gt;
* Yellow - Waiting for user input&lt;br /&gt;
&lt;br /&gt;
==Software Libraries used==&lt;br /&gt;
The following software was used in the program for this device:&lt;br /&gt;
* Arduino MiniCore hardware package. The board is programmed like a normal arduino board with the arduino bootloader, but this package makes it easier to get a bare 328 chip working with many of the power-heavy features disabled.&lt;br /&gt;
* avr/power, avr/sleep, Wire, SPI arduino libraries, included with the arduino IDE.&lt;br /&gt;
* https://github.com/Marzogh/SPIMemory - for using the external flash memory chip that stores the measurements&lt;br /&gt;
* The SparkFun BME280 library - For configuring the BME280 sensor&lt;br /&gt;
&lt;br /&gt;
==Build / Operating Instructions==&lt;br /&gt;
* Make the board and assemble the components onto it.  Everything is surface mount except the sensor board, which is an Aliexpress special. &lt;br /&gt;
* Flash the 328 on the board with the Arduino MiniCore bootloader. Make sure that the BOD is disabled, and the 8 mhz clock speed is selected. I tried 1 mhz as well, but found that the flash chip did not work well at this speed. The 328 can be programmed with ICSP, just like flashing a normal Arduino. The reset line, VCC and ground pins are brought out on the outside 5 pin header. The remaining SPI pins needed for ICP can be connected to either some test points on the board, or by clipping onto the legs of the flash chip.&lt;br /&gt;
* The board can now be programmed with a usb-serial adapter like any other arduino board. The tx and rx pins on the board connect to the rx and tx pins on the usb-serial device. The reset pin on the board connects through a 0.1uf cap to the DTR pin on the usb-serial, because I didn't include one on the board. Upload the main program.&lt;br /&gt;
* Push switches 1 (clock set) , 2 (flash erase) and 4 (status) on, and connect the board to a serial monitor and power (between 4 and 15 volts).&lt;br /&gt;
* Use the single button to set the clock following the serial terminal instructions. For example, to set the first parameter, the year, to 2018, press the button repeatedly until 18 is shown, then wait 2 seconds to continue. &lt;br /&gt;
* Erase the flash by pressing the button once when prompted.&lt;br /&gt;
* Return all dip switches to off position. Switch 4 can be left on, which will blink the green led whenever the 328 is powered up, but can be turned off to save power.&lt;br /&gt;
* After data is collected, turn switch 3 on and press the button. The data will be output over serial in the form of a table.&lt;br /&gt;
&lt;br /&gt;
==Example Output==&lt;br /&gt;
This is what it looks like when running / dumping measurements in the serial terminal. To create a dump, move switch 3 to on position and press button once. Data is dumped out the TXD pin at 115200 baud.&lt;br /&gt;
&amp;lt;pre&amp;gt;TIME CAPSULE LONG DURATION ENVIROMENTAL LOGGER - Alan J. WIlson 2018&lt;br /&gt;
Current time: 18/09/05 23:29:49&lt;br /&gt;
Flash capacity: 4194304 bytes, 131072 frames&lt;br /&gt;
Frame size: 32&lt;br /&gt;
Next ram index: 457&lt;br /&gt;
&lt;br /&gt;
Current time: 18/09/05 23:29:49&lt;br /&gt;
Cycle: 0&lt;br /&gt;
Index: 457&lt;br /&gt;
Battery ADC: 0&lt;br /&gt;
DS3231 Temperature (C): 22.75&lt;br /&gt;
BME280 Temperature (C): 22.70&lt;br /&gt;
BME280 Pressure   (Pa): 99898.25&lt;br /&gt;
BME280 Humidity    (%): 51.34&lt;br /&gt;
Write OK&lt;br /&gt;
&lt;br /&gt;
FLASH MEMORY DATA DUMP FOLLOWS&lt;br /&gt;
Index	Cycle	Year	Month	Day	Hour	Minute	Second	DS3231 Temperature (C)	Temperature (C)	Pressure (Pa)	Humidity (%)	Battery (10-bit ADC)&lt;br /&gt;
0	0	18	9	5	14	44	45	22.50	22.22	99527.77	45.77	0	&lt;br /&gt;
1	1	18	9	5	14	44	45	22.50	22.19	99525.35	46.32	0	&lt;br /&gt;
2	2	18	9	5	14	44	58	22.75	22.21	99520.82	46.35	0	&lt;br /&gt;
3	3	18	9	5	14	45	10	22.75	22.19	99530.60	46.18	0	&lt;br /&gt;
4	4	18	9	5	14	46	10	22.50	22.23	99526.82	46.55	0	&lt;br /&gt;
5	5	18	9	5	14	47	10	22.25	22.29	99525.55	46.44	0	&lt;br /&gt;
6	6	18	9	5	14	48	10	22.25	22.02	99528.21	47.05	0	&lt;br /&gt;
7	7	18	9	5	14	49	10	22.00	21.90	99535.94	47.95	0	&lt;br /&gt;
8	8	18	9	5	14	50	10	22.00	21.79	99527.93	48.24	0	&lt;br /&gt;
9	9	18	9	5	14	51	10	22.00	21.75	99536.94	48.41	0	&lt;br /&gt;
10	10	18	9	5	14	52	10	22.00	21.74	99537.06	48.59	0	&lt;br /&gt;
11	11	18	9	5	14	53	10	22.00	21.74	99530.03	48.66	0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Serial Console (12/08/2018)==&lt;br /&gt;
In the original version of the software, I use the single button to do most of the setup. This can be very difficult, because the board and button are tiny, and pressing it is required many times, especially for setting the time. Since I implement a simple serial console tool into many of my other projects, I figured it would be a good idea to have one here too, since it is probably fairly likely that you will have the device hooked up to a computer when setting it up anyways.&lt;br /&gt;
&lt;br /&gt;
The serial console I made accepts commands one line at a time, but there is a timeout so that the system does not get stuck waiting forever for a character that never comes. This makes it excellent for programs that send data one line at a time, like the Arduino IDE, but it will not work that well for standard dumb terminal unless you can type very quickly.&lt;br /&gt;
&lt;br /&gt;
The console accepts commands in the form of a single letter, followed by a floating point number, and finally the newline character. For example:&lt;br /&gt;
&amp;lt;pre&amp;gt;y18.00\n&amp;lt;/pre&amp;gt;&lt;br /&gt;
The letter gets passed along as the command, and the number gets passed along as the parameter. It doesn't have to stay floating point either, it will just get converted into an integer or whatever you need. &lt;br /&gt;
&lt;br /&gt;
To use the serial console, set your terminal to 9600 baud and reset the board. Then enter '''e'''. I found that the baudrate of the board's normal 115200 is too high to reliably accept serial input, but works fine for outputing data at high speed during normal operation of the board. &lt;br /&gt;
&lt;br /&gt;
The following table documents all the commands and their functions:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Command !! Parameter !! Description !! Example&lt;br /&gt;
|-&lt;br /&gt;
| e || any || Enters the Serial control mode || &lt;br /&gt;
|-&lt;br /&gt;
| t || 1 - start listing&amp;lt;br&amp;gt;0 - stop listing || Lists continuously the current time. This is helpful when setting it exactly to another source. || t1 - lists time&lt;br /&gt;
|-&lt;br /&gt;
| a || 1 - start listing&amp;lt;br&amp;gt;0 - stop listing || Lists continuously the flash data. This is much slower than using the switch #3 because it is only at 9600 baud. || a1&lt;br /&gt;
|-&lt;br /&gt;
| r || any || Displays the DS3232M RTC registers ||&lt;br /&gt;
|-&lt;br /&gt;
| y || 2 digit year || Sets the year of the RTC || y18 - sets to year 2018&lt;br /&gt;
|-&lt;br /&gt;
| m || 2 digit month || Sets the month of the RTC || m12 - sets to month December&lt;br /&gt;
|-&lt;br /&gt;
| d || 2 digit day || Sets the day of month of the RTC || d8 - sets to day 8&lt;br /&gt;
|-&lt;br /&gt;
| H || 2 digit hour || Sets the hour of the RTC || H32 - sets to hour 1PM&lt;br /&gt;
|-&lt;br /&gt;
| M || 2 digit minute || Sets the minute of the RTC || M32 - sets to minute 32&lt;br /&gt;
|-&lt;br /&gt;
| S || 2 digit second || Sets the seconds of the RTC || S0 - sets to second 0&lt;br /&gt;
|-&lt;br /&gt;
| F || value 12345 || Erases the flash memory || F12345&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Octave Plotter==&lt;br /&gt;
If the output table is changed from tab formatting to CSV (you can do this with Notepad++ or some other text editor), you can use this script in GNU Octave (an open source MatLab alternative) to make a handy plot of the data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
clear;&lt;br /&gt;
clc;&lt;br /&gt;
clf;&lt;br /&gt;
&lt;br /&gt;
raw = load(&amp;quot;j.csv&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
%frame format:&lt;br /&gt;
%[ time, ds3231T, temp, pressure, humidity ]&lt;br /&gt;
&lt;br /&gt;
i = 0;&lt;br /&gt;
while(i&amp;lt;length(raw(:,1)))&lt;br /&gt;
  i++;&lt;br /&gt;
  frame(i,:)=[datenum([2000+raw(i,3),raw(i,4),raw(i,5),raw(i,6),raw(i,7),raw(i,8)]),raw(i,9),raw(i,10),raw(i,11),raw(i,12)];&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
figure(1)&lt;br /&gt;
ha(1)=subplot(3,1,1);&lt;br /&gt;
hold on;&lt;br /&gt;
title(strcat(&amp;quot;Enviroment data: &amp;quot;,datestr(frame(1,1),&amp;quot; mmm 'yy &amp;quot;),&amp;quot; to&amp;quot;,datestr(frame(i,1),&amp;quot; mmm 'yy &amp;quot;)));&lt;br /&gt;
plot(frame(:,1),frame(:,2)*(9/5)+32);&lt;br /&gt;
plot(frame(:,1),frame(:,3)*(9/5)+32);&lt;br /&gt;
datetick(&amp;quot;x&amp;quot;, &amp;quot;mmm/yy&amp;quot;);&lt;br /&gt;
grid on;&lt;br /&gt;
xlabel(&amp;quot;Time&amp;quot;);&lt;br /&gt;
ylabel(&amp;quot;Temperature (F)&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
ha(2)=subplot(3,1,2);&lt;br /&gt;
plot(frame(:,1),frame(:,4));&lt;br /&gt;
datetick(&amp;quot;x&amp;quot;, &amp;quot;mmm/yy&amp;quot;);&lt;br /&gt;
grid on;&lt;br /&gt;
xlabel(&amp;quot;Time&amp;quot;);&lt;br /&gt;
ylabel(&amp;quot;Pressure (Pa)&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
ha(3)=subplot(3,1,3);&lt;br /&gt;
plot(frame(:,1),frame(:,5));&lt;br /&gt;
datetick(&amp;quot;x&amp;quot;, &amp;quot;mmm/yy&amp;quot;);&lt;br /&gt;
grid on;&lt;br /&gt;
xlabel(&amp;quot;Time&amp;quot;);&lt;br /&gt;
ylabel(&amp;quot;Humidity (%)&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
linkaxes(ha, &amp;quot;x&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Details (hackaday.io)==&lt;br /&gt;
This project has been ongoing for a few months now, but I, like many people, don't document projects as well / as often as I should. This project represents a lot of firsts for me, such as the first time working with low power, first time ever soldering SMD components, and only the second project where I have designed a PCB. Most of my projects consist of either breadboards, prototyping boards, or some bizarre freeform/deadbug. I am not an electrical engineer or programmer, just a self taught hobbyist, which does sometimes mean that I have no idea if I am doing something &amp;quot;the right way.&amp;quot; I want to keep my designs simple, using components that are well known, and programming no more complex than Arduino.  With that in mind, the following ramblings explain my thoughts in building this project.&lt;br /&gt;
&lt;br /&gt;
Since this was designed to be buried in a time capsule, the first order of business is how to get power. I had never worked with any low power sleep modes on microcontrollers, but I have plenty of devices that last for years with just batteries. Plenty of projects here on HAD that use a coin cell, but i'd be hard pressed to get 5 years out of one of those. Sounds like a good solution is normal AA batteries. I will use enough so that even when the battery is near dead, the voltage will still be high enough to power the device. It also gives me a bit of extra room to not worry so much about squeezing every last drop of juice out the batteries.&lt;br /&gt;
&lt;br /&gt;
===The Microcontroller===&lt;br /&gt;
It's an ATmega328, nothing special. I thought about using an ATtiny, but since both have the same sleep power requirements, I kept the 328 for some extra pins and more ram, in case I wanted to expand the system a little or do some kind of heavier processing. Yes, this is essentially the same thing as building your own Arduino, but it's simple and it works.&lt;br /&gt;
&lt;br /&gt;
The important thing is to be able to get the 328 into and out of sleep mode. Having never experimented with this mode before, Nick Gammon's tutorial taught me most of what I needed to know. I want this board to run for a long time, so everything must go, like the ADCs, and brownout detection. This brings the sleep current down to an impressive 0.1 uA. I'll re-enable any periperials I need upon waking up.&lt;br /&gt;
&lt;br /&gt;
To wake up the chip, I'll use one of the interrupt pins.&lt;br /&gt;
&lt;br /&gt;
===The Clock===&lt;br /&gt;
I'm using a real-time clock chip to wake up the 328. It's much more accurate than an internal watchdog timer, and lets me add a timestamp to my measurements. I've used the DS3232 rtc before in the form of some inexpensive modules, so I decided to use them for this project as well. This chip contains a real time clock with temperature compensation. The clock accuracy is supposed to be quite good, on the order of a few PPM.&lt;br /&gt;
&lt;br /&gt;
The DS3231 can be powered exclusively through the Vbatt pin instead of the normal Vcc pin. This saves some power according to the datasheet, but also increases the temperature compensation time to once a minute rather than every few seconds. For my purposes, this is fine. &lt;br /&gt;
For interfacing to the 328, 3 pins are used. Two are the i2c pins, and the third is the INT pin. The DS3231 has an alarm function, which can trigger this pin to go LOW when tripped. Connected to an interrupt pin on the 328, it is used to wake up the microcontroller from deep sleep. The 328 then resets the alarm, and reads out a timestamp over i2c. Alarm functions are not inplemented in most of the Arduino libraries I have found, so I wrote a simple one to make working directly with the RTC easier (but it only uses one of the two alarms).&lt;br /&gt;
&lt;br /&gt;
My initial testing with RTC and microcontroller seemed promising, with the circuit waking up and blinking an LED once per minute as I had set it to do. However, when placing the setup in the freezer, the clock was more than a minute off after only a few hours. This is probably because the DS3231s I got from eBay were counterfeit. They work fine for room temperature operation (I have a counterfeit one in a nixie clock I made and it keeps accurate time) but my board needs to survive in freezing temperatures. A few genuine DS3232M from Maxim solved the problem, and my boards now keep accurate time even in the freezer. The M version is a smaller package, and is very slightly less accurate but is supposed to be more durable, using a MEMS oscillator instead of a crystal. The 3232 part number includes some extra battery backed general purpose memory, which I thought might be useful but is still not used yet in my design. The new chip takes the same commands as the old one, so no code changes were needed.&lt;br /&gt;
&lt;br /&gt;
===The Memory===&lt;br /&gt;
Initially, I tried using a 128kb 24lc1025 EEprom as the main storage space, but quickly found it would run out of space in less than the 5 year design life, so I would need to do something else.&lt;br /&gt;
&lt;br /&gt;
While working on this project, I came across the Pearl cave project, where the same idea is used to make environmental sensors for monitoring caves. Their solution was to use an SD card, but it was found that different SD cards used different amounts of power, and that the cards did not like being turned on and off. Rather than an SD card, I thought about using a flash memory chip, maybe in the same way SPIFFS is used in my esp8266 projects. It would also be soldered to the board, with no spring connectors that could corrode or come loose. I settled on the Winbond 25q32fvsig, a 4mbyte flash chip. This would have plenty of room for years of measurements, and also has a sleep mode which takes the power down to a sub-microamp level. I used Marzogh's SPIMemory library for this, which takes care of most of the work in interfacing with the flash. When working with these chips, I found I could not write my 17 byte long measurement frame sequentially like I could with the EEPROM. Increasing the frame size to 32 bytes aligned them with the flash pages and I was able to read and write without errors, as well as fit a few extra measurements in. Now I can store 128k samples, instead of just 128k bytes. At one sampling per hour, this is almost 15 years of storage.&lt;br /&gt;
&lt;br /&gt;
On the board in my picture, you can spot my mistake in the board design. I accidentally connected the reset and write protect pins to GND instead of VCC. This means that the chip is in a constant reset state, and cannot be read or written. With a Dremel, I carefully cut the traces. The reset pin is right next to the VCC pin, so a little excess solder is enough to bridge them. A jumper of thin wire across the top of the chip brings the write protect pin positive as well. This error is fixed in the released version provided in Files. &lt;br /&gt;
&lt;br /&gt;
Initially, all I wanted to measure was temperature. However, that's not a lot of payout for 5 years work. Since the time capsule supposed to be a sealed PVC tube, I thought that it would be interesting to see what the internal pressure was like.  The BME280 can do both temperature and pressure, and also humidity. It also has a low power sleep mode, and communicates over i2c. Since I've never soldered a part that small, I cheated and used a premade breakout board module, which fits on to the i2c header behind the main header.  The header allows for more expansion also, in addition to or if another i2c sensor is to be used.&lt;br /&gt;
&lt;br /&gt;
===Power===&lt;br /&gt;
With the selection of a 3.3v memory chip and sensor, I was limited to running the board at 3.3v instead of the 5v I had originally planned. I didn't think that two AA batteries or a coin cell could last long enough and still be enough voltage to power the board after it lost some charge. Since I care more about reliability with no maintenance, and ton't have too much of a size constraint on the size of the batteries. I thought it would be better to run the components at some stable voltage that did not drop with the batteries (though I recognize that this will increase idle current draw). I could have run the 328 at 4.5v with 3 AAs, but I would still need an additional 3.3v supply and some kind of logic level  conversion. The pearl cave project had the same problem, and solved it by using a simple linear regulator. However, the regular has to be a low quiesent current type, which drew little current on it's own, unlike an lm1117 found on most of the cheap Arduino boards. It took me a while to find a version that had an input voltage greater than 5.5v (in case I decided to use more than 3 cells), and still had good performance. Both the Mcp1703 and HT7773 are good candidates, and come in a sot89 package with the same pinout. I happened to have some of the through hole HT regulators on hand, and used them while I waited for some eBay mcp1703s to arrive. Unfortunately, the eBay mcp1703s turned out to be counterfeit also, and did not meet the claimed quiescent current listed in the datasheet. The current was instead around 70 microamps, more than 10x what I had planned. Again, some genuine samples from Microchip fixed the problem. Interestingly, the HT chips which are produced by a Chinese company and were bought overseas from an unknown seller performed perfectly.&lt;br /&gt;
&lt;br /&gt;
===Operator interface===&lt;br /&gt;
Only seven IO pins on the 328 were being used thus far. Two for i2c, one for the clock wake up pin, and 4 for the SPI flash memory. I added an eighth, a voltage divider of high value resistors on an ADC pin to measure the battery voltage, which has not been fitted yet. But that still leaves lots of other pins open on the 328. I connected four of them to a small 4 position dip switch that I had on hand, three more to colored LEDs, and a button switch to the other interrupt pin. In this way, the board can still be verified working after disconnecting from the programmer. Otherwise, I can't tell if the board is working fine or a brick. The button enables waking up the 328 without waiting for the clock alarm to trip, which could take an hour on the slow setting. The dip switches allow for selecting different models, like setting the clock accurately to the second, or erasing the flash to start over, or dumping the flash contents over serial.&lt;br /&gt;
&lt;br /&gt;
I decided to include a serial interface header on one side, so it will be easier to get the data off of the board when the time comes. I'll use the Arduino bootloader so I can program the chip over serial, since I have many more usb serial adapters than AVR programmers, and the cheap AVR programmers I do have only work 5% of the time anyways. VCC, GND, Tx, Rx, and the 328 /RST pins are brought out on the 5 pin header.&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=Electronic_Time_Capsule&amp;diff=576</id>
		<title>Electronic Time Capsule</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=Electronic_Time_Capsule&amp;diff=576"/>
		<updated>2020-05-20T21:25:21Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: /* envilog1 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:Eagle.png|thumb]]&lt;br /&gt;
This is a board with a bme280 pressure, humidity and temperature sensor, a ds3232m realtime clock, and 4MB of flash memory. The clock wakes up an atmega328p, it takes readings and stores them, and then goes back to sleep. A low dropout, low quiescent current voltage regulator provides 3.3v power to the board from an external battery. &lt;br /&gt;
&lt;br /&gt;
I built this to be buried inside a time capsule (though it has plenty of other uses), so it has to be able to survive freezing temperatures, and should last at least 5 years without maintenance. A quick calculation of current draw on my prototype says that it should be able to run for about 15 years on AA batteries, and there is plenty of room in the flash memory for hourly measurements (15ish years worth).&lt;br /&gt;
&lt;br /&gt;
Over 10 years, the board is calculated to use &amp;lt;pre&amp;gt;88000hr*((20mA+4)*(.5/3600)+.35mA*(57/3600)+.006mA) = 1309 mAh&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Unit Status==&lt;br /&gt;
===envilog2===&lt;br /&gt;
buried in 2018 time 5 year capsule (started/sealed WC 42,hourly), lithium batteries&lt;br /&gt;
===envilog1===&lt;br /&gt;
On deck in tupperware outside house, started Jan 2019 (hourly), lithium batteries&lt;br /&gt;
* Checked at July 2019 Winter Camp party, status ok.&lt;br /&gt;
* Checked Jan 26, 2020, status OK. Left inside for few days to dry after finding the tupperware cracked. Time was about 65 seconds behind EST.&lt;br /&gt;
* Checked May 20, 2020 - after a heavy rainstorm the past few weeks, I found the tupperware cracked and the unit submerged in about an inch of water. All batteries are dead, although I do not think water reacted quickly with the lithium as I see no melting of the plastic case. Board is severely corroded, but looks cleanable.&lt;br /&gt;
&lt;br /&gt;
===envilog5===&lt;br /&gt;
At Jan trailer, started Jul 2019, hourly&lt;br /&gt;
&lt;br /&gt;
===envilog3===&lt;br /&gt;
buried in 2019 time 5 year capsule (started/sealed WC 43,hourly), lithium batteries&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
* [[File:Envilog-pcb-files.zip]] - The eagle and gerber files needed to make the boards&lt;br /&gt;
* [[File:Envilog-arduino-code.zip]] - The arduino program that runs on the board. Additional board hardware and libraries listed below are needed also.&lt;br /&gt;
&lt;br /&gt;
==Parts List==&lt;br /&gt;
* BME280 board measuring temperature, humidity and pressure.&lt;br /&gt;
* DS3231M measuring time, and temperature&lt;br /&gt;
* Optional voltage divider to measure battery voltage&lt;br /&gt;
* MCP1703AT-3302E/MB - 3.3V voltage low dropout linear regulator&lt;br /&gt;
* ATMEGA328P-AU&lt;br /&gt;
* Winbond 25q32fvsig&lt;br /&gt;
&lt;br /&gt;
==Interface Description==&lt;br /&gt;
[[File:Envilog-boardoutline.png|thumb|The board layout]]&lt;br /&gt;
The board contains 4 dip switches, which pretty much work independently, and one button.&lt;br /&gt;
The switches should be labeled 1-4, with 1 being closest to the center of the board. The off position of the switch has the handle toward the 3 indicator LEDs, and the on position is towards the button. &lt;br /&gt;
&lt;br /&gt;
The functions of the switches are:&lt;br /&gt;
* Switch 1 (on) - Enters the clock setting procedure. The clock is set using only the single button, pressed a number of times to set the date and time. '''You can only set the clock when the board is first connected to power or reset'''. &lt;br /&gt;
* Switch 2 (on) - '''Erases the flash chip.''' The flash must be empty to write new data from the beginning. An extra button press is needed to confirm you really want to do this. If the clock set switch is also on, then you will set the clock first, then erase the flash. '''You can only erase the flash when the board is first connected to power or reset'''.  This helps avoid doing it accidentally. &lt;br /&gt;
* Switch 3 (on) - Dumps the flash. Over serial, the board will output a tab-formatted table of the measurements that have been recorded. The dump will continue until either the entire chip has been read (including blank areas), or Switch 3 has been turned off. To start a dump, turn switch 3 on and then either wait for the RTC to wake up the system, or wake it up yourself with a single press of the button.&lt;br /&gt;
&lt;br /&gt;
* Switch 4 (on) - Blinks a green light every time the 328 powers up. Turn off to save power.&lt;br /&gt;
&lt;br /&gt;
The LED functions are, in general:&lt;br /&gt;
* Green - Status - Blinks when something happens / has happened&lt;br /&gt;
* Red - Working - Turns on when user input is acknowledged &lt;br /&gt;
* Yellow - Waiting for user input&lt;br /&gt;
&lt;br /&gt;
==Software Libraries used==&lt;br /&gt;
The following software was used in the program for this device:&lt;br /&gt;
* Arduino MiniCore hardware package. The board is programmed like a normal arduino board with the arduino bootloader, but this package makes it easier to get a bare 328 chip working with many of the power-heavy features disabled.&lt;br /&gt;
* avr/power, avr/sleep, Wire, SPI arduino libraries, included with the arduino IDE.&lt;br /&gt;
* https://github.com/Marzogh/SPIMemory - for using the external flash memory chip that stores the measurements&lt;br /&gt;
* The SparkFun BME280 library - For configuring the BME280 sensor&lt;br /&gt;
&lt;br /&gt;
==Build / Operating Instructions==&lt;br /&gt;
* Make the board and assemble the components onto it.  Everything is surface mount except the sensor board, which is an Aliexpress special. &lt;br /&gt;
* Flash the 328 on the board with the Arduino MiniCore bootloader. Make sure that the BOD is disabled, and the 8 mhz clock speed is selected. I tried 1 mhz as well, but found that the flash chip did not work well at this speed. The 328 can be programmed with ICSP, just like flashing a normal Arduino. The reset line, VCC and ground pins are brought out on the outside 5 pin header. The remaining SPI pins needed for ICP can be connected to either some test points on the board, or by clipping onto the legs of the flash chip.&lt;br /&gt;
* The board can now be programmed with a usb-serial adapter like any other arduino board. The tx and rx pins on the board connect to the rx and tx pins on the usb-serial device. The reset pin on the board connects through a 0.1uf cap to the DTR pin on the usb-serial, because I didn't include one on the board. Upload the main program.&lt;br /&gt;
* Push switches 1 (clock set) , 2 (flash erase) and 4 (status) on, and connect the board to a serial monitor and power (between 4 and 15 volts).&lt;br /&gt;
* Use the single button to set the clock following the serial terminal instructions. For example, to set the first parameter, the year, to 2018, press the button repeatedly until 18 is shown, then wait 2 seconds to continue. &lt;br /&gt;
* Erase the flash by pressing the button once when prompted.&lt;br /&gt;
* Return all dip switches to off position. Switch 4 can be left on, which will blink the green led whenever the 328 is powered up, but can be turned off to save power.&lt;br /&gt;
* After data is collected, turn switch 3 on and press the button. The data will be output over serial in the form of a table.&lt;br /&gt;
&lt;br /&gt;
==Example Output==&lt;br /&gt;
This is what it looks like when running / dumping measurements in the serial terminal. To create a dump, move switch 3 to on position and press button once. Data is dumped out the TXD pin at 115200 baud.&lt;br /&gt;
&amp;lt;pre&amp;gt;TIME CAPSULE LONG DURATION ENVIROMENTAL LOGGER - Alan J. WIlson 2018&lt;br /&gt;
Current time: 18/09/05 23:29:49&lt;br /&gt;
Flash capacity: 4194304 bytes, 131072 frames&lt;br /&gt;
Frame size: 32&lt;br /&gt;
Next ram index: 457&lt;br /&gt;
&lt;br /&gt;
Current time: 18/09/05 23:29:49&lt;br /&gt;
Cycle: 0&lt;br /&gt;
Index: 457&lt;br /&gt;
Battery ADC: 0&lt;br /&gt;
DS3231 Temperature (C): 22.75&lt;br /&gt;
BME280 Temperature (C): 22.70&lt;br /&gt;
BME280 Pressure   (Pa): 99898.25&lt;br /&gt;
BME280 Humidity    (%): 51.34&lt;br /&gt;
Write OK&lt;br /&gt;
&lt;br /&gt;
FLASH MEMORY DATA DUMP FOLLOWS&lt;br /&gt;
Index	Cycle	Year	Month	Day	Hour	Minute	Second	DS3231 Temperature (C)	Temperature (C)	Pressure (Pa)	Humidity (%)	Battery (10-bit ADC)&lt;br /&gt;
0	0	18	9	5	14	44	45	22.50	22.22	99527.77	45.77	0	&lt;br /&gt;
1	1	18	9	5	14	44	45	22.50	22.19	99525.35	46.32	0	&lt;br /&gt;
2	2	18	9	5	14	44	58	22.75	22.21	99520.82	46.35	0	&lt;br /&gt;
3	3	18	9	5	14	45	10	22.75	22.19	99530.60	46.18	0	&lt;br /&gt;
4	4	18	9	5	14	46	10	22.50	22.23	99526.82	46.55	0	&lt;br /&gt;
5	5	18	9	5	14	47	10	22.25	22.29	99525.55	46.44	0	&lt;br /&gt;
6	6	18	9	5	14	48	10	22.25	22.02	99528.21	47.05	0	&lt;br /&gt;
7	7	18	9	5	14	49	10	22.00	21.90	99535.94	47.95	0	&lt;br /&gt;
8	8	18	9	5	14	50	10	22.00	21.79	99527.93	48.24	0	&lt;br /&gt;
9	9	18	9	5	14	51	10	22.00	21.75	99536.94	48.41	0	&lt;br /&gt;
10	10	18	9	5	14	52	10	22.00	21.74	99537.06	48.59	0	&lt;br /&gt;
11	11	18	9	5	14	53	10	22.00	21.74	99530.03	48.66	0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Serial Console (12/08/2018)==&lt;br /&gt;
In the original version of the software, I use the single button to do most of the setup. This can be very difficult, because the board and button are tiny, and pressing it is required many times, especially for setting the time. Since I implement a simple serial console tool into many of my other projects, I figured it would be a good idea to have one here too, since it is probably fairly likely that you will have the device hooked up to a computer when setting it up anyways.&lt;br /&gt;
&lt;br /&gt;
The serial console I made accepts commands one line at a time, but there is a timeout so that the system does not get stuck waiting forever for a character that never comes. This makes it excellent for programs that send data one line at a time, like the Arduino IDE, but it will not work that well for standard dumb terminal unless you can type very quickly.&lt;br /&gt;
&lt;br /&gt;
The console accepts commands in the form of a single letter, followed by a floating point number, and finally the newline character. For example:&lt;br /&gt;
&amp;lt;pre&amp;gt;y18.00\n&amp;lt;/pre&amp;gt;&lt;br /&gt;
The letter gets passed along as the command, and the number gets passed along as the parameter. It doesn't have to stay floating point either, it will just get converted into an integer or whatever you need. &lt;br /&gt;
&lt;br /&gt;
To use the serial console, set your terminal to 9600 baud and reset the board. Then enter '''e'''. I found that the baudrate of the board's normal 115200 is too high to reliably accept serial input, but works fine for outputing data at high speed during normal operation of the board. &lt;br /&gt;
&lt;br /&gt;
The following table documents all the commands and their functions:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Command !! Parameter !! Description !! Example&lt;br /&gt;
|-&lt;br /&gt;
| e || any || Enters the Serial control mode || &lt;br /&gt;
|-&lt;br /&gt;
| t || 1 - start listing&amp;lt;br&amp;gt;0 - stop listing || Lists continuously the current time. This is helpful when setting it exactly to another source. || t1 - lists time&lt;br /&gt;
|-&lt;br /&gt;
| a || 1 - start listing&amp;lt;br&amp;gt;0 - stop listing || Lists continuously the flash data. This is much slower than using the switch #3 because it is only at 9600 baud. || a1&lt;br /&gt;
|-&lt;br /&gt;
| r || any || Displays the DS3232M RTC registers ||&lt;br /&gt;
|-&lt;br /&gt;
| y || 2 digit year || Sets the year of the RTC || y18 - sets to year 2018&lt;br /&gt;
|-&lt;br /&gt;
| m || 2 digit month || Sets the month of the RTC || m12 - sets to month December&lt;br /&gt;
|-&lt;br /&gt;
| d || 2 digit day || Sets the day of month of the RTC || d8 - sets to day 8&lt;br /&gt;
|-&lt;br /&gt;
| H || 2 digit hour || Sets the hour of the RTC || H32 - sets to hour 1PM&lt;br /&gt;
|-&lt;br /&gt;
| M || 2 digit minute || Sets the minute of the RTC || M32 - sets to minute 32&lt;br /&gt;
|-&lt;br /&gt;
| S || 2 digit second || Sets the seconds of the RTC || S0 - sets to second 0&lt;br /&gt;
|-&lt;br /&gt;
| F || value 12345 || Erases the flash memory || F12345&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Octave Plotter==&lt;br /&gt;
If the output table is changed from tab formatting to CSV (you can do this with Notepad++ or some other text editor), you can use this script in GNU Octave (an open source MatLab alternative) to make a handy plot of the data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
clear;&lt;br /&gt;
clc;&lt;br /&gt;
clf;&lt;br /&gt;
&lt;br /&gt;
raw = load(&amp;quot;j.csv&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
%frame format:&lt;br /&gt;
%[ time, ds3231T, temp, pressure, humidity ]&lt;br /&gt;
&lt;br /&gt;
i = 0;&lt;br /&gt;
while(i&amp;lt;length(raw(:,1)))&lt;br /&gt;
  i++;&lt;br /&gt;
  frame(i,:)=[datenum([2000+raw(i,3),raw(i,4),raw(i,5),raw(i,6),raw(i,7),raw(i,8)]),raw(i,9),raw(i,10),raw(i,11),raw(i,12)];&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
figure(1)&lt;br /&gt;
ha(1)=subplot(3,1,1);&lt;br /&gt;
hold on;&lt;br /&gt;
title(strcat(&amp;quot;Enviroment data: &amp;quot;,datestr(frame(1,1),&amp;quot; mmm 'yy &amp;quot;),&amp;quot; to&amp;quot;,datestr(frame(i,1),&amp;quot; mmm 'yy &amp;quot;)));&lt;br /&gt;
plot(frame(:,1),frame(:,2)*(9/5)+32);&lt;br /&gt;
plot(frame(:,1),frame(:,3)*(9/5)+32);&lt;br /&gt;
datetick(&amp;quot;x&amp;quot;, &amp;quot;mmm/yy&amp;quot;);&lt;br /&gt;
grid on;&lt;br /&gt;
xlabel(&amp;quot;Time&amp;quot;);&lt;br /&gt;
ylabel(&amp;quot;Temperature (F)&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
ha(2)=subplot(3,1,2);&lt;br /&gt;
plot(frame(:,1),frame(:,4));&lt;br /&gt;
datetick(&amp;quot;x&amp;quot;, &amp;quot;mmm/yy&amp;quot;);&lt;br /&gt;
grid on;&lt;br /&gt;
xlabel(&amp;quot;Time&amp;quot;);&lt;br /&gt;
ylabel(&amp;quot;Pressure (Pa)&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
ha(3)=subplot(3,1,3);&lt;br /&gt;
plot(frame(:,1),frame(:,5));&lt;br /&gt;
datetick(&amp;quot;x&amp;quot;, &amp;quot;mmm/yy&amp;quot;);&lt;br /&gt;
grid on;&lt;br /&gt;
xlabel(&amp;quot;Time&amp;quot;);&lt;br /&gt;
ylabel(&amp;quot;Humidity (%)&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
linkaxes(ha, &amp;quot;x&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Details (hackaday.io)==&lt;br /&gt;
This project has been ongoing for a few months now, but I, like many people, don't document projects as well / as often as I should. This project represents a lot of firsts for me, such as the first time working with low power, first time ever soldering SMD components, and only the second project where I have designed a PCB. Most of my projects consist of either breadboards, prototyping boards, or some bizarre freeform/deadbug. I am not an electrical engineer or programmer, just a self taught hobbyist, which does sometimes mean that I have no idea if I am doing something &amp;quot;the right way.&amp;quot; I want to keep my designs simple, using components that are well known, and programming no more complex than Arduino.  With that in mind, the following ramblings explain my thoughts in building this project.&lt;br /&gt;
&lt;br /&gt;
Since this was designed to be buried in a time capsule, the first order of business is how to get power. I had never worked with any low power sleep modes on microcontrollers, but I have plenty of devices that last for years with just batteries. Plenty of projects here on HAD that use a coin cell, but i'd be hard pressed to get 5 years out of one of those. Sounds like a good solution is normal AA batteries. I will use enough so that even when the battery is near dead, the voltage will still be high enough to power the device. It also gives me a bit of extra room to not worry so much about squeezing every last drop of juice out the batteries.&lt;br /&gt;
&lt;br /&gt;
===The Microcontroller===&lt;br /&gt;
It's an ATmega328, nothing special. I thought about using an ATtiny, but since both have the same sleep power requirements, I kept the 328 for some extra pins and more ram, in case I wanted to expand the system a little or do some kind of heavier processing. Yes, this is essentially the same thing as building your own Arduino, but it's simple and it works.&lt;br /&gt;
&lt;br /&gt;
The important thing is to be able to get the 328 into and out of sleep mode. Having never experimented with this mode before, Nick Gammon's tutorial taught me most of what I needed to know. I want this board to run for a long time, so everything must go, like the ADCs, and brownout detection. This brings the sleep current down to an impressive 0.1 uA. I'll re-enable any periperials I need upon waking up.&lt;br /&gt;
&lt;br /&gt;
To wake up the chip, I'll use one of the interrupt pins.&lt;br /&gt;
&lt;br /&gt;
===The Clock===&lt;br /&gt;
I'm using a real-time clock chip to wake up the 328. It's much more accurate than an internal watchdog timer, and lets me add a timestamp to my measurements. I've used the DS3232 rtc before in the form of some inexpensive modules, so I decided to use them for this project as well. This chip contains a real time clock with temperature compensation. The clock accuracy is supposed to be quite good, on the order of a few PPM.&lt;br /&gt;
&lt;br /&gt;
The DS3231 can be powered exclusively through the Vbatt pin instead of the normal Vcc pin. This saves some power according to the datasheet, but also increases the temperature compensation time to once a minute rather than every few seconds. For my purposes, this is fine. &lt;br /&gt;
For interfacing to the 328, 3 pins are used. Two are the i2c pins, and the third is the INT pin. The DS3231 has an alarm function, which can trigger this pin to go LOW when tripped. Connected to an interrupt pin on the 328, it is used to wake up the microcontroller from deep sleep. The 328 then resets the alarm, and reads out a timestamp over i2c. Alarm functions are not inplemented in most of the Arduino libraries I have found, so I wrote a simple one to make working directly with the RTC easier (but it only uses one of the two alarms).&lt;br /&gt;
&lt;br /&gt;
My initial testing with RTC and microcontroller seemed promising, with the circuit waking up and blinking an LED once per minute as I had set it to do. However, when placing the setup in the freezer, the clock was more than a minute off after only a few hours. This is probably because the DS3231s I got from eBay were counterfeit. They work fine for room temperature operation (I have a counterfeit one in a nixie clock I made and it keeps accurate time) but my board needs to survive in freezing temperatures. A few genuine DS3232M from Maxim solved the problem, and my boards now keep accurate time even in the freezer. The M version is a smaller package, and is very slightly less accurate but is supposed to be more durable, using a MEMS oscillator instead of a crystal. The 3232 part number includes some extra battery backed general purpose memory, which I thought might be useful but is still not used yet in my design. The new chip takes the same commands as the old one, so no code changes were needed.&lt;br /&gt;
&lt;br /&gt;
===The Memory===&lt;br /&gt;
Initially, I tried using a 128kb 24lc1025 EEprom as the main storage space, but quickly found it would run out of space in less than the 5 year design life, so I would need to do something else.&lt;br /&gt;
&lt;br /&gt;
While working on this project, I came across the Pearl cave project, where the same idea is used to make environmental sensors for monitoring caves. Their solution was to use an SD card, but it was found that different SD cards used different amounts of power, and that the cards did not like being turned on and off. Rather than an SD card, I thought about using a flash memory chip, maybe in the same way SPIFFS is used in my esp8266 projects. It would also be soldered to the board, with no spring connectors that could corrode or come loose. I settled on the Winbond 25q32fvsig, a 4mbyte flash chip. This would have plenty of room for years of measurements, and also has a sleep mode which takes the power down to a sub-microamp level. I used Marzogh's SPIMemory library for this, which takes care of most of the work in interfacing with the flash. When working with these chips, I found I could not write my 17 byte long measurement frame sequentially like I could with the EEPROM. Increasing the frame size to 32 bytes aligned them with the flash pages and I was able to read and write without errors, as well as fit a few extra measurements in. Now I can store 128k samples, instead of just 128k bytes. At one sampling per hour, this is almost 15 years of storage.&lt;br /&gt;
&lt;br /&gt;
On the board in my picture, you can spot my mistake in the board design. I accidentally connected the reset and write protect pins to GND instead of VCC. This means that the chip is in a constant reset state, and cannot be read or written. With a Dremel, I carefully cut the traces. The reset pin is right next to the VCC pin, so a little excess solder is enough to bridge them. A jumper of thin wire across the top of the chip brings the write protect pin positive as well. This error is fixed in the released version provided in Files. &lt;br /&gt;
&lt;br /&gt;
Initially, all I wanted to measure was temperature. However, that's not a lot of payout for 5 years work. Since the time capsule supposed to be a sealed PVC tube, I thought that it would be interesting to see what the internal pressure was like.  The BME280 can do both temperature and pressure, and also humidity. It also has a low power sleep mode, and communicates over i2c. Since I've never soldered a part that small, I cheated and used a premade breakout board module, which fits on to the i2c header behind the main header.  The header allows for more expansion also, in addition to or if another i2c sensor is to be used.&lt;br /&gt;
&lt;br /&gt;
===Power===&lt;br /&gt;
With the selection of a 3.3v memory chip and sensor, I was limited to running the board at 3.3v instead of the 5v I had originally planned. I didn't think that two AA batteries or a coin cell could last long enough and still be enough voltage to power the board after it lost some charge. Since I care more about reliability with no maintenance, and ton't have too much of a size constraint on the size of the batteries. I thought it would be better to run the components at some stable voltage that did not drop with the batteries (though I recognize that this will increase idle current draw). I could have run the 328 at 4.5v with 3 AAs, but I would still need an additional 3.3v supply and some kind of logic level  conversion. The pearl cave project had the same problem, and solved it by using a simple linear regulator. However, the regular has to be a low quiesent current type, which drew little current on it's own, unlike an lm1117 found on most of the cheap Arduino boards. It took me a while to find a version that had an input voltage greater than 5.5v (in case I decided to use more than 3 cells), and still had good performance. Both the Mcp1703 and HT7773 are good candidates, and come in a sot89 package with the same pinout. I happened to have some of the through hole HT regulators on hand, and used them while I waited for some eBay mcp1703s to arrive. Unfortunately, the eBay mcp1703s turned out to be counterfeit also, and did not meet the claimed quiescent current listed in the datasheet. The current was instead around 70 microamps, more than 10x what I had planned. Again, some genuine samples from Microchip fixed the problem. Interestingly, the HT chips which are produced by a Chinese company and were bought overseas from an unknown seller performed perfectly.&lt;br /&gt;
&lt;br /&gt;
===Operator interface===&lt;br /&gt;
Only seven IO pins on the 328 were being used thus far. Two for i2c, one for the clock wake up pin, and 4 for the SPI flash memory. I added an eighth, a voltage divider of high value resistors on an ADC pin to measure the battery voltage, which has not been fitted yet. But that still leaves lots of other pins open on the 328. I connected four of them to a small 4 position dip switch that I had on hand, three more to colored LEDs, and a button switch to the other interrupt pin. In this way, the board can still be verified working after disconnecting from the programmer. Otherwise, I can't tell if the board is working fine or a brick. The button enables waking up the 328 without waiting for the clock alarm to trip, which could take an hour on the slow setting. The dip switches allow for selecting different models, like setting the clock accurately to the second, or erasing the flash to start over, or dumping the flash contents over serial.&lt;br /&gt;
&lt;br /&gt;
I decided to include a serial interface header on one side, so it will be easier to get the data off of the board when the time comes. I'll use the Arduino bootloader so I can program the chip over serial, since I have many more usb serial adapters than AVR programmers, and the cheap AVR programmers I do have only work 5% of the time anyways. VCC, GND, Tx, Rx, and the 328 /RST pins are brought out on the 5 pin header.&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=TRS-80_-_WTERM&amp;diff=575</id>
		<title>TRS-80 - WTERM</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=TRS-80_-_WTERM&amp;diff=575"/>
		<updated>2020-05-20T12:35:26Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: Alnwlsn moved page TRS-80 - WTERM to WTERM: better title&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[WTERM]]&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=WTERM&amp;diff=574</id>
		<title>WTERM</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=WTERM&amp;diff=574"/>
		<updated>2020-05-20T12:35:26Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: Alnwlsn moved page TRS-80 - WTERM to WTERM: better title&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''WTERM''' is my own TRS-80 terminal emulator (written in Z80 Assembly), for use with my recently completed RS232 adapter. Although the RS232 board I wired up is 100% compatible with the true Radio Shack offering, and works as such, the software was a bit lacking. I tried out a couple programs for using the RS232 port (Omniterm and Matrix ALgorithim), and found a few problems for the things I wanted to do (connect to a Linux box and work with the command line):&lt;br /&gt;
&lt;br /&gt;
* No cursor control. I want an ability to put the cursor anywhere on screen (Ok, so Omniterm does have this, but it has its share of other problems).&lt;br /&gt;
* CR and LF are both the same. This is a big one - on pretty much any other terminal, these have 2 different purposes. CR makes the cursor move to the start of the line, and LF makes the cursor move one row/line down (and stays at the same column). The existing programs presumably use the TRS-80 ROM routines to drive the screen, and I don't think it has these features. Instead, both an LF or CR will make the cursor move to the start of the next line. This is a problem for Linux, which always sends CRLF. Even after editing the terminfo source to try to turn one of these signals off, it seems as though it's baked in. Some of the terminal programs like Omniterm try to suppress LF in a CRLF, but I found it doesn't work very well. Some Linux programs will send one or the other, fully expecting standard usage.&lt;br /&gt;
* Too slow. The TRS-80 is only 1.77 mhz, and the RS232 does not use interrupts. So, anything that happens which is not reading the RS232 data stream in means that bytes can be missed from the data stream. In particular, scrolling the screen upwards in most existing programs seems to use the ROM routines, during which time the UART cannot be read, and we might clip a few letters from a word. Therefore, most terminal programs can only do 600 baud maximum, because any faster and some data will be lost during the time it takes the screen to scroll upwards. &lt;br /&gt;
* No handshaking. RTS/CTS are not used for anything. In most programs they are toggleable for general purpose, but not for handshaking. Omniterm is supposed to support XON/XOFF though.&lt;br /&gt;
&lt;br /&gt;
==WTERM==&lt;br /&gt;
My version roughly emulates a DEC VT-52 (the predecessor to the VT-100). I implemented the ESC codes for moving the cursor around, homing, and clearing stuff, which is most of them. There's no need to do alternate fonts or inverted colors; the TRS-80 character generator is fixed in a ROM. The rest is just fixing the issues discussed previously:&lt;br /&gt;
&lt;br /&gt;
* Custom routines for drawing on the screen, and reading the keyboard. No ROM routines, I access the hardware directly. Not sure if these are any quicker, but by not being in ROM I can put extra stuff inside - between writes onto the screen, and between every keypress, I can read the UART to see if we got new data, meaning nothing gets missed. Also, thanks to these routines:&lt;br /&gt;
** Custom keyboard map. I use the CLEAR, SHIFT and BREAK keys as modifiers. It is possible you type every single ASCII code 0-127.  Enter is the only key handled separate (it just sends CR).&lt;br /&gt;
** CR and LF now do their standard functions, thanks to custom cursor control routines. &lt;br /&gt;
** It should be possible to operate WTERM without a TRS-80 ROM installed. (Why or how you would do this I'm not sure).&lt;br /&gt;
* Circular buffers on UART input and output. The keyboard probably doesn't need a buffer (nobody can type that fast), but the input buffer helps us collect the data quickly when we are doing something else like scrolling the screen or reading a key. Then, when we are not doing anything, we can work on the data we just collected.&lt;br /&gt;
* Hardware flow control - When the buffer gets close to full, the RTS pin goes OFF. When it empties most of the way, it turns it back on. Simple and easy, but missing from existing programs. Why would I spend all that time building up a RS232 board if these signals are never used?&lt;br /&gt;
* Faster. Wterm can keep up with updating the screen at 1200 baud, which is fast enough to not feel excruciatingly slow, and makes things pretty usable.&lt;br /&gt;
&lt;br /&gt;
WTERM is probably not perfect (this is only the 2nd/3rd assembly program I have ever written!) but at least it makes the TRS-80 act like a more standard terminal.&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
[[File:Wterm.zip]] - source and binary. Note that in here it's called &amp;quot;a&amp;quot;. I use this to quickly compile and test my program:&lt;br /&gt;
# Compile with yaza z80 compiler&lt;br /&gt;
# Convert binary to Intel hex with a Python bin2hex program (I didn't write it, it comes as an example for the bin2hex module)&lt;br /&gt;
# Modify Intel Hex with the correct entry point&lt;br /&gt;
# Convert to CMD file using George Phillips's trld converter program&lt;br /&gt;
# Import the file onto a DOS disk image, already pre-configured to execute &amp;quot;a.cmd&amp;quot; on startup&lt;br /&gt;
# Start trs80gp emulator with the disk in drive 0.&lt;br /&gt;
&lt;br /&gt;
The included batch file does all of this within about 3 seconds, making it a very quick way to test my programs after making minor changes. Note that you will need the above programs to do it, though.&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=Main_Page&amp;diff=573</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=Main_Page&amp;diff=573"/>
		<updated>2020-05-20T12:34:53Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: /* Project List */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''This site''' serves documentation for my various projects, mostly for my own reference, but also as a way to share them. Using a wiki for this purpose is certainly overkill, but it is easy to edit and format, and I am familiar with it. Whenever I do a fairly involved project, there's a decent chance I will write up the things I learned on this site. As it stands, this site is still relatively sparse, because I like working on projects more than writing about them, like most people.&lt;br /&gt;
&lt;br /&gt;
This web server and the content it provides is also a project in and of itself. This is running on server hardware and software that I set up myself, so as such even the content you are reading now is experimental. I guarantee no stability or reliable access, although I will do my best to keep this available online for as long as possible (though as of 2020 I have had a server like this running continuously for over 5 years).&lt;br /&gt;
&lt;br /&gt;
==Project List==&lt;br /&gt;
More major projects are listed below, in no particular order. See the list of [[Special:AllPages|all pages here.]]&lt;br /&gt;
&lt;br /&gt;
* [[AWOL Box]] - My take on the infamous Mikal Hart Reverse Geocache™ puzzle&lt;br /&gt;
* [[Nixie Clock 1]] - First completed Nixie clock/display unit&lt;br /&gt;
* [[Nixie Clock 2]] - Second Nixie display&lt;br /&gt;
* [[RC Wheelchair 1]] - Hacking the joystick of an electric wheelchair for remote control&lt;br /&gt;
* [[RC Wheelchair 2]] - A complete high-power h-bridge controller, controls, and radio system.&lt;br /&gt;
* [[Wilson X carriage]] - Improved x-carriage for the Prusa i3 style 3d printer.&lt;br /&gt;
* [[COW arm]] - a robot arm for the wheelchair robot base&lt;br /&gt;
* [[Lora module]] - module that makes using LoRa radio communications easier to work with.&lt;br /&gt;
* [[Environmental logger]] - A time capsule experiment&lt;br /&gt;
* [[Model T speedometer]] - General purpose GPS based speedometer for cars that don't have one. &lt;br /&gt;
* [[Geiger counter]] - my homemade Geiger counter using a SBM20 tube&lt;br /&gt;
* [[Raspberry Pi DVR]] - my main way of recording TV off the air&lt;br /&gt;
* [[Wilson Tetris]] - my version of a Tetris clone (in C)&lt;br /&gt;
* [[Door monitor]] - putting an unused security system to &amp;quot;good&amp;quot; use.&lt;br /&gt;
* [[uScreen]] - general purpose module with esp8266 and Nokia LCD&lt;br /&gt;
* [[6888 Transmitter]] - 6888 tube-based AM transmitter&lt;br /&gt;
* [[Sky Pointer]] - a thingy for pointing out objects in the sky.&lt;br /&gt;
* [[Atlas Gears]] - Replacement lathe/mill changegears&lt;br /&gt;
* [[Smart Response XE]] - Hacking of a school response system&lt;br /&gt;
* [[Floppy disk drive hacking]] - Poking around floppy drives with a logic analyzer&lt;br /&gt;
* [[Coronameter]] - Dedicated screen for pandemic panicking.&lt;br /&gt;
* [[Miscellaneous]] - short things that aren't long enough for their own page&lt;br /&gt;
* [[Unpublished Projects]]&lt;br /&gt;
&lt;br /&gt;
==TRS-80 Stuff==&lt;br /&gt;
* [[TRS-80 Model 1 - Character ROM]]&lt;br /&gt;
* [[TRS-80 Model 1 - Wilson Expansion Interface]] - an Expansion Interface (needed to connect floppy drives and other peripherals to the 1977 TRS-80 microcomputer) &lt;br /&gt;
* [[TRS-80 Plug 'n Power Controller]] - reverse engineering an extinct trs-80 accessory&lt;br /&gt;
&lt;br /&gt;
==Other Services==&lt;br /&gt;
This server has hosted a number of different things since it's original incarnation in high school. Some of the old services are still up (but others are unlisted or taken down due to spam or security risks)&lt;br /&gt;
&lt;br /&gt;
* [http://alnwlsn.ax.lt/lists/ Public Lists] - experimental spam collection&lt;br /&gt;
* [http://alnwlsn.ax.lt/wintercamp/notifier/ Winter Camp Notifier Script] - service that emails you when new Winter Camp discussion takes place&lt;br /&gt;
* [http://alnwlsn.ax.lt/wicm/index.php/Main_Page WICM] - A copy of the Winter Camp encyclopedia translated into MediaWiki&lt;br /&gt;
&lt;br /&gt;
===What is the fish?===&lt;br /&gt;
The green fish is from a game I made when I first learned how to program. Over time, it turned into the logo that I use for my projects.&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=Main_Page&amp;diff=572</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=Main_Page&amp;diff=572"/>
		<updated>2020-05-20T12:31:50Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: /* What is the fish? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''This site''' serves documentation for my various projects, mostly for my own reference, but also as a way to share them. Using a wiki for this purpose is certainly overkill, but it is easy to edit and format, and I am familiar with it. Whenever I do a fairly involved project, there's a decent chance I will write up the things I learned on this site. As it stands, this site is still relatively sparse, because I like working on projects more than writing about them, like most people.&lt;br /&gt;
&lt;br /&gt;
This web server and the content it provides is also a project in and of itself. This is running on server hardware and software that I set up myself, so as such even the content you are reading now is experimental. I guarantee no stability or reliable access, although I will do my best to keep this available online for as long as possible (though as of 2020 I have had a server like this running continuously for over 5 years).&lt;br /&gt;
&lt;br /&gt;
==Project List==&lt;br /&gt;
More major projects are listed below, in no particular order. See the list of [[Special:AllPages|all pages here.]]&lt;br /&gt;
&lt;br /&gt;
* [[AWOL Box]] - My take on the infamous Mikal Hart Reverse Geocache™ puzzle&lt;br /&gt;
* [[Nixie Clock 1]] - First completed Nixie clock/display unit&lt;br /&gt;
* [[Nixie Clock 2]] - Second Nixie display&lt;br /&gt;
* [[RC Wheelchair 1]] - Hacking the joystick of an electric wheelchair for remote control&lt;br /&gt;
* [[RC Wheelchair 2]] - A complete high-power h-bridge controller, controls, and radio system.&lt;br /&gt;
* [[Wilson X carriage]] - Improved x-carriage for the Prusa i3 style 3d printer.&lt;br /&gt;
* [[COW arm]] - a robot arm for the wheelchair robot base&lt;br /&gt;
* [[Lora module]] - module that makes using LoRa radio communications easier to work with.&lt;br /&gt;
* [[TRS-80 Model 1 - Character ROM]]&lt;br /&gt;
* [[TRS-80 Model 1 - Wilson Expansion Interface]] - an Expansion Interface (needed to connect floppy drives and other peripherals to the 1977 TRS-80 microcomputer) &lt;br /&gt;
* [[TRS-80 Plug 'n Power Controller]] - reverse engineering an extinct trs-80 accessory&lt;br /&gt;
* [[Environmental logger]] - A time capsule experiment&lt;br /&gt;
* [[Model T speedometer]] - General purpose GPS based speedometer for cars that don't have one. &lt;br /&gt;
* [[Geiger counter]] - my homemade Geiger counter using a SBM20 tube&lt;br /&gt;
* [[Raspberry Pi DVR]] - my main way of recording TV off the air&lt;br /&gt;
* [[Wilson Tetris]] - my version of a Tetris clone (in C)&lt;br /&gt;
* [[Door monitor]] - putting an unused security system to &amp;quot;good&amp;quot; use.&lt;br /&gt;
* [[uScreen]] - general purpose module with esp8266 and Nokia LCD&lt;br /&gt;
* [[6888 Transmitter]] - 6888 tube-based AM transmitter&lt;br /&gt;
* [[Sky Pointer]] - a thingy for pointing out objects in the sky.&lt;br /&gt;
* [[Atlas Gears]] - Replacement lathe/mill changegears&lt;br /&gt;
* [[Smart Response XE]] - Hacking of a school response system&lt;br /&gt;
* [[Floppy disk drive hacking]] - Poking around floppy drives with a logic analyzer&lt;br /&gt;
* [[Coronameter]] - Dedicated screen for pandemic panicking.&lt;br /&gt;
* [[Miscellaneous]] - short things that aren't long enough for their own page&lt;br /&gt;
* [[Unpublished Projects]]&lt;br /&gt;
&lt;br /&gt;
==Other Services==&lt;br /&gt;
This server has hosted a number of different things since it's original incarnation in high school. Some of the old services are still up (but others are unlisted or taken down due to spam or security risks)&lt;br /&gt;
&lt;br /&gt;
* [http://alnwlsn.ax.lt/lists/ Public Lists] - experimental spam collection&lt;br /&gt;
* [http://alnwlsn.ax.lt/wintercamp/notifier/ Winter Camp Notifier Script] - service that emails you when new Winter Camp discussion takes place&lt;br /&gt;
* [http://alnwlsn.ax.lt/wicm/index.php/Main_Page WICM] - A copy of the Winter Camp encyclopedia translated into MediaWiki&lt;br /&gt;
&lt;br /&gt;
===What is the fish?===&lt;br /&gt;
The green fish is from a game I made when I first learned how to program. Over time, it turned into the logo that I use for my projects.&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=WTERM&amp;diff=571</id>
		<title>WTERM</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=WTERM&amp;diff=571"/>
		<updated>2020-05-20T03:45:42Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: /* Files */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''WTERM''' is my own TRS-80 terminal emulator (written in Z80 Assembly), for use with my recently completed RS232 adapter. Although the RS232 board I wired up is 100% compatible with the true Radio Shack offering, and works as such, the software was a bit lacking. I tried out a couple programs for using the RS232 port (Omniterm and Matrix ALgorithim), and found a few problems for the things I wanted to do (connect to a Linux box and work with the command line):&lt;br /&gt;
&lt;br /&gt;
* No cursor control. I want an ability to put the cursor anywhere on screen (Ok, so Omniterm does have this, but it has its share of other problems).&lt;br /&gt;
* CR and LF are both the same. This is a big one - on pretty much any other terminal, these have 2 different purposes. CR makes the cursor move to the start of the line, and LF makes the cursor move one row/line down (and stays at the same column). The existing programs presumably use the TRS-80 ROM routines to drive the screen, and I don't think it has these features. Instead, both an LF or CR will make the cursor move to the start of the next line. This is a problem for Linux, which always sends CRLF. Even after editing the terminfo source to try to turn one of these signals off, it seems as though it's baked in. Some of the terminal programs like Omniterm try to suppress LF in a CRLF, but I found it doesn't work very well. Some Linux programs will send one or the other, fully expecting standard usage.&lt;br /&gt;
* Too slow. The TRS-80 is only 1.77 mhz, and the RS232 does not use interrupts. So, anything that happens which is not reading the RS232 data stream in means that bytes can be missed from the data stream. In particular, scrolling the screen upwards in most existing programs seems to use the ROM routines, during which time the UART cannot be read, and we might clip a few letters from a word. Therefore, most terminal programs can only do 600 baud maximum, because any faster and some data will be lost during the time it takes the screen to scroll upwards. &lt;br /&gt;
* No handshaking. RTS/CTS are not used for anything. In most programs they are toggleable for general purpose, but not for handshaking. Omniterm is supposed to support XON/XOFF though.&lt;br /&gt;
&lt;br /&gt;
==WTERM==&lt;br /&gt;
My version roughly emulates a DEC VT-52 (the predecessor to the VT-100). I implemented the ESC codes for moving the cursor around, homing, and clearing stuff, which is most of them. There's no need to do alternate fonts or inverted colors; the TRS-80 character generator is fixed in a ROM. The rest is just fixing the issues discussed previously:&lt;br /&gt;
&lt;br /&gt;
* Custom routines for drawing on the screen, and reading the keyboard. No ROM routines, I access the hardware directly. Not sure if these are any quicker, but by not being in ROM I can put extra stuff inside - between writes onto the screen, and between every keypress, I can read the UART to see if we got new data, meaning nothing gets missed. Also, thanks to these routines:&lt;br /&gt;
** Custom keyboard map. I use the CLEAR, SHIFT and BREAK keys as modifiers. It is possible you type every single ASCII code 0-127.  Enter is the only key handled separate (it just sends CR).&lt;br /&gt;
** CR and LF now do their standard functions, thanks to custom cursor control routines. &lt;br /&gt;
** It should be possible to operate WTERM without a TRS-80 ROM installed. (Why or how you would do this I'm not sure).&lt;br /&gt;
* Circular buffers on UART input and output. The keyboard probably doesn't need a buffer (nobody can type that fast), but the input buffer helps us collect the data quickly when we are doing something else like scrolling the screen or reading a key. Then, when we are not doing anything, we can work on the data we just collected.&lt;br /&gt;
* Hardware flow control - When the buffer gets close to full, the RTS pin goes OFF. When it empties most of the way, it turns it back on. Simple and easy, but missing from existing programs. Why would I spend all that time building up a RS232 board if these signals are never used?&lt;br /&gt;
* Faster. Wterm can keep up with updating the screen at 1200 baud, which is fast enough to not feel excruciatingly slow, and makes things pretty usable.&lt;br /&gt;
&lt;br /&gt;
WTERM is probably not perfect (this is only the 2nd/3rd assembly program I have ever written!) but at least it makes the TRS-80 act like a more standard terminal.&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
[[File:Wterm.zip]] - source and binary. Note that in here it's called &amp;quot;a&amp;quot;. I use this to quickly compile and test my program:&lt;br /&gt;
# Compile with yaza z80 compiler&lt;br /&gt;
# Convert binary to Intel hex with a Python bin2hex program (I didn't write it, it comes as an example for the bin2hex module)&lt;br /&gt;
# Modify Intel Hex with the correct entry point&lt;br /&gt;
# Convert to CMD file using George Phillips's trld converter program&lt;br /&gt;
# Import the file onto a DOS disk image, already pre-configured to execute &amp;quot;a.cmd&amp;quot; on startup&lt;br /&gt;
# Start trs80gp emulator with the disk in drive 0.&lt;br /&gt;
&lt;br /&gt;
The included batch file does all of this within about 3 seconds, making it a very quick way to test my programs after making minor changes. Note that you will need the above programs to do it, though.&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=WTERM&amp;diff=570</id>
		<title>WTERM</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=WTERM&amp;diff=570"/>
		<updated>2020-05-20T03:45:05Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''WTERM''' is my own TRS-80 terminal emulator (written in Z80 Assembly), for use with my recently completed RS232 adapter. Although the RS232 board I wired up is 100% compatible with the true Radio Shack offering, and works as such, the software was a bit lacking. I tried out a couple programs for using the RS232 port (Omniterm and Matrix ALgorithim), and found a few problems for the things I wanted to do (connect to a Linux box and work with the command line):&lt;br /&gt;
&lt;br /&gt;
* No cursor control. I want an ability to put the cursor anywhere on screen (Ok, so Omniterm does have this, but it has its share of other problems).&lt;br /&gt;
* CR and LF are both the same. This is a big one - on pretty much any other terminal, these have 2 different purposes. CR makes the cursor move to the start of the line, and LF makes the cursor move one row/line down (and stays at the same column). The existing programs presumably use the TRS-80 ROM routines to drive the screen, and I don't think it has these features. Instead, both an LF or CR will make the cursor move to the start of the next line. This is a problem for Linux, which always sends CRLF. Even after editing the terminfo source to try to turn one of these signals off, it seems as though it's baked in. Some of the terminal programs like Omniterm try to suppress LF in a CRLF, but I found it doesn't work very well. Some Linux programs will send one or the other, fully expecting standard usage.&lt;br /&gt;
* Too slow. The TRS-80 is only 1.77 mhz, and the RS232 does not use interrupts. So, anything that happens which is not reading the RS232 data stream in means that bytes can be missed from the data stream. In particular, scrolling the screen upwards in most existing programs seems to use the ROM routines, during which time the UART cannot be read, and we might clip a few letters from a word. Therefore, most terminal programs can only do 600 baud maximum, because any faster and some data will be lost during the time it takes the screen to scroll upwards. &lt;br /&gt;
* No handshaking. RTS/CTS are not used for anything. In most programs they are toggleable for general purpose, but not for handshaking. Omniterm is supposed to support XON/XOFF though.&lt;br /&gt;
&lt;br /&gt;
==WTERM==&lt;br /&gt;
My version roughly emulates a DEC VT-52 (the predecessor to the VT-100). I implemented the ESC codes for moving the cursor around, homing, and clearing stuff, which is most of them. There's no need to do alternate fonts or inverted colors; the TRS-80 character generator is fixed in a ROM. The rest is just fixing the issues discussed previously:&lt;br /&gt;
&lt;br /&gt;
* Custom routines for drawing on the screen, and reading the keyboard. No ROM routines, I access the hardware directly. Not sure if these are any quicker, but by not being in ROM I can put extra stuff inside - between writes onto the screen, and between every keypress, I can read the UART to see if we got new data, meaning nothing gets missed. Also, thanks to these routines:&lt;br /&gt;
** Custom keyboard map. I use the CLEAR, SHIFT and BREAK keys as modifiers. It is possible you type every single ASCII code 0-127.  Enter is the only key handled separate (it just sends CR).&lt;br /&gt;
** CR and LF now do their standard functions, thanks to custom cursor control routines. &lt;br /&gt;
** It should be possible to operate WTERM without a TRS-80 ROM installed. (Why or how you would do this I'm not sure).&lt;br /&gt;
* Circular buffers on UART input and output. The keyboard probably doesn't need a buffer (nobody can type that fast), but the input buffer helps us collect the data quickly when we are doing something else like scrolling the screen or reading a key. Then, when we are not doing anything, we can work on the data we just collected.&lt;br /&gt;
* Hardware flow control - When the buffer gets close to full, the RTS pin goes OFF. When it empties most of the way, it turns it back on. Simple and easy, but missing from existing programs. Why would I spend all that time building up a RS232 board if these signals are never used?&lt;br /&gt;
* Faster. Wterm can keep up with updating the screen at 1200 baud, which is fast enough to not feel excruciatingly slow, and makes things pretty usable.&lt;br /&gt;
&lt;br /&gt;
WTERM is probably not perfect (this is only the 2nd/3rd assembly program I have ever written!) but at least it makes the TRS-80 act like a more standard terminal.&lt;br /&gt;
&lt;br /&gt;
==Files==&lt;br /&gt;
[[File:Wterm.zip]] - source and binary. Note that in here it's called &amp;quot;a&amp;quot;. I use this to quickly compile and test my program:&lt;br /&gt;
1. Compile with yaza z80 compiler&lt;br /&gt;
2. Convert binary to Intel hex with a Python bin2hex program (I didn't write it, it comes as an example for the bin2hex module)&lt;br /&gt;
3. Modify Intel Hex with the correct entry point&lt;br /&gt;
4. Convert to CMD file using George Phillips's trld converter program&lt;br /&gt;
5. Import the file onto a DOS disk image, already pre-configured to execute &amp;quot;a.cmd&amp;quot; on startup&lt;br /&gt;
6. Start trs80gp emulator with the disk in drive 0.&lt;br /&gt;
&lt;br /&gt;
The included batch file does all of this within about 3 seconds, making it a very quick way to test my programs after making minor changes. Note that you will need the above programs to do it, though.&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=Main_Page&amp;diff=569</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=Main_Page&amp;diff=569"/>
		<updated>2020-05-20T03:34:52Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''This site''' serves documentation for my various projects, mostly for my own reference, but also as a way to share them. Using a wiki for this purpose is certainly overkill, but it is easy to edit and format, and I am familiar with it. Whenever I do a fairly involved project, there's a decent chance I will write up the things I learned on this site. As it stands, this site is still relatively sparse, because I like working on projects more than writing about them, like most people.&lt;br /&gt;
&lt;br /&gt;
This web server and the content it provides is also a project in and of itself. This is running on server hardware and software that I set up myself, so as such even the content you are reading now is experimental. I guarantee no stability or reliable access, although I will do my best to keep this available online for as long as possible (though as of 2020 I have had a server like this running continuously for over 5 years).&lt;br /&gt;
&lt;br /&gt;
==Project List==&lt;br /&gt;
More major projects are listed below, in no particular order. See the list of [[Special:AllPages|all pages here.]]&lt;br /&gt;
&lt;br /&gt;
* [[AWOL Box]] - My take on the infamous Mikal Hart Reverse Geocache™ puzzle&lt;br /&gt;
* [[Nixie Clock 1]] - First completed Nixie clock/display unit&lt;br /&gt;
* [[Nixie Clock 2]] - Second Nixie display&lt;br /&gt;
* [[RC Wheelchair 1]] - Hacking the joystick of an electric wheelchair for remote control&lt;br /&gt;
* [[RC Wheelchair 2]] - A complete high-power h-bridge controller, controls, and radio system.&lt;br /&gt;
* [[Wilson X carriage]] - Improved x-carriage for the Prusa i3 style 3d printer.&lt;br /&gt;
* [[COW arm]] - a robot arm for the wheelchair robot base&lt;br /&gt;
* [[Lora module]] - module that makes using LoRa radio communications easier to work with.&lt;br /&gt;
* [[TRS-80 Model 1 - Character ROM]]&lt;br /&gt;
* [[TRS-80 Model 1 - Wilson Expansion Interface]] - an Expansion Interface (needed to connect floppy drives and other peripherals to the 1977 TRS-80 microcomputer) &lt;br /&gt;
* [[TRS-80 Plug 'n Power Controller]] - reverse engineering an extinct trs-80 accessory&lt;br /&gt;
* [[Environmental logger]] - A time capsule experiment&lt;br /&gt;
* [[Model T speedometer]] - General purpose GPS based speedometer for cars that don't have one. &lt;br /&gt;
* [[Geiger counter]] - my homemade Geiger counter using a SBM20 tube&lt;br /&gt;
* [[Raspberry Pi DVR]] - my main way of recording TV off the air&lt;br /&gt;
* [[Wilson Tetris]] - my version of a Tetris clone (in C)&lt;br /&gt;
* [[Door monitor]] - putting an unused security system to &amp;quot;good&amp;quot; use.&lt;br /&gt;
* [[uScreen]] - general purpose module with esp8266 and Nokia LCD&lt;br /&gt;
* [[6888 Transmitter]] - 6888 tube-based AM transmitter&lt;br /&gt;
* [[Sky Pointer]] - a thingy for pointing out objects in the sky.&lt;br /&gt;
* [[Atlas Gears]] - Replacement lathe/mill changegears&lt;br /&gt;
* [[Smart Response XE]] - Hacking of a school response system&lt;br /&gt;
* [[Floppy disk drive hacking]] - Poking around floppy drives with a logic analyzer&lt;br /&gt;
* [[Coronameter]] - Dedicated screen for pandemic panicking.&lt;br /&gt;
* [[Miscellaneous]] - short things that aren't long enough for their own page&lt;br /&gt;
* [[Unpublished Projects]]&lt;br /&gt;
&lt;br /&gt;
==Other Services==&lt;br /&gt;
This server has hosted a number of different things since it's original incarnation in high school. Some of the old services are still up (but others are unlisted or taken down due to spam or security risks)&lt;br /&gt;
&lt;br /&gt;
* [http://alnwlsn.ax.lt/lists/ Public Lists] - experimental spam collection&lt;br /&gt;
* [http://alnwlsn.ax.lt/wintercamp/notifier/ Winter Camp Notifier Script] - service that emails you when new Winter Camp discussion takes place&lt;br /&gt;
* [http://alnwlsn.ax.lt/wicm/index.php/Main_Page WICM] - A copy of the Winter Camp encyclopedia translated into MediaWiki&lt;br /&gt;
&lt;br /&gt;
===What is the fish?===&lt;br /&gt;
The green fish is from a game I made when I first learned how to program. Over time, it turned into the logo that I use for my projects. If I do have my own logo, that's what it is.&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=File:Wterm.zip&amp;diff=568</id>
		<title>File:Wterm.zip</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=File:Wterm.zip&amp;diff=568"/>
		<updated>2020-05-20T03:33:42Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: source code and binary for my TRS-80 terminal program&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
source code and binary for my TRS-80 terminal program&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=WTERM&amp;diff=567</id>
		<title>WTERM</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=WTERM&amp;diff=567"/>
		<updated>2020-04-13T04:48:39Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''WTERM''' is my own TRS-80 terminal emulator (written in Z80 Assembly), for use with my recently completed RS232 adapter. Although the RS232 board I wired up is 100% compatible with the true Radio Shack offering, and works as such, the software was a bit lacking. I tried out a couple programs for using the RS232 port (Omniterm and Matrix ALgorithim), and found a few problems for the things I wanted to do (connect to a Linux box and work with the command line):&lt;br /&gt;
&lt;br /&gt;
* No cursor control. I want an ability to put the cursor anywhere on screen (Ok, so Omniterm does have this, but it has its share of other problems).&lt;br /&gt;
* CR and LF are both the same. This is a big one - on pretty much any other terminal, these have 2 different purposes. CR makes the cursor move to the start of the line, and LF makes the cursor move one row/line down (and stays at the same column). The existing programs presumably use the TRS-80 ROM routines to drive the screen, and I don't think it has these features. Instead, both an LF or CR will make the cursor move to the start of the next line. This is a problem for Linux, which always sends CRLF. Even after editing the terminfo source to try to turn one of these signals off, it seems as though it's baked in. Some of the terminal programs like Omniterm try to suppress LF in a CRLF, but I found it doesn't work very well. Some Linux programs will send one or the other, fully expecting standard usage.&lt;br /&gt;
* Too slow. The TRS-80 is only 1.77 mhz, and the RS232 does not use interrupts. So, anything that happens which is not reading the RS232 data stream in means that bytes can be missed from the data stream. In particular, scrolling the screen upwards in most existing programs seems to use the ROM routines, during which time the UART cannot be read, and we might clip a few letters from a word. Therefore, most terminal programs can only do 600 baud maximum, because any faster and some data will be lost during the time it takes the screen to scroll upwards. &lt;br /&gt;
* No handshaking. RTS/CTS are not used for anything. In most programs they are toggleable for general purpose, but not for handshaking. Omniterm is supposed to support XON/XOFF though.&lt;br /&gt;
&lt;br /&gt;
==WTERM==&lt;br /&gt;
My version roughly emulates a DEC VT-52 (the predecessor to the VT-100). I implemented the ESC codes for moving the cursor around, homing, and clearing stuff, which is most of them. There's no need to do alternate fonts or inverted colors; the TRS-80 character generator is fixed in a ROM. The rest is just fixing the issues discussed previously:&lt;br /&gt;
&lt;br /&gt;
* Custom routines for drawing on the screen, and reading the keyboard. No ROM routines, I access the hardware directly. Not sure if these are any quicker, but by not being in ROM I can put extra stuff inside - between writes onto the screen, and between every keypress, I can read the UART to see if we got new data, meaning nothing gets missed. Also, thanks to these routines:&lt;br /&gt;
** Custom keyboard map. I use the CLEAR, SHIFT and BREAK keys as modifiers. It is possible you type every single ASCII code 0-127.  Enter is the only key handled separate (it just sends CR).&lt;br /&gt;
** CR and LF now do their standard functions, thanks to custom cursor control routines. &lt;br /&gt;
** It should be possible to operate WTERM without a TRS-80 ROM installed. (Why or how you would do this I'm not sure).&lt;br /&gt;
* Circular buffers on UART input and output. The keyboard probably doesn't need a buffer (nobody can type that fast), but the input buffer helps us collect the data quickly when we are doing something else like scrolling the screen or reading a key. Then, when we are not doing anything, we can work on the data we just collected.&lt;br /&gt;
* Hardware flow control - When the buffer gets close to full, the RTS pin goes OFF. When it empties most of the way, it turns it back on. Simple and easy, but missing from existing programs. Why would I spend all that time building up a RS232 board if these signals are never used?&lt;br /&gt;
* Faster. Wterm can keep up with updating the screen at 1200 baud, which is fast enough to not feel excruciatingly slow, and makes things pretty usable.&lt;br /&gt;
&lt;br /&gt;
WTERM is probably not perfect (this is only the 2nd/3rd assembly program I have ever written!) but at least it makes the TRS-80 act like a more standard terminal.&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=WTERM&amp;diff=566</id>
		<title>WTERM</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=WTERM&amp;diff=566"/>
		<updated>2020-04-13T04:36:40Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''WTERM''' is my own TRS-80 terminal emulator (written in Z80 Assembly), for use with my recently completed RS232 adapter. Although the RS232 board I wired up is 100% compatible with the true Radio Shack offering, and works as such, the software was a bit lacking. I tried out a couple programs for using the RS232 port (Omniterm and Matrix ALgorithim), and found a few problems for the things I wanted to do (connect to a Linux box and work with the command line):&lt;br /&gt;
&lt;br /&gt;
* No cursor control. I want an ability to put the cursor anywhere on screen (Omniterm does have this, but it has other problems).&lt;br /&gt;
* CR and LF are both the same. This is a big one - on pretty much any other terminal, these have 2 different purposes. CR makes the cursor move to the start of the line, and LF makes the cursor move one row/line down (and stays at the same column). The existing programs presumably use the TRS-80 ROM routines to drive the screen, and I don't think it has these features. Instead, either a LF or CR will make the cursor move to the start of the next line. This is a problem for Linux, which always sends CRLF. Even after editing the terminfo source to try to turn one of these signals off, it seems as though it's baked in. Some of the terminal programs like Omniterm try to suppress one or the other, but I found it doesn't work very well. Some Linux programs will send one or the other, fully expecting standard usage.&lt;br /&gt;
* Too slow. The TRS-80 is only 1.77 mhz, and the RS232 does not use interrupts. So, anything that happens which is not reading the RS232 data stream in means that things can be easily missed. In particular, scrolling the screen upwards in most existing programs seems to use the ROM routines, during which time the UART cannot be read. Therefore, most terminal programs can only do 600 baud maximum, because any faster and some data will be lost during the time it takes the screen to scroll upwards. &lt;br /&gt;
* No handshaking. RTS/CTS are not used for anything. In most programs they are toggleable for general purpose, but not for handshaking. Omniterm is supposed to support XON/XOFF though.&lt;br /&gt;
&lt;br /&gt;
My version roughly emulates a DEC VT-52 (the predecessor to the VT-100). It uses ESC codes which are shorter, and there are less of them, making implementation easier. I implemented the ESC codes for moving the cursor around, homing, and clearing stuff, which is most of them. There's no need to do alternate fonts or inverted colors; the TRS-80 character generator is fixed in a ROM. The rest is just improvements (especially on the issues discussed previously):&lt;br /&gt;
&lt;br /&gt;
* Custom routines for drawing on the screen, and reading the keyboard. No ROM routines, I access the hardware directly. Not sure if these are any quicker, but by not being in ROM I can put extra stuff inside - between writes onto the screen, and between every keypress, I can read the UART to see if we got new data, meaning nothing gets missed. Also, thanks to these routines:&lt;br /&gt;
** Custom keyboard map. I use the CLEAR, SHIFT and BREAK keys as modifiers. It is possible you type every single ASCII code 0-127.  Enter is the only key handled separate (it just sends CR).&lt;br /&gt;
** CR and LF now do their standard functions, thanks to custom cursor control routines. &lt;br /&gt;
* Circular buffers on UART input and output. The keyboard probably doesn't need a buffer (nobody can type that fast), but the input buffer helps us collect the data quickly when we are doing something else like scrolling the screen or reading a key. Then, when we are not doing anything, we can work on the data we just collected.&lt;br /&gt;
* Hardware flow control - When the buffer gets close to full, the RTS pin goes OFF. When it empties most of the way, it turns it back on. Simple and easy, but missing from existing programs. Why would I spend all that time building up a RS232 board if these signals are never used.&lt;br /&gt;
* Faster. Wterm can keep up with updating the screen at 1200 baud, which is fast enough to not feel excruciatingly slow, and makes things very usable.&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=WTERM&amp;diff=565</id>
		<title>WTERM</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=WTERM&amp;diff=565"/>
		<updated>2020-04-13T04:12:31Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: Created page with &amp;quot;'''WTERM''' is my own TRS-80 terminal emulator (written in Z80 Assembly), for use with my recently completed RS232 adapter. Although the RS232 board I wired up is 100% compati...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''WTERM''' is my own TRS-80 terminal emulator (written in Z80 Assembly), for use with my recently completed RS232 adapter. Although the RS232 board I wired up is 100% compatible with the true Radio Shack offering, and works as such, the software was a bit lacking. I tried out a couple programs for using the RS232 port (Omniterm and Matrix ALgorithim), and found a few problems for the things I wanted to do (connect to a Linux box and work with the command line):&lt;br /&gt;
&lt;br /&gt;
* No cursor control. I want an ability to put the cursor anywhere on screen (Omniterm does have this, but it has other problems).&lt;br /&gt;
* CR and LF are both the same. This is a big one - on pretty much any other terminal, these have 2 different purposes. CR makes the cursor move to the start of the line, and LF makes the cursor move one row/line down (and stays at the same column). The existing programs presumably use the TRS-80 ROM routines to drive the screen, and I don't think it has these features. Instead, either a LF or CR will make the cursor move to the start of the next line. This is a problem for Linux, which always sends CRLF. Even after editing the terminfo source to try to turn one of these signals off, it seems as though it's baked in. Some of the terminal programs like Omniterm try to suppress one or the other, but I found it doesn't work very well. Some Linux programs will send one or the other, fully expecting standard usage.&lt;br /&gt;
* Too slow. The TRS-80 is only 1.77 mhz, and the RS232 does not use interrupts. So, anything that happens which is not reading the RS232 data stream in means that things can be easily missed. In particular, scrolling the screen upwards in most existing programs seems to use the ROM routines, during which time the UART cannot be read. Therefore, most terminal programs can only do 600 baud maximum, because any faster and some data will be lost during the time it takes the screen to scroll upwards. &lt;br /&gt;
* No handshaking. RTS/CTS are not used for anything. In most programs they are toggleable for general purpose, but not for handshaking. Omniterm is supposed to support XON/XOFF though.&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=TRS-80_Model_1_-_Wilson_Expansion_Interface&amp;diff=564</id>
		<title>TRS-80 Model 1 - Wilson Expansion Interface</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=TRS-80_Model_1_-_Wilson_Expansion_Interface&amp;diff=564"/>
		<updated>2020-04-13T03:44:45Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: /* RS-232 board - 3-24-2020 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:Trs80ei2-proto.jpg|thumb|Completed prototype featuring extra RAM, SD and DD floppy controllers, RTC heartbeat, SD Card and shared 0x3000 RAM]]&lt;br /&gt;
The '''TRS-80 Model 1''' originally comprised just the keyboard unit (containing the cpu, video diver, keyboard, cassette reader circuitry and 16k or ram) and monitor. Later, Radio Shack introduced the '''Expansion Interface (EI)''', which was another unit that sat behind the keyboard and added up to 32K more ram, a floppy disk controller, and some extra logic to control a printer or other accessories. Other features could be added to the expansion interface, including a doubler that added extra capacity to floppy disks or an rs232 board.&lt;br /&gt;
&lt;br /&gt;
When I got my first TRS-80 in 2018, all I got was the keyboard. I had to make my own adapters to connect a standard NTSC monitor, cassette recorder, and I even rewound a transformer to provide the combination of different voltages that the TRS-80 needs for power. However, since I eventually wanted to add more memory and run some of the DOS operating systems; this meant I would have to come up with an Expansion Interface. Unfortunately, most of the original EIs online, even ones that could not be confirmed working, were priced higher than I originally bought the keyboard. The schematics for the EI are in the Radio Shack Service manuals, which are easily found online, and the EI is made using almost entirely using 74 series logic. Since I happened to have a large stock of 74 logic chips and some prototyping boards, I decided to make my own EI. Doing this would also allow the freedom to change the wiring around for experiments while not destroying a piece of irreplaceable vintage hardware.&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
[[File:Ei2-block-diagran.png|thumb|Block diagram of my EI so far]]&lt;br /&gt;
My EI contains:&lt;br /&gt;
&lt;br /&gt;
* Blinkenlights - Connected to address bus, data bus, and control signals.&lt;br /&gt;
* 32K RAM (using a 128K SRAM chip because that's what I happened to have; the rest is not yet used)&lt;br /&gt;
* INS1771N floppy disk controller for FM encoded disks (fully compatible with the original FD1771 in a real EI, can therefore read the nonstandard DAM records used by TRSDOS)&lt;br /&gt;
* WD2793 floppy controller for MFM encoded disks, set up like a Percom Doubler&amp;lt;ref&amp;gt;[http://www.trs-80.org/percom-doubler/ Percom Doubler]&amp;lt;/ref&amp;gt; but this particular chip also has good internal data separation too, saving a few chips.&lt;br /&gt;
* 1K dual port SRAM, placed at TRS-80 memory address 0x3000-0x33ff, which is unused address space in a normal Model 1 system. This ram is shared between the trs80 and the atmega microcontoller.&lt;br /&gt;
* ATMEGA1284p microcontroller, running the [https://github.com/MCUdude/MightyCore Mightycore] bootloader; this makes it compatible with and able to be programmed by Arduino software. &lt;br /&gt;
* SD card, connected to Arduino&lt;br /&gt;
&lt;br /&gt;
Things not covered yet are:&lt;br /&gt;
* Printer port&lt;br /&gt;
* TRS-80 RS232 port&lt;br /&gt;
* Screen printer port&lt;br /&gt;
* Extra cassette port / switch&lt;br /&gt;
&lt;br /&gt;
==Theory of operation / Features==&lt;br /&gt;
[[File:Trs80ei2-address-decoder.PNG|thumb|Simulation of prototype address decoder / glue logic]]&lt;br /&gt;
Most of my system works like a normal EI, at least as far as the floppy controllers and extra system memory goes. I mainly drew inspiration for the wiring from the TRS-80 technical manuals and schematics. In this version, some sections are taken verbatim from the original schematics, like the six chips used for the clock divider. I found a design for a double density disk adapter in an old copy of Northern Bytes&amp;lt;ref&amp;gt;Northern Bytes, Volume 7, Number 5[https://archive.org/details/Northern_Bytes_Volume_7_Number_5_1985_Alternate_Source_Information_Outlet_The_US]&amp;lt;/ref&amp;gt;. However, the address decoding and buffering logic is my own design, done with 74 series logic. Programmable logic might make things more compact, but there is a steep learning curve (I have not found anything like Arduino for programmable logic) and the PAL devices I have are no longer in manufacturing. Besides, traditional 74 series logic seems to fit better with the spirit of the project.&lt;br /&gt;
&lt;br /&gt;
I have not implemented any power supply for the board, just some filtering; because I needed a standard ATX power supply to drive floppy drives, I decided to use the +5, -5, and +12 volt supplies from that instead.&lt;br /&gt;
&lt;br /&gt;
===ATMEGA Section===&lt;br /&gt;
The special new features come from the combination of the dual port RAM and ATMEGA microcontroller. A few TRS-80 projects I have looked at place some extra memory at TRS-80 address 3000H (which is otherwise completely unused in a stock machine), for extra code or other features. I have placed a Dual port RAM at this location, which means the TRS-80 can read, write, and execute code at this section, adding an extra 1K of general purpose RAM to the system. However, this is dual port RAM, meaning that it can be accessed by two devices '''at the same time'''. I connected the other side of the dual port RAM to an ATMEGA1284P, which is a modern microcontroller which runs at 16MHz and has 16K ram, an impressive system in it's own right when compared to the TRS-80. The microcontroller has an attached SD card for tons of space for storage, and its own operating system, with the express goal of monitoring and managing the shared memory section. &lt;br /&gt;
&lt;br /&gt;
My additions to the TRS-80 are broken down into two programs:&lt;br /&gt;
*'''SH'''ared '''R'''am '''I'''nterface '''P'''rogram - a short z80 program that the TRS-80 runs from the shared dual port RAM&lt;br /&gt;
*'''A'''tmega '''C'''ontrol '''P'''rogram '''T'''erminal - the Arduino sketch that runs on the Atmega, and provides a whole host of functions to read, write and modify data and access hardware on the TRS-80 (via SHRIP).&lt;br /&gt;
&lt;br /&gt;
ACPT command-line functions include:&lt;br /&gt;
* Local (shared dual port RAM) memory functions, which directly change data on the shared RAM. Addresses for these commands range from 000-3ff.&lt;br /&gt;
** &amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt; - display entire contends of RAM (in hex editor-like grid)&lt;br /&gt;
** &amp;lt;code&amp;gt;lr haddress&amp;lt;/code&amp;gt; - reads single byte&lt;br /&gt;
** &amp;lt;code&amp;gt;ll file.bin hstartaddress&amp;lt;/code&amp;gt; - load a dump file to RAM, starting at the given address&lt;br /&gt;
** &amp;lt;code&amp;gt;ld file.bin hstartaddress hendaddress&amp;lt;/code&amp;gt; - dumps a section of memory to a file on the SD card&lt;br /&gt;
* TRS-80 memory functions, working through the monitor program on the shared memory. It must be running for these to work.&lt;br /&gt;
** &amp;lt;code&amp;gt;rs hstartaddress hendaddress&amp;lt;/code&amp;gt; - works like ls, but you can spec sections of Z80 memory instead of the whole thing.&lt;br /&gt;
** &amp;lt;code&amp;gt;lr / ll / ld&amp;lt;/code&amp;gt; - work like their local ram equivalents, but now the address range is the whole Z80 address space: 0000-ffff&lt;br /&gt;
** &amp;lt;code&amp;gt;j address&amp;lt;/code&amp;gt; - jump to code at ''address''&lt;br /&gt;
** &amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; - exit the monitor program and Return to normal TRS-80 functioning.&lt;br /&gt;
** &amp;lt;code&amp;gt;p&amp;lt;/code&amp;gt; - Pause the TRS-80 and run the monitor code.&lt;br /&gt;
** &amp;lt;code&amp;gt;rv file.bin hstartaddress&amp;lt;/code&amp;gt; - verify a file matches a section of RAM&lt;br /&gt;
* SD file commands, special for managing files on the SD card.&lt;br /&gt;
** &amp;lt;code&amp;gt;dir&amp;lt;/code&amp;gt; - shows list of files and their sizes&lt;br /&gt;
** &amp;lt;code&amp;gt;del file.bin&amp;lt;/code&amp;gt; - delete a file&lt;br /&gt;
* File send/receive. Using a special terminal program like teraterm, you can transfer files to and from the SD card without turning off the system and taking it out. There is no special protocol (like XMODEM); files are just sent as raw binary streams.&lt;br /&gt;
** &amp;lt;code&amp;gt;xs file.bin&amp;lt;/code&amp;gt; - send raw file over serial port. If you log these bytes, you can reconstruct the file. &lt;br /&gt;
** &amp;lt;code&amp;gt;xr file.bin&amp;lt;/code&amp;gt; - receive raw file over serial port. Using a terminal program with xon/xoff, send the binary file. &lt;br /&gt;
&lt;br /&gt;
This is all well and good, but SHRIP can only work when the Z80 cpu is running the code, stored inside the shared RAM. One way to get to it is to type SYSTEM ↵ / 12288 ↵ from BASIC to start executing code from address 3000H. But, it sure would be nice to be able to use the monitor program whenever I want, without typing in anything. If I am in DOS or inside a program, there's no way to make the Z80 jump to address 3000H without exiting it. To this end, I decided to modify the TRS-80 ROM. I had already placed an EPROM inside the TRS-80 case to update to the R/S ROM version. I replaced the routine at 002BH with a jump to 3000H. The replaced routine is the part that detects which key is pressed on the TRS-80 keyboard, meaning that it gets run all the time. We can place the rest of the 002BH code at the end of SHRIP, so it can get back to normal operation if we aren't using any SHRIP functions. &lt;br /&gt;
&lt;br /&gt;
Being in the middle of the keyboard routine, the monitor program also has the opportunity to return alternate data than what the keyboard is actually doing. The monitor program checks a byte of shared ram at address 3306H, and if it is nonzero, it will return that value as if the keyboard typed it. This means we can use the microcontroller to type things on the TRS-80 without ever touching the keyboard. I implemented two commands on the microcontroller command line interface to do this:&lt;br /&gt;
* &amp;lt;code&amp;gt;k sometext-to-type&amp;lt;/code&amp;gt; - everything after the &amp;quot;k &amp;quot; is typed on the TRS-80 keyboard.&lt;br /&gt;
**It also works with escape characters, like &amp;lt;code&amp;gt;\n&amp;lt;/code&amp;gt; for eNter and &amp;lt;code&amp;gt;\k&amp;lt;/code&amp;gt; for breaK, &amp;lt;code&amp;gt;\u \d \l \r&amp;lt;/code&amp;gt; for the direction keys, and &amp;lt;code&amp;gt;\c&amp;lt;/code&amp;gt; for the clear key.&lt;br /&gt;
* &amp;lt;code&amp;gt;ks file.txt&amp;lt;/code&amp;gt; - an entire file on the SD card is typed out over the keyboard. Escape characters aren't used here, it just sends the file as if it were a sequence of keyboard scan codes (most of the keyboard is ASCII).&lt;br /&gt;
&lt;br /&gt;
====Files====&lt;br /&gt;
Revision 6 - [[File:Trs80ei2-v6.zip]]&lt;br /&gt;
&lt;br /&gt;
Revision 7 - [[File:Trs80ei2-v7.zip]]&lt;br /&gt;
&lt;br /&gt;
==Hardware/Construction==&lt;br /&gt;
I constructed this on protoboard. Many of these types of projects use wire wrapping, but I don't have any wire wrapping sockets or any tools for doing this. Instead, I used the wire wrap-esque technique where wires jump to different points on the bottom of the board, and are soldered in place. This looks incredibly messy, and it is extremely easy to burn existing wires with the iron (if you look closely, you can see where I have fixed burnt wires with liquid electrical tape.) However, this technique does allow for a comparatively high chip density on the board, because none of the PCB area is used to carry signals. &lt;br /&gt;
&lt;br /&gt;
Most of my chips are socketed, as pretty much all of them are either very old and fragile, or new and very cheap and of questionable quality. The sockets make it very easy to fix bad chips, especially since the bottom of these sockets usually have a separate, free wire on most of the pins. &lt;br /&gt;
&lt;br /&gt;
On most of my chips, I have applied a label, making it easy to see the function on every pin. I make my labels using a thermal printer that was originally used to print price stickers in a store. The type of thermal labels used will turn black when they get hot. I have used this property more than once to detect a bad chip on my board (the chips often heat up when they go bad).&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
trs80ei2-base-bottom.jpg|base-bottom&lt;br /&gt;
Trs80ei2-base-top.jpg|base-top&lt;br /&gt;
Trs80ei2-doubler-bottom.jpg|doubler-bottom&lt;br /&gt;
Trs80ei2-doubler-top.jpg|doubler-top&lt;br /&gt;
Trs80ei2-floppy-bottom.jpg|floppy-bottom&lt;br /&gt;
Trs80ei2-floppy-top.jpg|floppy-top&lt;br /&gt;
Trs80ei2-lights-top.jpg|lights-top&lt;br /&gt;
Trs80ei2-lights-bottom.jpg|lights-bottom&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Log==&lt;br /&gt;
===Debugging a bad RAM chip - May 31, 2019===&lt;br /&gt;
While running the TRS-80 today, I noticed that I was not able to read files reliably under LDOS, but DoubleDos worked just as well as ever. I know that I had LDOS working when I first completed the expansion interface, so something was clearly wrong. Upon further investigation, I noticed that the errors only occurred when the double density driver (FDUBL) was loaded in LDOS. When the driver was loaded, I experienced complete instability when trying to read disks, and I could not format double density disks at all; the system would simply hang instead of stepping the disk drive. Initially, I thought there might be something wrong with my doubler board, after all, it does sit on top of the stack of boards and is slightly crooked. But, double density still worked fine under DoubleDos, so this was probably not the issue. &lt;br /&gt;
&lt;br /&gt;
My next thought was some kind of ram problem. I did notice that when loading data directly to ram with my Atmega monitor program, I noticed some verify errors at system address 0xb300. At first, I thought that this might be a stack pointer that was getting overwritten, but then I remembered that my monitor places it's stack within the shared 1k RAM. I tried directly writing to 0xb300 and could not get a consistent write, though the bytes before and after it worked fine. Could this verify error be the problem? This section of system memory would be within the 32K of SRAM provided by my expansion board. I am using a 128K chip here because that's what I had, but the chips I have are not credibly sourced, and I have had problems in this board with clone chips of 74 logic. &lt;br /&gt;
&lt;br /&gt;
I tried replacing the RAM and the problem did indeed go away, and I was finally able to fully load entire (48K) ram snapshots using my monitor program with no verify errors. At the same time, I found LDOS once again able to read and write disks reliably. So it seems that the ram was bad, although I find it strange that only a single byte was affected.&lt;br /&gt;
&lt;br /&gt;
===FreHD clone experiment - June 3, 2019===&lt;br /&gt;
Before I had completed the floppy controllers, I had began work on a frehd clone that would fit on my weird stacked EI, which I could never get to work properly. Developed by Frederic Vecoven, The [http://www.vecoven.com/trs80/trs80.html frehd project is an open source project] that emulates a hard drive in a manner consistent with the original mfm disk controllers you could get back in the day, though there is [http://members.iinet.net.au/~ianmav/trs80/emulator.htm now a closed source version assembled and sold] by Ian Mavric of Australia. After getting all my floppy drives working, having two floppy drives simulated with Goteks (more on this later), I really don't need a frehd too, but I had already made the board (following the schematic in the original frehd open source release), and so it was worth another try.&lt;br /&gt;
&lt;br /&gt;
My first experiment was to try a program called [http://48k.ca/trsvid.html TRSVID] (developed by George Phillips) which can display full motion video on the trs-80 using a frehd, although you need to change the firmware on the frehd PIC microcontroller first. Having constructed the entire board myself, I had a programmer too in order to program the PIC in the first place, and after loading the Phillips firmware, I too was able to watch grainy videos on my TRS-80, with equally crummy sound. Although the quality isn't great by modern standards, this is an extremely impressive demo considering the TRS-80's age. You only need to look at [[Wilson Tetris|my Tetris program]] to see the normal limitations of the TRS-80 graphics.&lt;br /&gt;
&lt;br /&gt;
However, I ran into trouble when trying to actually use my clone frehd for its intended purpose: as a hard drive. The freHD comes with some utilities to help mount and create hard disks, and &amp;lt;code&amp;gt;VHDUTL&amp;lt;/code&amp;gt; seems to work ok. However, trying to actually format the disks using &amp;lt;code&amp;gt;RSHARD1&amp;lt;/code&amp;gt; (using 840 tracks, 6 heads and 140 sectors), gave mixed results. Sometimes, the system would lock up after I finished filling out the prompts to mount the disk using &amp;lt;code&amp;gt;SYSTEM (DRIVE=4,DISABLE,DRIVER=&amp;quot;RSHARD1&amp;quot;)&amp;lt;/code&amp;gt; Other times, it would lock up after trying the &amp;lt;code&amp;gt;RSFORM1&amp;lt;/code&amp;gt; program which is supposed to format the disk. Most of the time though, these commands would work successfully, even verifying the format correctly. But, I found that afterwards, the system might lock up when trying to display the directory. &lt;br /&gt;
&lt;br /&gt;
In the event that all of the above setup works properly, I was still left with one final problem, which is that when I copy a file to the emulated hard drive, it doen't always seem to be a complete copy. I could copy my Tetris program and get it to load at least once or twice, but my other version (Metris, with more pieces) would not run no matter what I tried. Soon afterwards, my hard disk got corrupted and I would have to start all over.&lt;br /&gt;
&lt;br /&gt;
Incidentally, I also noticed that the &amp;lt;code&amp;gt;IMPORT2&amp;lt;/code&amp;gt; command which is used to transfer files from the freHD SD card to TRS-80 devices like floppy disks would also only make corrupt files. Perhaps if I examine the files, or make some test payloads, I may be able to determine what's going on, but that will need to wait for another day. I know that Ian made some changes the software, either to the frehd, trs-80 or both, to make the frehd work on a Model 1, but as of now I am not sure what those changes are. &lt;br /&gt;
&lt;br /&gt;
It may be better to continue my ATMEGA/dual port ram solution anyway, as this has the advantage of modifying any memory location.&lt;br /&gt;
&lt;br /&gt;
===Version 7 of software - June 19, 2019===&lt;br /&gt;
It seems like there is some kind of problem with the 32K ram on the board, most likely a bad connection or something, which is near impossible to find. I sometimes find that reseating the SRAM chip or swapping it for a diffrent one will usually work, even though all of my ram chips test good using my EPROM programmer as a tester. &lt;br /&gt;
&lt;br /&gt;
Anyways, I decided I needed to add a RAM test feature to the atmega operator interface. Before this, I created two 48k files of all 1s (0xFF) and all 0s (0x00), which I would load each with the '''rl''' command, which verifies each byte as it is written, allowing me to find RAM errors. Since this is a rather long command to type out, I decided to include a command &amp;lt;code&amp;gt;tstram&amp;lt;/code&amp;gt; that would write all 1s, then all 0s, for each ram address over the entire 48K ram. An optional number after the command will specify the number of loops to do, from once to a whole bunch.&lt;br /&gt;
&lt;br /&gt;
Additionally, I discovered a bug that would not allow the 3rd noun to be counted, making all commands think that there were only 2 nouns maximum (nouns=parameters after the command), making commands like &amp;lt;code&amp;gt;rd&amp;lt;/code&amp;gt; inoperable. As a workaround for v7, I increased the allowed number of nouns to 4, which means that we still have the same error, but now the parser thinks that we can't have more than 3 nouns, which is the most that any command has. I will fix this properly in the next releases.&lt;br /&gt;
&lt;br /&gt;
===v9 - Aug 2019===&lt;br /&gt;
Since the last log entry, I have indeed found the cause of the RAM issue - an address pin (one of the high address pins that isn't used) was left floating! This means that the ram would randomly switch to a different bank; this would be as if the ram randomly erases everything sometimes.&lt;br /&gt;
&lt;br /&gt;
The reason I discovered this was because I wired these extra address pins to some atmega pins, for an experiment. Now, through the Atmega, I can switch the 32k of system ram between 4 banks. &lt;br /&gt;
&lt;br /&gt;
I also am experimenting with an on-board terminal for the Atmega terminal control interface. From the little z80 program running in the dual port RAM, we already have access to the TRS-80 keyboard. To get access to the screen, we can call address 0x033a, which writes the character in A onto the screen, then advances the cursor. There are a few other routines &amp;lt;ref&amp;gt;http://www.trs-80.org/trs-80-rom-routines-documented/&amp;lt;/ref&amp;gt; that can help manage the screen, so that makes things like advancing the cursor and scrolling down something that I don't need to implement myself in my own program. I added calls to these routines in SHRIP, meaning that SHRIP-ACPT can now write stuff directly to the screen. I even added extra functions to the ACPT sketch, to make the Arduino stream functions like Serial.print also write to the TRS-80 screen.&lt;br /&gt;
&lt;br /&gt;
Ok, so we now have access to the TRS-80 screen and keyboard. The last step is to make ACPT available on the TRS-80 directly, without needing to use an extra serial terminal on the Atmega. I added a keyboard shortcut to SHRIP, which will enter into an on-screen ACP terminal. All I need to do is first copy the contents of the screen and save the cursor position so I can get back exactly where I left off. I store the image data in one of the extra banks of SRAM, since it is literally wasted space until now. When I'm done with the terminal, I issue the exit command, and the screen data is copied back onto the screen.&lt;br /&gt;
&lt;br /&gt;
This means it is now possible to load machine code directly from the Atmega SD card into trs-80 memory, and execute it, without ever hooking up the trsei2 board to another computer. You can't really load full ram images yet, because the onscreen terminal itself uses some of the system RAM, but overall, the system is much more accessible now.&lt;br /&gt;
&lt;br /&gt;
===v12 - December 2019===&lt;br /&gt;
With 128K of SRAM on the expansion board, it would really be nice if that extra ram could be accessed from the Z80 side of things, so that I don't have to go through the Atmega. This should also speed up assembly programs that use the extra RAM. &lt;br /&gt;
In the memory mapped IO section that breaks out address 37E0-37EC, I used 74LS138 octal decoders, so I also have lines for 37F0-37FC that are not used in a standard TRS-80. Therefore, I connected a 74LS175 latch to data pins D0-D1, and latch on writes to memory address 37FC. This makes the extra RAM (4 blocks of 32K, placed in the upper half of Z80 memory map) accessible from the Z80 directly.&lt;br /&gt;
&lt;br /&gt;
Hardware also has a parallel port added, which is nothing more than a couple buffers and latches. I needed this to make the winter camp latrine monitor program, since I discovered that it needs a printer to work properly.&lt;br /&gt;
&lt;br /&gt;
===RS-232 board - 3-24-2020===&lt;br /&gt;
The real expansion interfaces had an option for a RS-232 serial port, which enabled communication to the outside world; most modems required an RS232 port. Since I want to hook this up to a raspberry pi eventually, I'll need one.&lt;br /&gt;
&lt;br /&gt;
The original board used a tr1602 UART and BR1941 as the baud rate generator. I couldn't find either of these chips, but I did find a TR1865 and COM8136, which are chips used in the Model 4, and are compatible. They also do not require a -5 or 12V supply, not that it's a big deal as I have an ATX supply as the power source. There's also a few supporting 74 series components, and some buffers to convert the 5V signals to the RS-232 voltages. In place of the buffers, I will try to use MAX232s instead, since I have a bunch of them for some reason. &lt;br /&gt;
&lt;br /&gt;
For address decoding, I am trying a GAL22v10. Normally I don't like using GAL programmable logic, because these chips are no longer made (there's still a lot though), and they sort of obscure how everything works. In this case, I found myself short of 74 logic chips in the middle of a global pandemic, so why not try something else and replace 4-5 74 chips instead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
edit 4/12/2020 - It works! I implemented everything in a real Model I RS232 board, except:&lt;br /&gt;
* I didn't implement the DIP switches. This is just some dip switches hooked to an IO port input. You can set everything from software. Model 3 removed these.&lt;br /&gt;
** This left me with an extra unused output pin on the GAL22V10. I found some TIL-311 hexadecimal LED displays / decoders, so I wired them up to IO port 0xEC. Now, I can have a debug display, simply OUT (0ech),a to write to the display.&lt;br /&gt;
* Did not implement the original TTL-RS232 voltage converters (I didn't have any of these ICs) What I did have was a bunch of MAX232 converter ICs, which do the same thing but have a capacitor charge pump to get the RS232 voltage. Also, per chip, they have just 2 TTL--&amp;gt;232 and 2 232--&amp;gt;TTL paths. So, I implemented tx/rx, rts/cts, dtr/dsr and DCD. RI is not connected to any 232 pin, and the TRS-80 thinks it is always OFF. &lt;br /&gt;
&lt;br /&gt;
I wrote my own terminal emulator after finding the TRS-80 offerings lacking. See [[TRS-80 - WTERM]].&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=TRS-80_Model_1_-_Wilson_Expansion_Interface&amp;diff=563</id>
		<title>TRS-80 Model 1 - Wilson Expansion Interface</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=TRS-80_Model_1_-_Wilson_Expansion_Interface&amp;diff=563"/>
		<updated>2020-03-25T02:51:01Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: /* RS-232 board - 3-24-2020 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:Trs80ei2-proto.jpg|thumb|Completed prototype featuring extra RAM, SD and DD floppy controllers, RTC heartbeat, SD Card and shared 0x3000 RAM]]&lt;br /&gt;
The '''TRS-80 Model 1''' originally comprised just the keyboard unit (containing the cpu, video diver, keyboard, cassette reader circuitry and 16k or ram) and monitor. Later, Radio Shack introduced the '''Expansion Interface (EI)''', which was another unit that sat behind the keyboard and added up to 32K more ram, a floppy disk controller, and some extra logic to control a printer or other accessories. Other features could be added to the expansion interface, including a doubler that added extra capacity to floppy disks or an rs232 board.&lt;br /&gt;
&lt;br /&gt;
When I got my first TRS-80 in 2018, all I got was the keyboard. I had to make my own adapters to connect a standard NTSC monitor, cassette recorder, and I even rewound a transformer to provide the combination of different voltages that the TRS-80 needs for power. However, since I eventually wanted to add more memory and run some of the DOS operating systems; this meant I would have to come up with an Expansion Interface. Unfortunately, most of the original EIs online, even ones that could not be confirmed working, were priced higher than I originally bought the keyboard. The schematics for the EI are in the Radio Shack Service manuals, which are easily found online, and the EI is made using almost entirely using 74 series logic. Since I happened to have a large stock of 74 logic chips and some prototyping boards, I decided to make my own EI. Doing this would also allow the freedom to change the wiring around for experiments while not destroying a piece of irreplaceable vintage hardware.&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
[[File:Ei2-block-diagran.png|thumb|Block diagram of my EI so far]]&lt;br /&gt;
My EI contains:&lt;br /&gt;
&lt;br /&gt;
* Blinkenlights - Connected to address bus, data bus, and control signals.&lt;br /&gt;
* 32K RAM (using a 128K SRAM chip because that's what I happened to have; the rest is not yet used)&lt;br /&gt;
* INS1771N floppy disk controller for FM encoded disks (fully compatible with the original FD1771 in a real EI, can therefore read the nonstandard DAM records used by TRSDOS)&lt;br /&gt;
* WD2793 floppy controller for MFM encoded disks, set up like a Percom Doubler&amp;lt;ref&amp;gt;[http://www.trs-80.org/percom-doubler/ Percom Doubler]&amp;lt;/ref&amp;gt; but this particular chip also has good internal data separation too, saving a few chips.&lt;br /&gt;
* 1K dual port SRAM, placed at TRS-80 memory address 0x3000-0x33ff, which is unused address space in a normal Model 1 system. This ram is shared between the trs80 and the atmega microcontoller.&lt;br /&gt;
* ATMEGA1284p microcontroller, running the [https://github.com/MCUdude/MightyCore Mightycore] bootloader; this makes it compatible with and able to be programmed by Arduino software. &lt;br /&gt;
* SD card, connected to Arduino&lt;br /&gt;
&lt;br /&gt;
Things not covered yet are:&lt;br /&gt;
* Printer port&lt;br /&gt;
* TRS-80 RS232 port&lt;br /&gt;
* Screen printer port&lt;br /&gt;
* Extra cassette port / switch&lt;br /&gt;
&lt;br /&gt;
==Theory of operation / Features==&lt;br /&gt;
[[File:Trs80ei2-address-decoder.PNG|thumb|Simulation of prototype address decoder / glue logic]]&lt;br /&gt;
Most of my system works like a normal EI, at least as far as the floppy controllers and extra system memory goes. I mainly drew inspiration for the wiring from the TRS-80 technical manuals and schematics. In this version, some sections are taken verbatim from the original schematics, like the six chips used for the clock divider. I found a design for a double density disk adapter in an old copy of Northern Bytes&amp;lt;ref&amp;gt;Northern Bytes, Volume 7, Number 5[https://archive.org/details/Northern_Bytes_Volume_7_Number_5_1985_Alternate_Source_Information_Outlet_The_US]&amp;lt;/ref&amp;gt;. However, the address decoding and buffering logic is my own design, done with 74 series logic. Programmable logic might make things more compact, but there is a steep learning curve (I have not found anything like Arduino for programmable logic) and the PAL devices I have are no longer in manufacturing. Besides, traditional 74 series logic seems to fit better with the spirit of the project.&lt;br /&gt;
&lt;br /&gt;
I have not implemented any power supply for the board, just some filtering; because I needed a standard ATX power supply to drive floppy drives, I decided to use the +5, -5, and +12 volt supplies from that instead.&lt;br /&gt;
&lt;br /&gt;
===ATMEGA Section===&lt;br /&gt;
The special new features come from the combination of the dual port RAM and ATMEGA microcontroller. A few TRS-80 projects I have looked at place some extra memory at TRS-80 address 3000H (which is otherwise completely unused in a stock machine), for extra code or other features. I have placed a Dual port RAM at this location, which means the TRS-80 can read, write, and execute code at this section, adding an extra 1K of general purpose RAM to the system. However, this is dual port RAM, meaning that it can be accessed by two devices '''at the same time'''. I connected the other side of the dual port RAM to an ATMEGA1284P, which is a modern microcontroller which runs at 16MHz and has 16K ram, an impressive system in it's own right when compared to the TRS-80. The microcontroller has an attached SD card for tons of space for storage, and its own operating system, with the express goal of monitoring and managing the shared memory section. &lt;br /&gt;
&lt;br /&gt;
My additions to the TRS-80 are broken down into two programs:&lt;br /&gt;
*'''SH'''ared '''R'''am '''I'''nterface '''P'''rogram - a short z80 program that the TRS-80 runs from the shared dual port RAM&lt;br /&gt;
*'''A'''tmega '''C'''ontrol '''P'''rogram '''T'''erminal - the Arduino sketch that runs on the Atmega, and provides a whole host of functions to read, write and modify data and access hardware on the TRS-80 (via SHRIP).&lt;br /&gt;
&lt;br /&gt;
ACPT command-line functions include:&lt;br /&gt;
* Local (shared dual port RAM) memory functions, which directly change data on the shared RAM. Addresses for these commands range from 000-3ff.&lt;br /&gt;
** &amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt; - display entire contends of RAM (in hex editor-like grid)&lt;br /&gt;
** &amp;lt;code&amp;gt;lr haddress&amp;lt;/code&amp;gt; - reads single byte&lt;br /&gt;
** &amp;lt;code&amp;gt;ll file.bin hstartaddress&amp;lt;/code&amp;gt; - load a dump file to RAM, starting at the given address&lt;br /&gt;
** &amp;lt;code&amp;gt;ld file.bin hstartaddress hendaddress&amp;lt;/code&amp;gt; - dumps a section of memory to a file on the SD card&lt;br /&gt;
* TRS-80 memory functions, working through the monitor program on the shared memory. It must be running for these to work.&lt;br /&gt;
** &amp;lt;code&amp;gt;rs hstartaddress hendaddress&amp;lt;/code&amp;gt; - works like ls, but you can spec sections of Z80 memory instead of the whole thing.&lt;br /&gt;
** &amp;lt;code&amp;gt;lr / ll / ld&amp;lt;/code&amp;gt; - work like their local ram equivalents, but now the address range is the whole Z80 address space: 0000-ffff&lt;br /&gt;
** &amp;lt;code&amp;gt;j address&amp;lt;/code&amp;gt; - jump to code at ''address''&lt;br /&gt;
** &amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; - exit the monitor program and Return to normal TRS-80 functioning.&lt;br /&gt;
** &amp;lt;code&amp;gt;p&amp;lt;/code&amp;gt; - Pause the TRS-80 and run the monitor code.&lt;br /&gt;
** &amp;lt;code&amp;gt;rv file.bin hstartaddress&amp;lt;/code&amp;gt; - verify a file matches a section of RAM&lt;br /&gt;
* SD file commands, special for managing files on the SD card.&lt;br /&gt;
** &amp;lt;code&amp;gt;dir&amp;lt;/code&amp;gt; - shows list of files and their sizes&lt;br /&gt;
** &amp;lt;code&amp;gt;del file.bin&amp;lt;/code&amp;gt; - delete a file&lt;br /&gt;
* File send/receive. Using a special terminal program like teraterm, you can transfer files to and from the SD card without turning off the system and taking it out. There is no special protocol (like XMODEM); files are just sent as raw binary streams.&lt;br /&gt;
** &amp;lt;code&amp;gt;xs file.bin&amp;lt;/code&amp;gt; - send raw file over serial port. If you log these bytes, you can reconstruct the file. &lt;br /&gt;
** &amp;lt;code&amp;gt;xr file.bin&amp;lt;/code&amp;gt; - receive raw file over serial port. Using a terminal program with xon/xoff, send the binary file. &lt;br /&gt;
&lt;br /&gt;
This is all well and good, but SHRIP can only work when the Z80 cpu is running the code, stored inside the shared RAM. One way to get to it is to type SYSTEM ↵ / 12288 ↵ from BASIC to start executing code from address 3000H. But, it sure would be nice to be able to use the monitor program whenever I want, without typing in anything. If I am in DOS or inside a program, there's no way to make the Z80 jump to address 3000H without exiting it. To this end, I decided to modify the TRS-80 ROM. I had already placed an EPROM inside the TRS-80 case to update to the R/S ROM version. I replaced the routine at 002BH with a jump to 3000H. The replaced routine is the part that detects which key is pressed on the TRS-80 keyboard, meaning that it gets run all the time. We can place the rest of the 002BH code at the end of SHRIP, so it can get back to normal operation if we aren't using any SHRIP functions. &lt;br /&gt;
&lt;br /&gt;
Being in the middle of the keyboard routine, the monitor program also has the opportunity to return alternate data than what the keyboard is actually doing. The monitor program checks a byte of shared ram at address 3306H, and if it is nonzero, it will return that value as if the keyboard typed it. This means we can use the microcontroller to type things on the TRS-80 without ever touching the keyboard. I implemented two commands on the microcontroller command line interface to do this:&lt;br /&gt;
* &amp;lt;code&amp;gt;k sometext-to-type&amp;lt;/code&amp;gt; - everything after the &amp;quot;k &amp;quot; is typed on the TRS-80 keyboard.&lt;br /&gt;
**It also works with escape characters, like &amp;lt;code&amp;gt;\n&amp;lt;/code&amp;gt; for eNter and &amp;lt;code&amp;gt;\k&amp;lt;/code&amp;gt; for breaK, &amp;lt;code&amp;gt;\u \d \l \r&amp;lt;/code&amp;gt; for the direction keys, and &amp;lt;code&amp;gt;\c&amp;lt;/code&amp;gt; for the clear key.&lt;br /&gt;
* &amp;lt;code&amp;gt;ks file.txt&amp;lt;/code&amp;gt; - an entire file on the SD card is typed out over the keyboard. Escape characters aren't used here, it just sends the file as if it were a sequence of keyboard scan codes (most of the keyboard is ASCII).&lt;br /&gt;
&lt;br /&gt;
====Files====&lt;br /&gt;
Revision 6 - [[File:Trs80ei2-v6.zip]]&lt;br /&gt;
&lt;br /&gt;
Revision 7 - [[File:Trs80ei2-v7.zip]]&lt;br /&gt;
&lt;br /&gt;
==Hardware/Construction==&lt;br /&gt;
I constructed this on protoboard. Many of these types of projects use wire wrapping, but I don't have any wire wrapping sockets or any tools for doing this. Instead, I used the wire wrap-esque technique where wires jump to different points on the bottom of the board, and are soldered in place. This looks incredibly messy, and it is extremely easy to burn existing wires with the iron (if you look closely, you can see where I have fixed burnt wires with liquid electrical tape.) However, this technique does allow for a comparatively high chip density on the board, because none of the PCB area is used to carry signals. &lt;br /&gt;
&lt;br /&gt;
Most of my chips are socketed, as pretty much all of them are either very old and fragile, or new and very cheap and of questionable quality. The sockets make it very easy to fix bad chips, especially since the bottom of these sockets usually have a separate, free wire on most of the pins. &lt;br /&gt;
&lt;br /&gt;
On most of my chips, I have applied a label, making it easy to see the function on every pin. I make my labels using a thermal printer that was originally used to print price stickers in a store. The type of thermal labels used will turn black when they get hot. I have used this property more than once to detect a bad chip on my board (the chips often heat up when they go bad).&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
trs80ei2-base-bottom.jpg|base-bottom&lt;br /&gt;
Trs80ei2-base-top.jpg|base-top&lt;br /&gt;
Trs80ei2-doubler-bottom.jpg|doubler-bottom&lt;br /&gt;
Trs80ei2-doubler-top.jpg|doubler-top&lt;br /&gt;
Trs80ei2-floppy-bottom.jpg|floppy-bottom&lt;br /&gt;
Trs80ei2-floppy-top.jpg|floppy-top&lt;br /&gt;
Trs80ei2-lights-top.jpg|lights-top&lt;br /&gt;
Trs80ei2-lights-bottom.jpg|lights-bottom&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Log==&lt;br /&gt;
===Debugging a bad RAM chip - May 31, 2019===&lt;br /&gt;
While running the TRS-80 today, I noticed that I was not able to read files reliably under LDOS, but DoubleDos worked just as well as ever. I know that I had LDOS working when I first completed the expansion interface, so something was clearly wrong. Upon further investigation, I noticed that the errors only occurred when the double density driver (FDUBL) was loaded in LDOS. When the driver was loaded, I experienced complete instability when trying to read disks, and I could not format double density disks at all; the system would simply hang instead of stepping the disk drive. Initially, I thought there might be something wrong with my doubler board, after all, it does sit on top of the stack of boards and is slightly crooked. But, double density still worked fine under DoubleDos, so this was probably not the issue. &lt;br /&gt;
&lt;br /&gt;
My next thought was some kind of ram problem. I did notice that when loading data directly to ram with my Atmega monitor program, I noticed some verify errors at system address 0xb300. At first, I thought that this might be a stack pointer that was getting overwritten, but then I remembered that my monitor places it's stack within the shared 1k RAM. I tried directly writing to 0xb300 and could not get a consistent write, though the bytes before and after it worked fine. Could this verify error be the problem? This section of system memory would be within the 32K of SRAM provided by my expansion board. I am using a 128K chip here because that's what I had, but the chips I have are not credibly sourced, and I have had problems in this board with clone chips of 74 logic. &lt;br /&gt;
&lt;br /&gt;
I tried replacing the RAM and the problem did indeed go away, and I was finally able to fully load entire (48K) ram snapshots using my monitor program with no verify errors. At the same time, I found LDOS once again able to read and write disks reliably. So it seems that the ram was bad, although I find it strange that only a single byte was affected.&lt;br /&gt;
&lt;br /&gt;
===FreHD clone experiment - June 3, 2019===&lt;br /&gt;
Before I had completed the floppy controllers, I had began work on a frehd clone that would fit on my weird stacked EI, which I could never get to work properly. Developed by Frederic Vecoven, The [http://www.vecoven.com/trs80/trs80.html frehd project is an open source project] that emulates a hard drive in a manner consistent with the original mfm disk controllers you could get back in the day, though there is [http://members.iinet.net.au/~ianmav/trs80/emulator.htm now a closed source version assembled and sold] by Ian Mavric of Australia. After getting all my floppy drives working, having two floppy drives simulated with Goteks (more on this later), I really don't need a frehd too, but I had already made the board (following the schematic in the original frehd open source release), and so it was worth another try.&lt;br /&gt;
&lt;br /&gt;
My first experiment was to try a program called [http://48k.ca/trsvid.html TRSVID] (developed by George Phillips) which can display full motion video on the trs-80 using a frehd, although you need to change the firmware on the frehd PIC microcontroller first. Having constructed the entire board myself, I had a programmer too in order to program the PIC in the first place, and after loading the Phillips firmware, I too was able to watch grainy videos on my TRS-80, with equally crummy sound. Although the quality isn't great by modern standards, this is an extremely impressive demo considering the TRS-80's age. You only need to look at [[Wilson Tetris|my Tetris program]] to see the normal limitations of the TRS-80 graphics.&lt;br /&gt;
&lt;br /&gt;
However, I ran into trouble when trying to actually use my clone frehd for its intended purpose: as a hard drive. The freHD comes with some utilities to help mount and create hard disks, and &amp;lt;code&amp;gt;VHDUTL&amp;lt;/code&amp;gt; seems to work ok. However, trying to actually format the disks using &amp;lt;code&amp;gt;RSHARD1&amp;lt;/code&amp;gt; (using 840 tracks, 6 heads and 140 sectors), gave mixed results. Sometimes, the system would lock up after I finished filling out the prompts to mount the disk using &amp;lt;code&amp;gt;SYSTEM (DRIVE=4,DISABLE,DRIVER=&amp;quot;RSHARD1&amp;quot;)&amp;lt;/code&amp;gt; Other times, it would lock up after trying the &amp;lt;code&amp;gt;RSFORM1&amp;lt;/code&amp;gt; program which is supposed to format the disk. Most of the time though, these commands would work successfully, even verifying the format correctly. But, I found that afterwards, the system might lock up when trying to display the directory. &lt;br /&gt;
&lt;br /&gt;
In the event that all of the above setup works properly, I was still left with one final problem, which is that when I copy a file to the emulated hard drive, it doen't always seem to be a complete copy. I could copy my Tetris program and get it to load at least once or twice, but my other version (Metris, with more pieces) would not run no matter what I tried. Soon afterwards, my hard disk got corrupted and I would have to start all over.&lt;br /&gt;
&lt;br /&gt;
Incidentally, I also noticed that the &amp;lt;code&amp;gt;IMPORT2&amp;lt;/code&amp;gt; command which is used to transfer files from the freHD SD card to TRS-80 devices like floppy disks would also only make corrupt files. Perhaps if I examine the files, or make some test payloads, I may be able to determine what's going on, but that will need to wait for another day. I know that Ian made some changes the software, either to the frehd, trs-80 or both, to make the frehd work on a Model 1, but as of now I am not sure what those changes are. &lt;br /&gt;
&lt;br /&gt;
It may be better to continue my ATMEGA/dual port ram solution anyway, as this has the advantage of modifying any memory location.&lt;br /&gt;
&lt;br /&gt;
===Version 7 of software - June 19, 2019===&lt;br /&gt;
It seems like there is some kind of problem with the 32K ram on the board, most likely a bad connection or something, which is near impossible to find. I sometimes find that reseating the SRAM chip or swapping it for a diffrent one will usually work, even though all of my ram chips test good using my EPROM programmer as a tester. &lt;br /&gt;
&lt;br /&gt;
Anyways, I decided I needed to add a RAM test feature to the atmega operator interface. Before this, I created two 48k files of all 1s (0xFF) and all 0s (0x00), which I would load each with the '''rl''' command, which verifies each byte as it is written, allowing me to find RAM errors. Since this is a rather long command to type out, I decided to include a command &amp;lt;code&amp;gt;tstram&amp;lt;/code&amp;gt; that would write all 1s, then all 0s, for each ram address over the entire 48K ram. An optional number after the command will specify the number of loops to do, from once to a whole bunch.&lt;br /&gt;
&lt;br /&gt;
Additionally, I discovered a bug that would not allow the 3rd noun to be counted, making all commands think that there were only 2 nouns maximum (nouns=parameters after the command), making commands like &amp;lt;code&amp;gt;rd&amp;lt;/code&amp;gt; inoperable. As a workaround for v7, I increased the allowed number of nouns to 4, which means that we still have the same error, but now the parser thinks that we can't have more than 3 nouns, which is the most that any command has. I will fix this properly in the next releases.&lt;br /&gt;
&lt;br /&gt;
===v9 - Aug 2019===&lt;br /&gt;
Since the last log entry, I have indeed found the cause of the RAM issue - an address pin (one of the high address pins that isn't used) was left floating! This means that the ram would randomly switch to a different bank; this would be as if the ram randomly erases everything sometimes.&lt;br /&gt;
&lt;br /&gt;
The reason I discovered this was because I wired these extra address pins to some atmega pins, for an experiment. Now, through the Atmega, I can switch the 32k of system ram between 4 banks. &lt;br /&gt;
&lt;br /&gt;
I also am experimenting with an on-board terminal for the Atmega terminal control interface. From the little z80 program running in the dual port RAM, we already have access to the TRS-80 keyboard. To get access to the screen, we can call address 0x033a, which writes the character in A onto the screen, then advances the cursor. There are a few other routines &amp;lt;ref&amp;gt;http://www.trs-80.org/trs-80-rom-routines-documented/&amp;lt;/ref&amp;gt; that can help manage the screen, so that makes things like advancing the cursor and scrolling down something that I don't need to implement myself in my own program. I added calls to these routines in SHRIP, meaning that SHRIP-ACPT can now write stuff directly to the screen. I even added extra functions to the ACPT sketch, to make the Arduino stream functions like Serial.print also write to the TRS-80 screen.&lt;br /&gt;
&lt;br /&gt;
Ok, so we now have access to the TRS-80 screen and keyboard. The last step is to make ACPT available on the TRS-80 directly, without needing to use an extra serial terminal on the Atmega. I added a keyboard shortcut to SHRIP, which will enter into an on-screen ACP terminal. All I need to do is first copy the contents of the screen and save the cursor position so I can get back exactly where I left off. I store the image data in one of the extra banks of SRAM, since it is literally wasted space until now. When I'm done with the terminal, I issue the exit command, and the screen data is copied back onto the screen.&lt;br /&gt;
&lt;br /&gt;
This means it is now possible to load machine code directly from the Atmega SD card into trs-80 memory, and execute it, without ever hooking up the trsei2 board to another computer. You can't really load full ram images yet, because the onscreen terminal itself uses some of the system RAM, but overall, the system is much more accessible now.&lt;br /&gt;
&lt;br /&gt;
===v12 - December 2019===&lt;br /&gt;
With 128K of SRAM on the expansion board, it would really be nice if that extra ram could be accessed from the Z80 side of things, so that I don't have to go through the Atmega. This should also speed up assembly programs that use the extra RAM. &lt;br /&gt;
In the memory mapped IO section that breaks out address 37E0-37EC, I used 74LS138 octal decoders, so I also have lines for 37F0-37FC that are not used in a standard TRS-80. Therefore, I connected a 74LS175 latch to data pins D0-D1, and latch on writes to memory address 37FC. This makes the extra RAM (4 blocks of 32K, placed in the upper half of Z80 memory map) accessible from the Z80 directly.&lt;br /&gt;
&lt;br /&gt;
Hardware also has a parallel port added, which is nothing more than a couple buffers and latches. I needed this to make the winter camp latrine monitor program, since I discovered that it needs a printer to work properly.&lt;br /&gt;
&lt;br /&gt;
===RS-232 board - 3-24-2020===&lt;br /&gt;
The real expansion interfaces had an option for a RS-232 serial port, which enabled communication to the outside world; most modems required an RS232 port. Since I want to hook this up to a raspberry pi eventually, I'll need one.&lt;br /&gt;
&lt;br /&gt;
The original board used a tr1602 UART and BR1941 as the baud rate generator. I couldn't find either of these chips, but I did find a TR1865 and COM8136, which are chips used in the Model 4, and are compatible. They also do not require a -5 or 12V supply, not that it's a big deal as I have an ATX supply as the power source. There's also a few supporting 74 series components, and some buffers to convert the 5V signals to the RS-232 voltages. In place of the buffers, I will try to use MAX232s instead, since I have a bunch of them for some reason. &lt;br /&gt;
&lt;br /&gt;
For address decoding, I am trying a GAL22v10. Normally I don't like using GAL programmable logic, because these chips are no longer made (there's still a lot though), and they sort of obscure how everything works. In this case, I found myself short of 74 logic chips in the middle of a global pandemic, so why not try something else and replace 4-5 74 chips instead.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=TRS-80_Model_1_-_Wilson_Expansion_Interface&amp;diff=562</id>
		<title>TRS-80 Model 1 - Wilson Expansion Interface</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=TRS-80_Model_1_-_Wilson_Expansion_Interface&amp;diff=562"/>
		<updated>2020-03-25T02:18:48Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: /* Log */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:Trs80ei2-proto.jpg|thumb|Completed prototype featuring extra RAM, SD and DD floppy controllers, RTC heartbeat, SD Card and shared 0x3000 RAM]]&lt;br /&gt;
The '''TRS-80 Model 1''' originally comprised just the keyboard unit (containing the cpu, video diver, keyboard, cassette reader circuitry and 16k or ram) and monitor. Later, Radio Shack introduced the '''Expansion Interface (EI)''', which was another unit that sat behind the keyboard and added up to 32K more ram, a floppy disk controller, and some extra logic to control a printer or other accessories. Other features could be added to the expansion interface, including a doubler that added extra capacity to floppy disks or an rs232 board.&lt;br /&gt;
&lt;br /&gt;
When I got my first TRS-80 in 2018, all I got was the keyboard. I had to make my own adapters to connect a standard NTSC monitor, cassette recorder, and I even rewound a transformer to provide the combination of different voltages that the TRS-80 needs for power. However, since I eventually wanted to add more memory and run some of the DOS operating systems; this meant I would have to come up with an Expansion Interface. Unfortunately, most of the original EIs online, even ones that could not be confirmed working, were priced higher than I originally bought the keyboard. The schematics for the EI are in the Radio Shack Service manuals, which are easily found online, and the EI is made using almost entirely using 74 series logic. Since I happened to have a large stock of 74 logic chips and some prototyping boards, I decided to make my own EI. Doing this would also allow the freedom to change the wiring around for experiments while not destroying a piece of irreplaceable vintage hardware.&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
[[File:Ei2-block-diagran.png|thumb|Block diagram of my EI so far]]&lt;br /&gt;
My EI contains:&lt;br /&gt;
&lt;br /&gt;
* Blinkenlights - Connected to address bus, data bus, and control signals.&lt;br /&gt;
* 32K RAM (using a 128K SRAM chip because that's what I happened to have; the rest is not yet used)&lt;br /&gt;
* INS1771N floppy disk controller for FM encoded disks (fully compatible with the original FD1771 in a real EI, can therefore read the nonstandard DAM records used by TRSDOS)&lt;br /&gt;
* WD2793 floppy controller for MFM encoded disks, set up like a Percom Doubler&amp;lt;ref&amp;gt;[http://www.trs-80.org/percom-doubler/ Percom Doubler]&amp;lt;/ref&amp;gt; but this particular chip also has good internal data separation too, saving a few chips.&lt;br /&gt;
* 1K dual port SRAM, placed at TRS-80 memory address 0x3000-0x33ff, which is unused address space in a normal Model 1 system. This ram is shared between the trs80 and the atmega microcontoller.&lt;br /&gt;
* ATMEGA1284p microcontroller, running the [https://github.com/MCUdude/MightyCore Mightycore] bootloader; this makes it compatible with and able to be programmed by Arduino software. &lt;br /&gt;
* SD card, connected to Arduino&lt;br /&gt;
&lt;br /&gt;
Things not covered yet are:&lt;br /&gt;
* Printer port&lt;br /&gt;
* TRS-80 RS232 port&lt;br /&gt;
* Screen printer port&lt;br /&gt;
* Extra cassette port / switch&lt;br /&gt;
&lt;br /&gt;
==Theory of operation / Features==&lt;br /&gt;
[[File:Trs80ei2-address-decoder.PNG|thumb|Simulation of prototype address decoder / glue logic]]&lt;br /&gt;
Most of my system works like a normal EI, at least as far as the floppy controllers and extra system memory goes. I mainly drew inspiration for the wiring from the TRS-80 technical manuals and schematics. In this version, some sections are taken verbatim from the original schematics, like the six chips used for the clock divider. I found a design for a double density disk adapter in an old copy of Northern Bytes&amp;lt;ref&amp;gt;Northern Bytes, Volume 7, Number 5[https://archive.org/details/Northern_Bytes_Volume_7_Number_5_1985_Alternate_Source_Information_Outlet_The_US]&amp;lt;/ref&amp;gt;. However, the address decoding and buffering logic is my own design, done with 74 series logic. Programmable logic might make things more compact, but there is a steep learning curve (I have not found anything like Arduino for programmable logic) and the PAL devices I have are no longer in manufacturing. Besides, traditional 74 series logic seems to fit better with the spirit of the project.&lt;br /&gt;
&lt;br /&gt;
I have not implemented any power supply for the board, just some filtering; because I needed a standard ATX power supply to drive floppy drives, I decided to use the +5, -5, and +12 volt supplies from that instead.&lt;br /&gt;
&lt;br /&gt;
===ATMEGA Section===&lt;br /&gt;
The special new features come from the combination of the dual port RAM and ATMEGA microcontroller. A few TRS-80 projects I have looked at place some extra memory at TRS-80 address 3000H (which is otherwise completely unused in a stock machine), for extra code or other features. I have placed a Dual port RAM at this location, which means the TRS-80 can read, write, and execute code at this section, adding an extra 1K of general purpose RAM to the system. However, this is dual port RAM, meaning that it can be accessed by two devices '''at the same time'''. I connected the other side of the dual port RAM to an ATMEGA1284P, which is a modern microcontroller which runs at 16MHz and has 16K ram, an impressive system in it's own right when compared to the TRS-80. The microcontroller has an attached SD card for tons of space for storage, and its own operating system, with the express goal of monitoring and managing the shared memory section. &lt;br /&gt;
&lt;br /&gt;
My additions to the TRS-80 are broken down into two programs:&lt;br /&gt;
*'''SH'''ared '''R'''am '''I'''nterface '''P'''rogram - a short z80 program that the TRS-80 runs from the shared dual port RAM&lt;br /&gt;
*'''A'''tmega '''C'''ontrol '''P'''rogram '''T'''erminal - the Arduino sketch that runs on the Atmega, and provides a whole host of functions to read, write and modify data and access hardware on the TRS-80 (via SHRIP).&lt;br /&gt;
&lt;br /&gt;
ACPT command-line functions include:&lt;br /&gt;
* Local (shared dual port RAM) memory functions, which directly change data on the shared RAM. Addresses for these commands range from 000-3ff.&lt;br /&gt;
** &amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt; - display entire contends of RAM (in hex editor-like grid)&lt;br /&gt;
** &amp;lt;code&amp;gt;lr haddress&amp;lt;/code&amp;gt; - reads single byte&lt;br /&gt;
** &amp;lt;code&amp;gt;ll file.bin hstartaddress&amp;lt;/code&amp;gt; - load a dump file to RAM, starting at the given address&lt;br /&gt;
** &amp;lt;code&amp;gt;ld file.bin hstartaddress hendaddress&amp;lt;/code&amp;gt; - dumps a section of memory to a file on the SD card&lt;br /&gt;
* TRS-80 memory functions, working through the monitor program on the shared memory. It must be running for these to work.&lt;br /&gt;
** &amp;lt;code&amp;gt;rs hstartaddress hendaddress&amp;lt;/code&amp;gt; - works like ls, but you can spec sections of Z80 memory instead of the whole thing.&lt;br /&gt;
** &amp;lt;code&amp;gt;lr / ll / ld&amp;lt;/code&amp;gt; - work like their local ram equivalents, but now the address range is the whole Z80 address space: 0000-ffff&lt;br /&gt;
** &amp;lt;code&amp;gt;j address&amp;lt;/code&amp;gt; - jump to code at ''address''&lt;br /&gt;
** &amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; - exit the monitor program and Return to normal TRS-80 functioning.&lt;br /&gt;
** &amp;lt;code&amp;gt;p&amp;lt;/code&amp;gt; - Pause the TRS-80 and run the monitor code.&lt;br /&gt;
** &amp;lt;code&amp;gt;rv file.bin hstartaddress&amp;lt;/code&amp;gt; - verify a file matches a section of RAM&lt;br /&gt;
* SD file commands, special for managing files on the SD card.&lt;br /&gt;
** &amp;lt;code&amp;gt;dir&amp;lt;/code&amp;gt; - shows list of files and their sizes&lt;br /&gt;
** &amp;lt;code&amp;gt;del file.bin&amp;lt;/code&amp;gt; - delete a file&lt;br /&gt;
* File send/receive. Using a special terminal program like teraterm, you can transfer files to and from the SD card without turning off the system and taking it out. There is no special protocol (like XMODEM); files are just sent as raw binary streams.&lt;br /&gt;
** &amp;lt;code&amp;gt;xs file.bin&amp;lt;/code&amp;gt; - send raw file over serial port. If you log these bytes, you can reconstruct the file. &lt;br /&gt;
** &amp;lt;code&amp;gt;xr file.bin&amp;lt;/code&amp;gt; - receive raw file over serial port. Using a terminal program with xon/xoff, send the binary file. &lt;br /&gt;
&lt;br /&gt;
This is all well and good, but SHRIP can only work when the Z80 cpu is running the code, stored inside the shared RAM. One way to get to it is to type SYSTEM ↵ / 12288 ↵ from BASIC to start executing code from address 3000H. But, it sure would be nice to be able to use the monitor program whenever I want, without typing in anything. If I am in DOS or inside a program, there's no way to make the Z80 jump to address 3000H without exiting it. To this end, I decided to modify the TRS-80 ROM. I had already placed an EPROM inside the TRS-80 case to update to the R/S ROM version. I replaced the routine at 002BH with a jump to 3000H. The replaced routine is the part that detects which key is pressed on the TRS-80 keyboard, meaning that it gets run all the time. We can place the rest of the 002BH code at the end of SHRIP, so it can get back to normal operation if we aren't using any SHRIP functions. &lt;br /&gt;
&lt;br /&gt;
Being in the middle of the keyboard routine, the monitor program also has the opportunity to return alternate data than what the keyboard is actually doing. The monitor program checks a byte of shared ram at address 3306H, and if it is nonzero, it will return that value as if the keyboard typed it. This means we can use the microcontroller to type things on the TRS-80 without ever touching the keyboard. I implemented two commands on the microcontroller command line interface to do this:&lt;br /&gt;
* &amp;lt;code&amp;gt;k sometext-to-type&amp;lt;/code&amp;gt; - everything after the &amp;quot;k &amp;quot; is typed on the TRS-80 keyboard.&lt;br /&gt;
**It also works with escape characters, like &amp;lt;code&amp;gt;\n&amp;lt;/code&amp;gt; for eNter and &amp;lt;code&amp;gt;\k&amp;lt;/code&amp;gt; for breaK, &amp;lt;code&amp;gt;\u \d \l \r&amp;lt;/code&amp;gt; for the direction keys, and &amp;lt;code&amp;gt;\c&amp;lt;/code&amp;gt; for the clear key.&lt;br /&gt;
* &amp;lt;code&amp;gt;ks file.txt&amp;lt;/code&amp;gt; - an entire file on the SD card is typed out over the keyboard. Escape characters aren't used here, it just sends the file as if it were a sequence of keyboard scan codes (most of the keyboard is ASCII).&lt;br /&gt;
&lt;br /&gt;
====Files====&lt;br /&gt;
Revision 6 - [[File:Trs80ei2-v6.zip]]&lt;br /&gt;
&lt;br /&gt;
Revision 7 - [[File:Trs80ei2-v7.zip]]&lt;br /&gt;
&lt;br /&gt;
==Hardware/Construction==&lt;br /&gt;
I constructed this on protoboard. Many of these types of projects use wire wrapping, but I don't have any wire wrapping sockets or any tools for doing this. Instead, I used the wire wrap-esque technique where wires jump to different points on the bottom of the board, and are soldered in place. This looks incredibly messy, and it is extremely easy to burn existing wires with the iron (if you look closely, you can see where I have fixed burnt wires with liquid electrical tape.) However, this technique does allow for a comparatively high chip density on the board, because none of the PCB area is used to carry signals. &lt;br /&gt;
&lt;br /&gt;
Most of my chips are socketed, as pretty much all of them are either very old and fragile, or new and very cheap and of questionable quality. The sockets make it very easy to fix bad chips, especially since the bottom of these sockets usually have a separate, free wire on most of the pins. &lt;br /&gt;
&lt;br /&gt;
On most of my chips, I have applied a label, making it easy to see the function on every pin. I make my labels using a thermal printer that was originally used to print price stickers in a store. The type of thermal labels used will turn black when they get hot. I have used this property more than once to detect a bad chip on my board (the chips often heat up when they go bad).&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
trs80ei2-base-bottom.jpg|base-bottom&lt;br /&gt;
Trs80ei2-base-top.jpg|base-top&lt;br /&gt;
Trs80ei2-doubler-bottom.jpg|doubler-bottom&lt;br /&gt;
Trs80ei2-doubler-top.jpg|doubler-top&lt;br /&gt;
Trs80ei2-floppy-bottom.jpg|floppy-bottom&lt;br /&gt;
Trs80ei2-floppy-top.jpg|floppy-top&lt;br /&gt;
Trs80ei2-lights-top.jpg|lights-top&lt;br /&gt;
Trs80ei2-lights-bottom.jpg|lights-bottom&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Log==&lt;br /&gt;
===Debugging a bad RAM chip - May 31, 2019===&lt;br /&gt;
While running the TRS-80 today, I noticed that I was not able to read files reliably under LDOS, but DoubleDos worked just as well as ever. I know that I had LDOS working when I first completed the expansion interface, so something was clearly wrong. Upon further investigation, I noticed that the errors only occurred when the double density driver (FDUBL) was loaded in LDOS. When the driver was loaded, I experienced complete instability when trying to read disks, and I could not format double density disks at all; the system would simply hang instead of stepping the disk drive. Initially, I thought there might be something wrong with my doubler board, after all, it does sit on top of the stack of boards and is slightly crooked. But, double density still worked fine under DoubleDos, so this was probably not the issue. &lt;br /&gt;
&lt;br /&gt;
My next thought was some kind of ram problem. I did notice that when loading data directly to ram with my Atmega monitor program, I noticed some verify errors at system address 0xb300. At first, I thought that this might be a stack pointer that was getting overwritten, but then I remembered that my monitor places it's stack within the shared 1k RAM. I tried directly writing to 0xb300 and could not get a consistent write, though the bytes before and after it worked fine. Could this verify error be the problem? This section of system memory would be within the 32K of SRAM provided by my expansion board. I am using a 128K chip here because that's what I had, but the chips I have are not credibly sourced, and I have had problems in this board with clone chips of 74 logic. &lt;br /&gt;
&lt;br /&gt;
I tried replacing the RAM and the problem did indeed go away, and I was finally able to fully load entire (48K) ram snapshots using my monitor program with no verify errors. At the same time, I found LDOS once again able to read and write disks reliably. So it seems that the ram was bad, although I find it strange that only a single byte was affected.&lt;br /&gt;
&lt;br /&gt;
===FreHD clone experiment - June 3, 2019===&lt;br /&gt;
Before I had completed the floppy controllers, I had began work on a frehd clone that would fit on my weird stacked EI, which I could never get to work properly. Developed by Frederic Vecoven, The [http://www.vecoven.com/trs80/trs80.html frehd project is an open source project] that emulates a hard drive in a manner consistent with the original mfm disk controllers you could get back in the day, though there is [http://members.iinet.net.au/~ianmav/trs80/emulator.htm now a closed source version assembled and sold] by Ian Mavric of Australia. After getting all my floppy drives working, having two floppy drives simulated with Goteks (more on this later), I really don't need a frehd too, but I had already made the board (following the schematic in the original frehd open source release), and so it was worth another try.&lt;br /&gt;
&lt;br /&gt;
My first experiment was to try a program called [http://48k.ca/trsvid.html TRSVID] (developed by George Phillips) which can display full motion video on the trs-80 using a frehd, although you need to change the firmware on the frehd PIC microcontroller first. Having constructed the entire board myself, I had a programmer too in order to program the PIC in the first place, and after loading the Phillips firmware, I too was able to watch grainy videos on my TRS-80, with equally crummy sound. Although the quality isn't great by modern standards, this is an extremely impressive demo considering the TRS-80's age. You only need to look at [[Wilson Tetris|my Tetris program]] to see the normal limitations of the TRS-80 graphics.&lt;br /&gt;
&lt;br /&gt;
However, I ran into trouble when trying to actually use my clone frehd for its intended purpose: as a hard drive. The freHD comes with some utilities to help mount and create hard disks, and &amp;lt;code&amp;gt;VHDUTL&amp;lt;/code&amp;gt; seems to work ok. However, trying to actually format the disks using &amp;lt;code&amp;gt;RSHARD1&amp;lt;/code&amp;gt; (using 840 tracks, 6 heads and 140 sectors), gave mixed results. Sometimes, the system would lock up after I finished filling out the prompts to mount the disk using &amp;lt;code&amp;gt;SYSTEM (DRIVE=4,DISABLE,DRIVER=&amp;quot;RSHARD1&amp;quot;)&amp;lt;/code&amp;gt; Other times, it would lock up after trying the &amp;lt;code&amp;gt;RSFORM1&amp;lt;/code&amp;gt; program which is supposed to format the disk. Most of the time though, these commands would work successfully, even verifying the format correctly. But, I found that afterwards, the system might lock up when trying to display the directory. &lt;br /&gt;
&lt;br /&gt;
In the event that all of the above setup works properly, I was still left with one final problem, which is that when I copy a file to the emulated hard drive, it doen't always seem to be a complete copy. I could copy my Tetris program and get it to load at least once or twice, but my other version (Metris, with more pieces) would not run no matter what I tried. Soon afterwards, my hard disk got corrupted and I would have to start all over.&lt;br /&gt;
&lt;br /&gt;
Incidentally, I also noticed that the &amp;lt;code&amp;gt;IMPORT2&amp;lt;/code&amp;gt; command which is used to transfer files from the freHD SD card to TRS-80 devices like floppy disks would also only make corrupt files. Perhaps if I examine the files, or make some test payloads, I may be able to determine what's going on, but that will need to wait for another day. I know that Ian made some changes the software, either to the frehd, trs-80 or both, to make the frehd work on a Model 1, but as of now I am not sure what those changes are. &lt;br /&gt;
&lt;br /&gt;
It may be better to continue my ATMEGA/dual port ram solution anyway, as this has the advantage of modifying any memory location.&lt;br /&gt;
&lt;br /&gt;
===Version 7 of software - June 19, 2019===&lt;br /&gt;
It seems like there is some kind of problem with the 32K ram on the board, most likely a bad connection or something, which is near impossible to find. I sometimes find that reseating the SRAM chip or swapping it for a diffrent one will usually work, even though all of my ram chips test good using my EPROM programmer as a tester. &lt;br /&gt;
&lt;br /&gt;
Anyways, I decided I needed to add a RAM test feature to the atmega operator interface. Before this, I created two 48k files of all 1s (0xFF) and all 0s (0x00), which I would load each with the '''rl''' command, which verifies each byte as it is written, allowing me to find RAM errors. Since this is a rather long command to type out, I decided to include a command &amp;lt;code&amp;gt;tstram&amp;lt;/code&amp;gt; that would write all 1s, then all 0s, for each ram address over the entire 48K ram. An optional number after the command will specify the number of loops to do, from once to a whole bunch.&lt;br /&gt;
&lt;br /&gt;
Additionally, I discovered a bug that would not allow the 3rd noun to be counted, making all commands think that there were only 2 nouns maximum (nouns=parameters after the command), making commands like &amp;lt;code&amp;gt;rd&amp;lt;/code&amp;gt; inoperable. As a workaround for v7, I increased the allowed number of nouns to 4, which means that we still have the same error, but now the parser thinks that we can't have more than 3 nouns, which is the most that any command has. I will fix this properly in the next releases.&lt;br /&gt;
&lt;br /&gt;
===v9 - Aug 2019===&lt;br /&gt;
Since the last log entry, I have indeed found the cause of the RAM issue - an address pin (one of the high address pins that isn't used) was left floating! This means that the ram would randomly switch to a different bank; this would be as if the ram randomly erases everything sometimes.&lt;br /&gt;
&lt;br /&gt;
The reason I discovered this was because I wired these extra address pins to some atmega pins, for an experiment. Now, through the Atmega, I can switch the 32k of system ram between 4 banks. &lt;br /&gt;
&lt;br /&gt;
I also am experimenting with an on-board terminal for the Atmega terminal control interface. From the little z80 program running in the dual port RAM, we already have access to the TRS-80 keyboard. To get access to the screen, we can call address 0x033a, which writes the character in A onto the screen, then advances the cursor. There are a few other routines &amp;lt;ref&amp;gt;http://www.trs-80.org/trs-80-rom-routines-documented/&amp;lt;/ref&amp;gt; that can help manage the screen, so that makes things like advancing the cursor and scrolling down something that I don't need to implement myself in my own program. I added calls to these routines in SHRIP, meaning that SHRIP-ACPT can now write stuff directly to the screen. I even added extra functions to the ACPT sketch, to make the Arduino stream functions like Serial.print also write to the TRS-80 screen.&lt;br /&gt;
&lt;br /&gt;
Ok, so we now have access to the TRS-80 screen and keyboard. The last step is to make ACPT available on the TRS-80 directly, without needing to use an extra serial terminal on the Atmega. I added a keyboard shortcut to SHRIP, which will enter into an on-screen ACP terminal. All I need to do is first copy the contents of the screen and save the cursor position so I can get back exactly where I left off. I store the image data in one of the extra banks of SRAM, since it is literally wasted space until now. When I'm done with the terminal, I issue the exit command, and the screen data is copied back onto the screen.&lt;br /&gt;
&lt;br /&gt;
This means it is now possible to load machine code directly from the Atmega SD card into trs-80 memory, and execute it, without ever hooking up the trsei2 board to another computer. You can't really load full ram images yet, because the onscreen terminal itself uses some of the system RAM, but overall, the system is much more accessible now.&lt;br /&gt;
&lt;br /&gt;
===v12 - December 2019===&lt;br /&gt;
With 128K of SRAM on the expansion board, it would really be nice if that extra ram could be accessed from the Z80 side of things, so that I don't have to go through the Atmega. This should also speed up assembly programs that use the extra RAM. &lt;br /&gt;
In the memory mapped IO section that breaks out address 37E0-37EC, I used 74LS138 octal decoders, so I also have lines for 37F0-37FC that are not used in a standard TRS-80. Therefore, I connected a 74LS175 latch to data pins D0-D1, and latch on writes to memory address 37FC. This makes the extra RAM (4 blocks of 32K, placed in the upper half of Z80 memory map) accessible from the Z80 directly.&lt;br /&gt;
&lt;br /&gt;
Hardware also has a parallel port added, which is nothing more than a couple buffers and latches. I needed this to make the winter camp latrine monitor program, since I discovered that it needs a printer to work properly.&lt;br /&gt;
&lt;br /&gt;
===RS-232 board - 3-24-2020===&lt;br /&gt;
The real expansion interfaces had an option for a RS-232 serial port, which enabled communication to the outside world; most modems required an RS232 port. Since I want to hook this up to a raspberry pi eventually, I'll need one.&lt;br /&gt;
&lt;br /&gt;
The original board used a tr1602 UART and BR1941 as the baud rate generator. I couldn't find either of these chips to, but I did find a TR1865 and COM8136, which are chips used in the Model 4, and are compatible. They also do not require a -5 or 12V supply, not that it's a big deal as I have an ATX supply as the power source. There's also a few supporting 74 series components, and some buffers to convert the 5V signals to the RS-232 voltages. In place of the buffers, I will try to use MAX232s instead, since I have a bunch of them for some reason. &lt;br /&gt;
&lt;br /&gt;
For address decoding, I am trying a GAL22v10. Normally I don't like using GAL programmable logic, because these chips are no longer made (there's still a lot though), and they sort of obscure how everything works. In this case, I found myself short of 74 logic chips in the middle of a global pandemic, so why not try something else and replace 4-5 74 chips instead.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=Coronameter&amp;diff=561</id>
		<title>Coronameter</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=Coronameter&amp;diff=561"/>
		<updated>2020-03-15T02:17:42Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;As Michigan gets it's first cases of COVID-19 and events, schools, and businesses close up, this global pandemic starts to feel a lot more real. To stay properly panicked, I decided to re-purpose my old temperature display from college as a dedicated screen showing what the score is.&lt;br /&gt;
&lt;br /&gt;
CDC and WHO update slowly, so for country and world stats I used a mirror of the John Hopkins interactive map you might have seen around, but in an easier to work with JSON format. For State data, I use the michigan.gov site, which I found to update more quickly. &lt;br /&gt;
I also decided to include the Dow and S&amp;amp;P indexes, as well as Clorox, just for the meme. That data comes directly from Yahoo Finance (not the discontinued API, but scraped off their website).&lt;br /&gt;
&lt;br /&gt;
==Screen==&lt;br /&gt;
===Hardware===&lt;br /&gt;
The hardware is nothing more than a ST7920 LCD wired to an ESP8266. The screen runs at 5V, so there is a logic level converter thrown in, too. This screen used to be a display for my indoor outdoor temperature thingy, is crudely soldered, and had no case. First order of business was to design and 3D print a small plastic enclosure for it. After wiring up a 5V wall adapter, the hardware is done.&lt;br /&gt;
===Software===&lt;br /&gt;
When it was a temperature display, all the fetching of data was done on the ESP8266 itself. For the new system, I decided to just make the display dump the contents of raw UDP packets. That way, I can do the hard work in an external program (like Python) and send it over to the screen when done. I did the same thing with the Winter Camp Mission timer setup with [[Nixie Clock 2|the Nixie Clocks]], and a character VFD hooked to an 8266, and it worked very well. &lt;br /&gt;
&lt;br /&gt;
The screen is driven using u8g2lib, and since the ST7920 doesn't have a built in text mode with this library, it was up to me to pick out a font and position the characters on the screen. U8g2lib will draw the characters graphically. I wound up with 21 columns and 7 rows, 147 characters total. On a screen this size, this makes for large and clearly readable text, even at a distance.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#include &amp;lt;Arduino.h&amp;gt;&lt;br /&gt;
#include &amp;lt;U8g2lib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;ESP8266WiFi.h&amp;gt;&lt;br /&gt;
#include &amp;lt;WiFiUdp.h&amp;gt;&lt;br /&gt;
#include &amp;lt;ESP8266mDNS.h&amp;gt;&lt;br /&gt;
#include &amp;lt;ArduinoOTA.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
const char* ssid = &amp;quot;ssidname&amp;quot;;    //  your network SSID (name) &lt;br /&gt;
const char* pass = &amp;quot;password&amp;quot;;   // your network password&lt;br /&gt;
&lt;br /&gt;
unsigned int localPort = 4212;&lt;br /&gt;
WiFiUDP Udp;&lt;br /&gt;
&lt;br /&gt;
U8G2_ST7920_128X64_F_SW_SPI u8g2(U8G2_R0, /* clock=*/ 12, /* data=*/ 13, /* CS=*/ 4, /* reset=*/ 14);&lt;br /&gt;
&lt;br /&gt;
char zbuffer[32];&lt;br /&gt;
&lt;br /&gt;
char sbuf[148];&lt;br /&gt;
&lt;br /&gt;
unsigned int millistimer=0;&lt;br /&gt;
&lt;br /&gt;
void setup(void) {&lt;br /&gt;
  u8g2.begin();&lt;br /&gt;
  Serial.begin(74880);&lt;br /&gt;
&lt;br /&gt;
  u8g2.setFont(u8g2_font_profont11_tr);&lt;br /&gt;
  u8g2.setFontRefHeightExtendedText();&lt;br /&gt;
  u8g2.setDrawColor(1);&lt;br /&gt;
  u8g2.setFontPosTop();&lt;br /&gt;
  u8g2.setFontDirection(0);&lt;br /&gt;
&lt;br /&gt;
  sprintf(zbuffer, &amp;quot;Starting&amp;quot;);&lt;br /&gt;
  u8g2.drawStr(0, 0, zbuffer);&lt;br /&gt;
  u8g2.sendBuffer();&lt;br /&gt;
&lt;br /&gt;
  WiFi.mode(WIFI_STA);&lt;br /&gt;
  WiFi.softAPdisconnect (true);&lt;br /&gt;
  WiFi.begin(ssid, pass);&lt;br /&gt;
  &lt;br /&gt;
  while(WiFi.status() != WL_CONNECTED){&lt;br /&gt;
    delay(1000);&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  Serial.println(WiFi.localIP());&lt;br /&gt;
  u8g2.clearBuffer();&lt;br /&gt;
  sprintf(zbuffer, &amp;quot;OK: %d.%d.%d.%d&amp;quot;,WiFi.localIP()[0],WiFi.localIP()[1],WiFi.localIP()[2],WiFi.localIP()[3]);&lt;br /&gt;
  u8g2.drawStr(0, 0, zbuffer);&lt;br /&gt;
  u8g2.sendBuffer();&lt;br /&gt;
&lt;br /&gt;
  Udp.begin(localPort);&lt;br /&gt;
  ArduinoOTA.setHostname(&amp;quot;graphomatic-udp&amp;quot;);&lt;br /&gt;
  ArduinoOTA.begin();&lt;br /&gt;
 &lt;br /&gt;
  millistimer=millis();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop(void) {&lt;br /&gt;
  ArduinoOTA.handle();&lt;br /&gt;
  int packetSize = Udp.parsePacket();&lt;br /&gt;
  if(packetSize){&lt;br /&gt;
    Udp.read(sbuf, sizeof(sbuf));&lt;br /&gt;
    u8g2.clearBuffer();&lt;br /&gt;
    u8g2.setFont(u8g2_font_profont11_tr);&lt;br /&gt;
    u8g2.setFontRefHeightExtendedText();&lt;br /&gt;
    u8g2.setDrawColor(1);&lt;br /&gt;
    u8g2.setFontPosTop();&lt;br /&gt;
    u8g2.setFontDirection(0);&lt;br /&gt;
    byte x=0;&lt;br /&gt;
    byte y=0;&lt;br /&gt;
    byte c=0;&lt;br /&gt;
    while(sbuf[c]!=0&amp;amp;&amp;amp;c!=255){&lt;br /&gt;
      if(sbuf[c]&amp;gt;31&amp;amp;&amp;amp;sbuf[c]&amp;lt;127){&lt;br /&gt;
        u8g2.drawGlyph(x*6+1,y*9-1,sbuf[c]);&lt;br /&gt;
      }&lt;br /&gt;
      if(sbuf[c]==10){y++; x=255;}&lt;br /&gt;
      x++;&lt;br /&gt;
      if(x==21){x=0; y++;}&lt;br /&gt;
      c++;&lt;br /&gt;
    }&lt;br /&gt;
    u8g2.sendBuffer();&lt;br /&gt;
  }&lt;br /&gt;
       &lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Getting the data==&lt;br /&gt;
Next, I need to get the stats, format it for the screen, and send it over. Python makes this easy to do, even though I hadn't done this before.&lt;br /&gt;
&lt;br /&gt;
Some of the numbers I get using a regex on the raw html I get when doing a request to a webpage. Another site I pick from that shows the COVID-19 world and country stats gives a JSON response, which Python also has good tools to work with.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;import requests&lt;br /&gt;
import json&lt;br /&gt;
import re&lt;br /&gt;
import socket&lt;br /&gt;
import datetime&lt;br /&gt;
import time&lt;br /&gt;
&lt;br /&gt;
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)&lt;br /&gt;
&lt;br /&gt;
while 1:&lt;br /&gt;
    try:&lt;br /&gt;
        rW = requests.get('https://covid19.mathdro.id/api')&lt;br /&gt;
        wc = rW.json().get('confirmed').get('value')&lt;br /&gt;
        wd = rW.json().get('deaths').get('value')&lt;br /&gt;
        wr = rW.json().get('recovered').get('value')&lt;br /&gt;
    except:&lt;br /&gt;
        wc=0&lt;br /&gt;
        wd=0&lt;br /&gt;
        wr=0&lt;br /&gt;
&lt;br /&gt;
    try:&lt;br /&gt;
        rA = requests.get('https://covid19.mathdro.id/api/countries/USA')&lt;br /&gt;
        ac = rA.json().get('confirmed').get('value')&lt;br /&gt;
        ad = rA.json().get('deaths').get('value')&lt;br /&gt;
        ar = rA.json().get('recovered').get('value')&lt;br /&gt;
    except:&lt;br /&gt;
        ac=0&lt;br /&gt;
        ad=0&lt;br /&gt;
        ar=0&lt;br /&gt;
        &lt;br /&gt;
    try:&lt;br /&gt;
        rM = requests.get('https://covid19.mathdro.id/api/countries/USA/confirmed')&lt;br /&gt;
        rMj = [x for x in rM.json() if x.get(&amp;quot;provinceState&amp;quot;) == &amp;quot;Michigan&amp;quot;][0]&lt;br /&gt;
        #mc = rMj.get('confirmed')&lt;br /&gt;
        md = rMj.get('deaths')&lt;br /&gt;
        mr = rMj.get('recovered')&lt;br /&gt;
    except:&lt;br /&gt;
        md=0&lt;br /&gt;
        mr=0&lt;br /&gt;
&lt;br /&gt;
    try:&lt;br /&gt;
        rD = requests.get('https://www.michigan.gov/Coronavirus')&lt;br /&gt;
        mc = int(re.search(&amp;quot;Positive for 2019-nCoV(.*?)&amp;amp;nbsp; (.*?)&amp;lt;&amp;quot;, rD.text, re.DOTALL).group(2))&lt;br /&gt;
    except:&lt;br /&gt;
        mc=0&lt;br /&gt;
&lt;br /&gt;
    try:&lt;br /&gt;
        rD = requests.get('https://finance.yahoo.com/quote/%5EDJI?p=%5EDJI')&lt;br /&gt;
        dow = float(re.search(&amp;quot;D\(b\)\&amp;quot; data-reactid=\&amp;quot;14\&amp;quot;&amp;gt;(.*?)&amp;lt;&amp;quot;, rD.text).group(1).replace(&amp;quot;,&amp;quot;,&amp;quot;&amp;quot;))&lt;br /&gt;
    except:&lt;br /&gt;
        dow=0&lt;br /&gt;
&lt;br /&gt;
    try:&lt;br /&gt;
        rD = requests.get('https://finance.yahoo.com/quote/%5EGSPC?p=%5EGSPC')&lt;br /&gt;
        sp = float(re.search(&amp;quot;D\(b\)\&amp;quot; data-reactid=\&amp;quot;14\&amp;quot;&amp;gt;(.*?)&amp;lt;&amp;quot;, rD.text).group(1).replace(&amp;quot;,&amp;quot;,&amp;quot;&amp;quot;))&lt;br /&gt;
    except:&lt;br /&gt;
        sp = 0&lt;br /&gt;
&lt;br /&gt;
    try:&lt;br /&gt;
        rD = requests.get('https://finance.yahoo.com/quote/CLX?p=CLX')&lt;br /&gt;
        clx = float(re.search(&amp;quot;D\(b\)\&amp;quot; data-reactid=\&amp;quot;14\&amp;quot;&amp;gt;(.*?)&amp;lt;&amp;quot;, rD.text).group(1).replace(&amp;quot;,&amp;quot;,&amp;quot;&amp;quot;))&lt;br /&gt;
    except:&lt;br /&gt;
        clx=0&lt;br /&gt;
    &lt;br /&gt;
    buf = &amp;quot;Wilson's Coronameter &amp;quot;&lt;br /&gt;
    buf += (&amp;quot;W %d|%d|%d&amp;quot; % (wc, wd, wr)).ljust(21)&lt;br /&gt;
    buf += (&amp;quot;USA %d|%d|%d&amp;quot; % (ac, ad, ar)).ljust(21)&lt;br /&gt;
    buf += (&amp;quot;MI %d|%d|%d&amp;quot; % (mc, md, mr)).ljust(21)&lt;br /&gt;
    buf += (&amp;quot;DJ %.1f S&amp;amp;P %.1f&amp;quot; % (dow, sp)).ljust(21)&lt;br /&gt;
    buf += (&amp;quot;CLX %.1f&amp;quot; % (clx)).ljust(21)&lt;br /&gt;
    buf += (datetime.datetime.now().strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;)).ljust(21)&lt;br /&gt;
&lt;br /&gt;
    sock.sendto(buf.encode(), ('192.168.1.44',4212))&lt;br /&gt;
    print(buf)&lt;br /&gt;
&lt;br /&gt;
    j = datetime.datetime.now().strftime(&amp;quot;%M&amp;quot;)&lt;br /&gt;
    while j == datetime.datetime.now().strftime(&amp;quot;%M&amp;quot;):&lt;br /&gt;
        time.sleep(1)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
	<entry>
		<id>https://alnwlsn.com/projectrepository/index.php?title=Coronameter&amp;diff=560</id>
		<title>Coronameter</title>
		<link rel="alternate" type="text/html" href="https://alnwlsn.com/projectrepository/index.php?title=Coronameter&amp;diff=560"/>
		<updated>2020-03-15T02:05:27Z</updated>

		<summary type="html">&lt;p&gt;Alnwlsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;As Michigan gets it's first cases of COVID-19 and events, schools, and businesses close up, this global pandemic starts to feel a lot more real. To stay properly panicked, I decided to re-purpose my old temperature display from college as a dedicated screen showing what the score is.&lt;br /&gt;
&lt;br /&gt;
==Screen==&lt;br /&gt;
===Hardware===&lt;br /&gt;
The hardware is nothing more than a ST7920 LCD wired to an ESP8266. The screen runs at 5V, so there is a logic level converter thrown in, too. This screen used to be a display for my indoor outdoor temperature thingy, is crudely soldered, and had no case. First order of business was to design and 3D print a small plastic enclosure for it. After wiring up a 5V wall adapter, the hardware is done.&lt;br /&gt;
===Software===&lt;br /&gt;
When it was a temperature display, all the fetching of data was done on the ESP8266 itself. For the new system, I decided to just make the display dump the contents of raw UDP packets. That way, I can do the hard work in an external program (like Python) and send it over to the screen when done. I did the same thing with the Winter Camp Mission timer setup with [[Nixie Clock 2|the Nixie Clocks]], and a character VFD hooked to an 8266, and it worked very well. &lt;br /&gt;
&lt;br /&gt;
The screen is driven using u8g2lib, and since the ST7920 doesn't have a built in text mode with this library, it was up to me to pick out a font and position the characters on the screen. U8g2lib will draw the characters graphically. I wound up with 21 columns and 7 rows, 147 characters total. On a screen this size, this makes for large and clearly readable text, even at a distance.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#include &amp;lt;Arduino.h&amp;gt;&lt;br /&gt;
#include &amp;lt;U8g2lib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;ESP8266WiFi.h&amp;gt;&lt;br /&gt;
#include &amp;lt;WiFiUdp.h&amp;gt;&lt;br /&gt;
#include &amp;lt;ESP8266mDNS.h&amp;gt;&lt;br /&gt;
#include &amp;lt;ArduinoOTA.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
const char* ssid = &amp;quot;ssidname&amp;quot;;    //  your network SSID (name) &lt;br /&gt;
const char* pass = &amp;quot;password&amp;quot;;   // your network password&lt;br /&gt;
&lt;br /&gt;
unsigned int localPort = 4212;&lt;br /&gt;
WiFiUDP Udp;&lt;br /&gt;
&lt;br /&gt;
U8G2_ST7920_128X64_F_SW_SPI u8g2(U8G2_R0, /* clock=*/ 12, /* data=*/ 13, /* CS=*/ 4, /* reset=*/ 14);&lt;br /&gt;
&lt;br /&gt;
char zbuffer[32];&lt;br /&gt;
&lt;br /&gt;
char sbuf[148];&lt;br /&gt;
&lt;br /&gt;
unsigned int millistimer=0;&lt;br /&gt;
&lt;br /&gt;
void setup(void) {&lt;br /&gt;
  u8g2.begin();&lt;br /&gt;
  Serial.begin(74880);&lt;br /&gt;
&lt;br /&gt;
  u8g2.setFont(u8g2_font_profont11_tr);&lt;br /&gt;
  u8g2.setFontRefHeightExtendedText();&lt;br /&gt;
  u8g2.setDrawColor(1);&lt;br /&gt;
  u8g2.setFontPosTop();&lt;br /&gt;
  u8g2.setFontDirection(0);&lt;br /&gt;
&lt;br /&gt;
  sprintf(zbuffer, &amp;quot;Starting&amp;quot;);&lt;br /&gt;
  u8g2.drawStr(0, 0, zbuffer);&lt;br /&gt;
  u8g2.sendBuffer();&lt;br /&gt;
&lt;br /&gt;
  WiFi.mode(WIFI_STA);&lt;br /&gt;
  WiFi.softAPdisconnect (true);&lt;br /&gt;
  WiFi.begin(ssid, pass);&lt;br /&gt;
  &lt;br /&gt;
  while(WiFi.status() != WL_CONNECTED){&lt;br /&gt;
    delay(1000);&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  Serial.println(WiFi.localIP());&lt;br /&gt;
  u8g2.clearBuffer();&lt;br /&gt;
  sprintf(zbuffer, &amp;quot;OK: %d.%d.%d.%d&amp;quot;,WiFi.localIP()[0],WiFi.localIP()[1],WiFi.localIP()[2],WiFi.localIP()[3]);&lt;br /&gt;
  u8g2.drawStr(0, 0, zbuffer);&lt;br /&gt;
  u8g2.sendBuffer();&lt;br /&gt;
&lt;br /&gt;
  Udp.begin(localPort);&lt;br /&gt;
  ArduinoOTA.setHostname(&amp;quot;graphomatic-udp&amp;quot;);&lt;br /&gt;
  ArduinoOTA.begin();&lt;br /&gt;
 &lt;br /&gt;
  millistimer=millis();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop(void) {&lt;br /&gt;
  ArduinoOTA.handle();&lt;br /&gt;
  int packetSize = Udp.parsePacket();&lt;br /&gt;
  if(packetSize){&lt;br /&gt;
    Udp.read(sbuf, sizeof(sbuf));&lt;br /&gt;
    u8g2.clearBuffer();&lt;br /&gt;
    u8g2.setFont(u8g2_font_profont11_tr);&lt;br /&gt;
    u8g2.setFontRefHeightExtendedText();&lt;br /&gt;
    u8g2.setDrawColor(1);&lt;br /&gt;
    u8g2.setFontPosTop();&lt;br /&gt;
    u8g2.setFontDirection(0);&lt;br /&gt;
    byte x=0;&lt;br /&gt;
    byte y=0;&lt;br /&gt;
    byte c=0;&lt;br /&gt;
    while(sbuf[c]!=0&amp;amp;&amp;amp;c!=255){&lt;br /&gt;
      if(sbuf[c]&amp;gt;31&amp;amp;&amp;amp;sbuf[c]&amp;lt;127){&lt;br /&gt;
        u8g2.drawGlyph(x*6+1,y*9-1,sbuf[c]);&lt;br /&gt;
      }&lt;br /&gt;
      if(sbuf[c]==10){y++; x=255;}&lt;br /&gt;
      x++;&lt;br /&gt;
      if(x==21){x=0; y++;}&lt;br /&gt;
      c++;&lt;br /&gt;
    }&lt;br /&gt;
    u8g2.sendBuffer();&lt;br /&gt;
  }&lt;br /&gt;
       &lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Getting the data==&lt;br /&gt;
Next, I need to get the stats, format it for the screen, and send it over. Python makes this easy to do, even though I hadn't done this before.&lt;br /&gt;
&lt;br /&gt;
Some of the numbers I get using a regex on the raw html I get when doing a request to a webpage. Another site I pick from that shows the COVID-19 world and country stats gives a JSON response, which Python also has good tools to work with.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;import requests&lt;br /&gt;
import json&lt;br /&gt;
import re&lt;br /&gt;
import socket&lt;br /&gt;
import datetime&lt;br /&gt;
import time&lt;br /&gt;
&lt;br /&gt;
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)&lt;br /&gt;
&lt;br /&gt;
while 1:&lt;br /&gt;
    try:&lt;br /&gt;
        rW = requests.get('https://covid19.mathdro.id/api')&lt;br /&gt;
        wc = rW.json().get('confirmed').get('value')&lt;br /&gt;
        wd = rW.json().get('deaths').get('value')&lt;br /&gt;
        wr = rW.json().get('recovered').get('value')&lt;br /&gt;
    except:&lt;br /&gt;
        wc=0&lt;br /&gt;
        wd=0&lt;br /&gt;
        wr=0&lt;br /&gt;
&lt;br /&gt;
    try:&lt;br /&gt;
        rA = requests.get('https://covid19.mathdro.id/api/countries/USA')&lt;br /&gt;
        ac = rA.json().get('confirmed').get('value')&lt;br /&gt;
        ad = rA.json().get('deaths').get('value')&lt;br /&gt;
        ar = rA.json().get('recovered').get('value')&lt;br /&gt;
    except:&lt;br /&gt;
        ac=0&lt;br /&gt;
        ad=0&lt;br /&gt;
        ar=0&lt;br /&gt;
        &lt;br /&gt;
    try:&lt;br /&gt;
        rM = requests.get('https://covid19.mathdro.id/api/countries/USA/confirmed')&lt;br /&gt;
        rMj = [x for x in rM.json() if x.get(&amp;quot;provinceState&amp;quot;) == &amp;quot;Michigan&amp;quot;][0]&lt;br /&gt;
        #mc = rMj.get('confirmed')&lt;br /&gt;
        md = rMj.get('deaths')&lt;br /&gt;
        mr = rMj.get('recovered')&lt;br /&gt;
    except:&lt;br /&gt;
        md=0&lt;br /&gt;
        mr=0&lt;br /&gt;
&lt;br /&gt;
    try:&lt;br /&gt;
        rD = requests.get('https://www.michigan.gov/Coronavirus')&lt;br /&gt;
        mc = int(re.search(&amp;quot;Positive for 2019-nCoV(.*?)&amp;amp;nbsp; (.*?)&amp;lt;&amp;quot;, rD.text, re.DOTALL).group(2))&lt;br /&gt;
    except:&lt;br /&gt;
        mc=0&lt;br /&gt;
&lt;br /&gt;
    try:&lt;br /&gt;
        rD = requests.get('https://finance.yahoo.com/quote/%5EDJI?p=%5EDJI')&lt;br /&gt;
        dow = float(re.search(&amp;quot;D\(b\)\&amp;quot; data-reactid=\&amp;quot;14\&amp;quot;&amp;gt;(.*?)&amp;lt;&amp;quot;, rD.text).group(1).replace(&amp;quot;,&amp;quot;,&amp;quot;&amp;quot;))&lt;br /&gt;
    except:&lt;br /&gt;
        dow=0&lt;br /&gt;
&lt;br /&gt;
    try:&lt;br /&gt;
        rD = requests.get('https://finance.yahoo.com/quote/%5EGSPC?p=%5EGSPC')&lt;br /&gt;
        sp = float(re.search(&amp;quot;D\(b\)\&amp;quot; data-reactid=\&amp;quot;14\&amp;quot;&amp;gt;(.*?)&amp;lt;&amp;quot;, rD.text).group(1).replace(&amp;quot;,&amp;quot;,&amp;quot;&amp;quot;))&lt;br /&gt;
    except:&lt;br /&gt;
        sp = 0&lt;br /&gt;
&lt;br /&gt;
    try:&lt;br /&gt;
        rD = requests.get('https://finance.yahoo.com/quote/CLX?p=CLX')&lt;br /&gt;
        clx = float(re.search(&amp;quot;D\(b\)\&amp;quot; data-reactid=\&amp;quot;14\&amp;quot;&amp;gt;(.*?)&amp;lt;&amp;quot;, rD.text).group(1).replace(&amp;quot;,&amp;quot;,&amp;quot;&amp;quot;))&lt;br /&gt;
    except:&lt;br /&gt;
        clx=0&lt;br /&gt;
    &lt;br /&gt;
    buf = &amp;quot;Wilson's Coronameter &amp;quot;&lt;br /&gt;
    buf += (&amp;quot;W %d|%d|%d&amp;quot; % (wc, wd, wr)).ljust(21)&lt;br /&gt;
    buf += (&amp;quot;USA %d|%d|%d&amp;quot; % (ac, ad, ar)).ljust(21)&lt;br /&gt;
    buf += (&amp;quot;MI %d|%d|%d&amp;quot; % (mc, md, mr)).ljust(21)&lt;br /&gt;
    buf += (&amp;quot;DJ %.1f S&amp;amp;P %.1f&amp;quot; % (dow, sp)).ljust(21)&lt;br /&gt;
    buf += (&amp;quot;CLX %.1f&amp;quot; % (clx)).ljust(21)&lt;br /&gt;
    buf += (datetime.datetime.now().strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;)).ljust(21)&lt;br /&gt;
&lt;br /&gt;
    sock.sendto(buf.encode(), ('192.168.1.44',4212))&lt;br /&gt;
    print(buf)&lt;br /&gt;
&lt;br /&gt;
    j = datetime.datetime.now().strftime(&amp;quot;%M&amp;quot;)&lt;br /&gt;
    while j == datetime.datetime.now().strftime(&amp;quot;%M&amp;quot;):&lt;br /&gt;
        time.sleep(1)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Alnwlsn</name></author>
	</entry>
</feed>