Engine Designs LLC, Peter Tihanyi

As it happens, while I was working on an assembly project, I ran into some shortcomings that I tried to fix in this new release.

There was no way to just allocate bytes in memory, without actually writing data into the virtual memory. This normally isn't a problem in Commodore 64 projects, but in Gameboy projects the code is in ROM and the RAM is separated from it. Even though the ROM builder would ignore those bytes, words and arrays that you'd put between $c000-$dfff, it's not too elegant to write the code that way. Now it's possible to just allocate bytes these ways:

The directives .align and .storage now only allocate bytes in memory, if the Filler byte parameter is not set.

The directives .byte, .word and .dword can allocate bytes in memory, when the value is set to ?

//Example
RAM .org $c000
ButtonState .byte ?
Buffer .storage $20

Other changes, some were made in version 2.2.1

New directive .end added. It terminates the source code loading at that line, the rest of the lines are ignored.

Boolean values true and false now work everywhere where you expect them to work, such as in directives like .equ, .var, .if, .while, and also in .macro arguments and their default values.

Using of single character Labels is allowed (used to be forbidden), as long as it's not a match to a Register name in the selected CPU type. For example you can't use a / x / y as label names in 6502 code, but b / c / d are allowed.

Functions made by the .function directive no longer require brackets for the function definition, but they are still needed for the function call. It's just for alternative formatting.

The new version is out with a quite a lot of goodies. It took a while to implement all this, but it was worth it.

The Zilog Z80 CPU has been added to the list of supported CPUs, so now the assembler covers most of the classic home computers.

File formats for Sega Master System and Sega Game Gear ROMs have been added, along with TAP file support for ZX Spectrum 48K projects.

The expression evaluator now can handle complex expressions in Instructions and Macro function calls too. It used to work that way only for Variables, but now it's up to you to manage values using parentheses everywhere. It doesn't interfere with the parentheses used in the instruction addressing mode, if you do it right. When in doubt, just keep using a Variable to build your complex expressions.

Bitwise-NOT number manipulation has been added.

Light scripting is possible using the BeforeBuild and AfterBuild setting values that may appear multiple times in the source code.

The Visual Studio Code Extension has been updated, along with the Notepad++ support files to show the Register names with a more distinct color. The need for this became quite pressing in Z80 source code files.

If everything goes right, this will be the first time current users will be notified about a version update by the assembler itself. Exciting times!

I didn't plan to put out an update so quick, but a friend suggested some changes and I went for it, plus added some other useful things. I adore this project!

The assembler can now auto-start a successfully compiled code file by using the settings LaunchCommand and LaunchEnabled. The assembler will start the command as a new process, if launching is enabled, and all this can be managed in each source code file.

If launching is disabled but there is a launch command set up, VS Code's Build & Start command is still going to auto-start the code file, so this way you can keep Building without starting anything, and only start your code in an emulator when you use the keyboard shortcut you assigned to the command Build & Start

You can create a retroassembler-usersettings.xml file based on the default settings file, where you can keep your customized settings without the fear of overwriting it by an updated assembler package.

The assembler now periodically checks for updates and will notify you when there's something new to download.

Support for the 65816 CPU is here, along with the SNES ROM output format. Adding this CPU was not possible in the 1.x days, but with the new foundation it wasn't too hard. Since this CPU is a special one with 8 bit and 16 bit modes and 24 bit address size, I had to add some special things.

By prefixing a number with 0's, you can enforce a different number size. For example lda #$0012 will be compiled as a 16 bit number even in 8 bit mode.

The RegA16 and RegXY16 setting flags can enforce 8 or 16 bit value usage, so an lda #$12 can be compiled as lda #$0012 if needed.

The disassembler follows the Accumulator and Index register size selector CPU flag states to show properly decoded source code for sequentially placed instructions.

Other changes:

Alternative binary number format added: 0b11110011

Octal number format support added: 0o17

Hexadecimal and binary numbers can use the '_' (underscore) character as visual value separator: %1111_0011, $12_34

The disassembler can limit the number of disassembled bytes in a file.

A lot has changed since the last version, both in the assembler and in the world of computing alike. It's clear now that .Net Core is a thing, it's available on numerous operating systems which makes it possible to do true portable app development in C#. And also Visual Studio Code is becoming a mature product, available for most people, no matter what their favorite OS is.

I'm happy to announce that Retro Assembler has reached version 2.0 and after serious refactoring and improvements is now available as both Windows (.Net Framework 4.7, runs on Mono too) and Portable (.Net Core 2.1) builds. Not only that, it's even integrated with Visual Studio Code via a custom Extension, making it a powerful development tool for all the popular operating systems.

Retro Assembler in Visual Studio Code

There's more! Now it's a full-fledged development environment for Nintendo Entertainment System and Nintendo Gameboy with the recently added Gameboy CPU and ROM support.

The target memory management got completely overhauled, now there is no practical memory limit for the projects, as long as they fit into the 32 bit address space. Labels and values can be up to 32 bit numbers. The Segment support has been changed, now combined with Banks it can do a lot more than before. If you used .target and .segment directives, update those in your source code files to the latest specification. The command line options also changed, be aware of that.

See the documentation for details, there are a lot of additions and changes to read up on.

You may remember Epic Comeback, the small but punchy Commodore 64 demo I made last summer. There is a utility called C64 Debugger which, among other things, can visualize what's happening in the C64's memory while a program is running. This can reveal some interesting stuff, even if you are not that much into programming in assembly. I made a video of it, check it out in full screen!

Some of the observations that you can make...

‐ Upon a hard reset the Commodore 64's memory is initialized with a 00 00 00 00... FF FF FF FF... byte pattern, so you can see stripes in the visualized memory.

‐ When the demo is first loaded from disk, it takes up about 1/3rd of the 64K RAM. It's compressed though. First the decompressor copies the whole compressed data to the top of the memory, then starts working on it and fills up the memory with decompressed data from the bottom up. At one point the decompressed data starts overwriting the original compressed data (red line) while the rest of the data is still being decompressed (blue line). Red catches up at the end, just as the decompression ends, filling up nearly the whole 64K RAM of the Commodore 64. This is so much data that the C64 kernel couldn't load it from disk in a decompressed state by default.

‐ The chessboard animation is not too interesting in memory activity, it's mostly a character set based animation with a bunch of video matrices and video bank switching. On the bottom of the screen you can see the constant update of those zero page variables that the animation is using.

‐ When the animation goes away, the demo generates a bunch of wave tables for the plotter, this takes up about 1/4th of the RAM in the middle of the visualizer. The rest of the memory is filled up with two generated executable code blocks, one for each video buffer. The effect is redrawn on each screen refresh, but uses so much CPU time (well, all of it) that double buffering is necessary to avoid screen tearing.

‐ In the middle you can see the X and Y double-sine wave of each of the 128 plots (this looks awesome in the memory visualizer) applied to the lookup wave tables, to figure out where to draw the plots in the video memory. This is all reading, so it's shown with Blue color. The plotter drawing is pure writing, so it's shown with Red color above the waves.

‐ The generated plotter drawing code gets executed and self-modified all the time, as you can see it from the activity on the bottom part of the memory visualizer. As the plotter slides out of the visible screen area, less and less plots need to be drawn, so the memory darkens a bit until the next plotter configuration slides up into view.

‐ I did a hard reset at the end, so you can see how the memory is cleared up before the kernel initializes the BASIC interpreter, which is the default user interface of the Commodore 64.

After the last release of V1.1 I received a lot of support and encouragement from Commodore 64 and 6502 enthusiasts on Facebook, it was nice. As a result of it, the assembler has been fixed to work on Linux using Mono, I even set up my dusty Raspberry Pi 3 with an environment that can run it.

I began researching other CPU types and ended up adding support for the 65C02 and 65SC02 CPUs. They looked like small updates at first with a few new instructions, but the 65C02 has these weird bit instructions that combine a zero page address for bit testing and a relative address for branching. That was a bit of a trouble for the assembler, as it couldn't deal with instructions that use two parameters. However, it was added and it works nicely.

I also realized that I didn't address the D64 maker option for Linux, which was clearly made for Windows, using "c1541.exe" under WinVICE. I can't really test that one (yet), but I changed the call to use "c1541" and we will see if it works. If you can test it, let me know, I'd appreciate it. Alternatively I put the command line call into the ReadMe.txt file that you can use on Linux, to get a D64 file out of a T64 file.

As I mentioned before, when I started out developing Retro Assembler, I had a demo in mind that I wanted to make with it as a starter project. I've done it, took some time, I worked on it between July 25 and August 10 – finished it just in time for the 19th Árok Party, a cozy 8-bit gathering held in Ajka, Hungary. I couldn't be there but some of my friends were, the demo was sent in for the Commodore 64 demo competition and to my surprise it won 1st place. I couldn't be happier with how things turned out with this little passion project, it was quite the confirmation that it was worth the time I spent on it.

I uploaded a video capture to Youtube (and this way I have my own Youtube channel, yay!), check it out! The imperfections are due to the 50Hz system vs 60Hz video capture differences.

Now if you're interested, I can tell you about some technical details. As you may know, the Commodore 64 has 64 kilobytes of RAM, that's about the size of a blank Microsoft Word document before compression. That's a challenge on its own. The demo, without loading from disk, uses way more RAM than that, about 90K to be exact, by generating most code and data for the plotter effect.

The demo runs on both PAL and NTSC systems alike, by detecting the system type and making the music play on its intended 50Hz speed on NTSC systems that use 60Hz screen refresh. This can only be achieved by skipping the music player call in every 6th frame. That can cause some audio artifacts, but this is all we can do to make it sound right. The home of Commodore 64 demos (and better games) is mainly Europe, despite the computer's American roots.

The main block of the demo takes up the first 14K of the RAM, it contains the framework that runs the demo itself, the scroller and the chessboard zoomer's code, music, logo, scroller font and text. Then from $3800 the effects use basically the rest of the available memory. I didn't bother using the register area between $d000-$e000, that would come with too many complications that I tried to avoid.

Chessboard Zoomer

It's an animation, but that doesn't make it less cool. It would be hard to make this run in real time on this hardware, in 320*128 pixels. The raw data for it is 36K, with 22 animation phases (with 640 bytes of character matrix for each, taking up 14K in total), squeezed into 11 character sets (2K each, 22K in total).

22 phases of this animation would give us only 180 degrees of rotation. Due to the properties of the chessboard and the loop I went for, I can fake the other 22 animation phases by swapping the foreground and background colors halfway. Originally I wanted to go for a smoother animation with twice the phases, but that turned out to be impossible to hold in the RAM. Since the animation would run way too fast if it got refreshed in every frame, I only step to the next phase in every 2nd screen refresh. That still makes it smooth enough to be enjoyable. Every 4th loop of the animation the color palette changes to make the effect more interesting, while I'm babbling about in the scroll text.

The animation was made by using this data generator I wrote back in 2010 (see, I did plan to make this demo for a while). I can step the animation manually, and it calculates how many unique characters (8*8 pixel blocks) the phase contains, and how many unique characters we need for the entire animation so far. This way I could experiment with the optimal size, rotation speed and zoom properties without wasting time on implementing the entire effect on the Commodore 64 itself, only to realize halfway that something doesn't add up.

Plotter

I love this effect, it's 128 plots forming cool blobs. I wrote something similar in a demo in 1997, only that couldn't refresh in each frame, unlike this one. Remember how the demo's framework and the chessboard animation took up 50K of the 64K RAM? When the chessboard disappears, I start the execution of a small program (2K) that generates 14K sine wave data and 13K unrolled code that can draw the plotter on the screen. With the effect's execution code, video memory buffers and all this generated stuff, it takes up about 40K RAM above the 14K for the framework.

The plotter refreshes during each screen refresh and takes up so much CPU time that it barely fits into the "raster time" as we call it. Due to this, to avoid screen tearing, it has to use double buffering. That means, we see the previously drawn animation phase, while we draw the next animation phase in the other buffer. Then switch. The code for it is highly optimized, with this flow:

Cleaner code, that deletes the previously drawn screen.

Plotter code modifier, that sets up the X and Y coordinates for each of the 128 plots in the plot drawing code itself.

Plotter code that draws the screen and modifies the cleaner code.

This way it's self-sustained, the plotter cleans up after itself in the video memory after each frame. Each buffer requires its own code because they draw to different memory locations, each requires 6.5K of code.

For the curious, here is a sample of the code that draws one plot out of 128. It would be a bit too complicated to explain what exactly it does, but in short, it draws a plot with a dynamic vertical shift applied to it (the blobs swing up and down, then leave the entire screen to switch animation style) after figuring out where to draw in the video memory, and updates the cleaner code for this buffer to clean up after itself the next time the code is called.

The plotter's execution takes up so much CPU time that I had to unroll even the scroll routine to make it all fit into a single frame. But as you may know, the NTSC systems work with 60Hz refresh and basically a smaller screen border, which results in smoother animation with less CPU time to use. The plotter uses two kinds of blob shapes, simpler ones and more complex ones with additional position calculations. The shapes are controlled by a playlist that modify the values of the plot position calculator and on NTSC systems I was forced to skip the complex ones. This way the demo runs smoothly on both video types, but the PAL version is more colorful, so to speak.

The demo takes up so much initial memory space, that the Commodore 64 kernel can't even load it into the memory without serious compression, for which I used Pucrunch. But this isn't a surprise, most C64 executables are compressed some way. Only in this case I couldn't even test the demo without compressing it first, once I put it all together into one file.

In the past year most of my web work has been reduced to the development of Web APIs with clear JSON request and response. The response sent by the web server can be quite big, so it's a good idea to enable dynamic compression on it. However, even when you enable compression in IIS, it won't compress files sent with content type application/json. Not by default. Luckily the compression settings can be configured. To do it for the whole web server, open the settings panel for the web server root (these settings are applied to all websites) and open the Configuration Editor in the Management section.

In the Configuration Editor there is a Section selector on the top. Open the section system.webServer and choose httpCompression to access the dynamic and static file compression settings. As you can see, there are plenty more you can adjust in IIS, but I'd say it's best to leave most settings as vanilla as possible and apply adjustments in individual web.config files. But in this case it won't hurt to enable JSON compression for the entire web server.

Here you can set up the content types handled by the file compressor. Obviously it's not worth to GZip a Jpeg file, but text based files are good candidates. The types can be set for Dynamic and Static file compression individually.

In Dynamic Types add a new MIME type to the list: application/json and mark it as Enabled (True). You may also add application/json; charset=utf-8 just to be on the safe side.

Then do the same additions for Static Types, you may need it in certain situations. Don't forget to actually enable compression in the server root's IIS section, by going into the Compression editor.

Meet Nyunyu, my blue Ford Escape. It's more than just a car. It's my dream car. I've always been a big fan of the Ford Focus, we even got one for Leslie in 2012. I liked the boxy Escape from that era, then the revised version came out in 2013 and it was love at first sight. The same tech as in the Focus with the same interior, but in a slightly bigger package. I really wanted one, but I didn't even have a driver's license at that point, and Leslie definitely didn't want us to deal with two car payments anyway.

So I was just dreaming about it for quite some time, saving up for it, and in the meantime I bugged the heck out of my wife about what color I'd pick, what kind of tools and gadgets I'd carry in its obviously infinite trunk space and stuff like that. I always played with one at the Ford dealership, I even test-drove one later on. Mike the dealer kept nudging me to just sign a contract and get one already. No Mike, I have to save up for it... I had to be really annoying at times, but my wife is clearly an angel because she coped with it, and by 2015 she said alright, once we paid off the Focus, we can buy me the Blue Escape I want so much. Our adventure vehicle. Okay, say no more!

In 2016 January the news broke about the 2017 Escape face lift, and I didn't like it too much. It's surely a fine car, but looks too much like the Edge and the Deep Impact Blue color isn't even an option anymore. Ford already stopped manufacturing the now old car, and by talking to Mike it became obvious that it's gonna be hard to find a blue Escape with the packages I wanted. Found one in Ford's dealer system that was close enough, Mike had to do a swap with another dealership from another state to get it, but he's a nice guy. And I bet he wanted this constant annoyance to be over anyway. On January 30th of 2016, Nyunyu was born. So to speak.

Maybe I went a bit overboard with the specs, but I wanted a car that I can keep forever. So it's Titanium trim level with the beefiest engine available, a turbo charged 2.0L EcoBoost with 240 hp and 270 lb/ft torque. Coupled with the Intelligent 4WD system that we need here with all the hills and cold winters, it's a beast. There is no legal speed in the USA at which I couldn't just step on the gas without being kicked in the back by nearly instantaneous acceleration, sometimes with wheel spin. It's ridiculously stupid fast, almost at the point of questioning, why is there such an engine in a soccer mom car? It's a real sleeper, but it's just my opinion, your mileage may vary. With its keyless entry and push button start, it makes older solutions feel archaic. It's easy to get used to good things.

It came with Ford SYNC 3, which is sadly not developed by Microsoft anymore, but it's a really decent QNX based entertainment and navigation system with a proper touch screen. I love the navigation, it was a must have and we already had some epic trips with it, including driving to Wisconsin. The media player is fine, nothing fancy but it works and that's what matters. Two USB ports in the center console, either can take a flash drive with MP3 and AAC files and the bluetooth connectivity works like a charm for podcasts. I have no complaints apart from the song shuffle algorithm which has some issues and heavily favors Marvin Gaye. You'd think I have a hundred songs from him by the rate he keeps popping up on the Sony audio system, which sounds awesome by the way.

It's really pleasant inside, like being in a leather cocoon. The dashboard is modern enough to look good, but not too modern to age badly as time goes by. There is a small screen between the analog gauges that can show some essential information, including the current speed that I keep on all the time; I don't look at the analog speedometer at all. It even has a small area for navigation integration to show the upcoming turn which is really handy. It's a high definition display that doesn't show any noticeable pixels, but isn't too large or dense to cause issues. I would hate to have a car where the whole gauge cluster is just a large LCD or OLED screen, and suffer from (rows of) dead pixels down the road. The gauges have ice blue lighting during the night, it's just tasteful all around with a design I'd describe as timeless.

The car has some configurable lights in the doors, cup holders and in the foot rest area, the color of the light can be picked from a predefined palette. I roll with teal. There are physical buttons for the essential functions, it's rare that I need something that can be controlled only from the touch screen. The AC works fine, but I have some ear issues that makes me wish I could set up the vents and the temperature just right. Sadly I just can't, it's warm or too cold or drafty. The problem is likely located between the front seat and the steering wheel, Leslie has no issues on her side. The power windows work well too, but I open mine only when necessary and when the car is stationary, otherwise my next trip would be to the doctor's office with a nasty ear infection.

The fuel consumption is okay. It wouldn't be a good long distance commuter car, but with my usage habits it's fine. It can do 25 MPG in mixed city and highway and about 30 MPG in pure highway use. On long trips the fuel gauge is amusing to look at, as if there were a bunch of frat boys in the engine bay yelling chug-chug-chug! At least it's always fun to stop at service plazas to refill the 12-gallon tank and have a little break from the monotony of USA highways. Since I got the car, in just over 15000 miles it consumed 656 gallons of 93 octane premium gas that cost me $1600.

I'm happy with the car altogether and I have no buyer's remorse over a year of having the 2017 version on the roads. It's hard to find things I don't like about it, takes some nitpicking to list any. Beside the AC issues that are rooted in user error, I don't like how there are only a few storage compartments. It's hard to put stuff away in a neat, organized way. The space in the back seat area is fine, but could be bigger. Kids would fit alright, but longer trips may not be so pleasant for adults. However this is probably true for most cars with a normal wheel base.

I thought the trunk space will be bigger, too. It's big enough, sure, and can be made huge by folding the rear seats down. I really wish I could go to IKEA and buy a new dresser or something, it would fit in like a charm. We don't need new furniture now. But in day to day use, the trunk is a space hard to utilize neatly without some proper storage solution. It's true for most cars anyway, and maybe I'll figure something out for it. When I put my 29" mountain bike into the car, it's a bit of a logistical challenge but it just fits. Popping the front wheel off the bike would help a lot, I just keep forgetting to order a set of quick releases.

Ford has been teasing that the SYNC 3 that comes with this car will receive a software update to bring it up to the level of the 2017 version with Apple CarPlay and Android Auto capabilities. It didn't happen yet, in fact not a single update came so far and Ford is backpedaling now, saying that it would need hardware modifications to enable that... Yeah sure, sure it would. Sounds like BS to me, and I'm not sure what's gonna happen with it later on. Luckily I like the functionality as-is, so it's not a burning issue, but after several years the need may arise to replace it with Apple CarPlay. Also there are no map updates over the air as predicted, Ford is milking customers with that one. I'll probably wait a couple of years then get that paid update. It's okay so far, didn't run into any traffic issues due to outdated maps, yet.

Either way, it's a great car that's a joy to drive and I find it beautiful. If you're looking for a used one, try one, sounds like a good choice to me. But don't ask for mine, it's not for sale. We are inseparable.

I spent the majority of my childhood and my early adult life coding a lot of demos and a few games on Commodore 64 and Plus/4, in assembly. Later I also worked on demos for the Amiga and Gameboy Color. I miss those days a lot, and in the recent years I had this recurring urge to write a new C64 demo. Or at least do something in assembly because it's just fun. After spending quite some time reading up on the ins and outs of ARM V7 that I could code for in my Raspberry Pi, I realized that it's just silly to go down that route. The Pi hardware is surprisingly poorly documented, people are just guessing or reverse engineering Linux distributions to figure out how the GPU's screen buffer can be accessed, mail slots and messaging, ummm no thanks. I can spend my time a bit more wisely, and my heart is captured by the Commodore 64 forever anyway.

My friends and I used to code in an awesome utility called Turbo Assembler, it's still a fascinating piece of marvel. It loaded to the memory address $9000 and upwards, and the tokenized source code was building downwards from there, so it left us plenty of work space in the 64 kilobytes RAM that the Commodore 64 and Plus/4 were built with.

Back in those days I wished I could write my own assembler, but I couldn't even imagine how I could do that effectively, with at least the same features I used in Turbo Assembler. Then decades passed, I worked on a bunch of serious software, and suddenly it didn't sound impossible anymore, just a bit challenging. I decided that if I'm going to code a new demo (or twenty), it must be done in my own assembler. I got to it, started working on the project on a Sunday morning on June 18th, and by the next Saturday it compiled the first test source code, a music player.

Retro Assembler was born.

Retro Assembler – Commodore 64 source code in Notepad++

After a month of work on it (not full time, just evenings and weekends), it's ready to be published. It's so advanced that it could easily be called a version 2.0 but I'll just go with a solid 1.0 for good measure. I'm afraid I got a bit carried away with it (in the meantime my wife Leslie binge-watched all five seasons of Orange Is The New Black), now it's a macro assembler with a lot of serious features. I made it to support the MOS6502 microprocessor family, so most Commodore computers and some early game consoles (Nintendo NES, Atari 2600 etc) can be targeted with it. But it's not all it will ever do. The application was designed in a way that adding other CPU types is doable with moderate amount of work, so in a future release the Gameboy CPU will be supported too, and shortly after the Zilog Z80 as well. I might even code something for the ZX Spectrum I never had. Motorola 68000 and perhaps ARM may be supported as well, but those can wait.

Bin, H6X, T64 and D64 output formats, that work great with the WinVICE Commodore 64 etc Emulator

Disassembler

Syntax highlighting in Notepad++ by custom User Defined Languages

The assembler is a command line tool that can be wired up in Notepad++ (and other editors) to turn it into a Development Environment with Build & Execute

I guess that's good enough for an initial release. I don't expect too many people to start using it, but even if nobody ever downloads it, I don't mind. I'm really proud of this assembler and now it's time for me to start working on that demo. Fellow sceners, I'm back!

This is a bit of a recap. I wrote this app in April 2015 and now I issued a minor update.

Ambience puts you into the mood for work or relaxation by playing ambient sounds in the background. Whether it's nature or a busy coffee shop that helps you to get into the zone, Ambience got you covered. The app is based on Noizio for macOS and iOS, but it had no Windows version, so I made one.

Ambience – Noizio for Windows

Back then not only I made it High DPI aware, but also created an installer version. I decided to drop the installer due to its issues, the app can be used in portable mode which is better anyway. And with the release of Windows 10, I made a small adjustment in the header to look better with white title bars and compiled the app for .net Framework 4.7 which will be the minimum version for my applications from now on.

A couple of years ago I restarted this website, but it didn't go too far before I neglected it, as it happened before. Now once again I'm doing the same thing, hoping this time it sticks. As usual, these reboots are tied to blog engine rewrites and guess what, I've rewritten this blog engine again. One might think that I don't like to blog, I just like to write blog engines. One might be right. One may also recall the definition of insanity at this point.

Anyway, this blog is a platform for my thoughts, an outlet where I can write about things I developed, used, enjoyed, experienced, or just interesting to me. It may even contain some details of my personal life. How it will go remains to be seen, these pages are here to be filled with something you may find valuable or just entertaining. There are so many things in life that won't fit into a tweet.