More from Christian Selig
Spoiler: 3D printed! The colored ports really sell the effect If you’re anything like me, you’ve found the new, tinier Mac mini to be absolutely adorable. But you might also be like me that you either already have an awesome M1 Mac mini that you have no real reason to replace, or the new Mac mini just isn’t something you totally need. While that logic might be sound, but it doesn’t make you want one any less. To help cure this FOMO, I made a cute little 3D printable Mac mini that can sit on your desk and be all cute. But then I had an even better idea, the new Mac mini is powerful sure, but it can’t hold snacks. Or a plant. Or your phone. Or pens/pencils. So I also made some versions you can print that add some cute utility to your desk in the form of the new Mac mini. They’re free of course! Just chuck ’em into your (or your friend’s) 3D printer. It even has all the little details modeled, like the power button, ports (including rear), and fan holes! They’re pretty easy to print, it’s in separate parts for ease of printing the bottom a different color (black) versus the top, then just put a dab of glue (or just use gravity) to keep them together. If you have a multi-color 3D printer, you can color the ports and power LED to make it look extra cool (or just do it after the fact with paint). Here are the different options for your desk! Secret item stash The possibilities for what you can store on your desk are now truly endless. Individually wrapped mints? Key switches? Screws? Paper clips? Rubber bands? Flash drives? Download link: https://makerworld.com/en/models/793456 A very green sorta Mac First carbon neutral Mac is cool and all but what if your Mac mini literally had a plant in it? Every desk needs a cute little plant. Download link: https://makerworld.com/en/models/793464 Phone holder A phone/tablet holder is an essential item on my desk for debugging things, watching a video, or just keeping an eye on an Uber Eats order. Before, guests came over and saw my boring phone stand and judged me, now they come over and think I’m exciting and well-traveled. You can even charge your phone/tablet in portrait mode by pushing the cable through a tunnel made through the Ethernet port that then snakes up to the surface. Download link: https://makerworld.com/en/models/793495 Pen holder The Playdate had the cutest little pen/pencil holder accessory but it unfortunately never shipped and my desk is sad. This will be a nice stand in for your beloved pens, pencils, markers, and Apple Pencils. Download link: https://makerworld.com/en/models/793470 A solid model Or if you just want to stare at it without any frills, you can just print the normal model too! Download link: https://makerworld.com/en/models/793447 Printer recommendation Whenever I post about 3D printing I understandably get a bunch of “Which 3D printer should I buy??” questions. This isn’t sponsored, but I’ve found over the last few years the answer has been pretty easy: something from Bambu Lab. Their printers are somehow super easy to use, well designed, and reasonably priced. Prusas are great too, but I think Bambu is hard to beat for the price. Don’t get an Ender. So if you’re looking for a printer now, Black Friday deals are aplenty so it’s pretty much the best time to pick one up. I’d grab something in their A series if you’re on a budget, or the P1S for a bit more if you can swing it (that’s what I use). https://bambulab.com On the other hand if you just want to print one thing now and again, a lot of local libraries are starting to have 3D printers so that might be worth looking into! And online services exist too (eg: JLCPCB and PCBWay), but if you do it with any regularity a 3D printer is a really fun thing to pick up. Enjoy! ❤️ Learning 3D modeling over the last year has been a ton of fun so I love a good excuse to practice, and shout out to Jerrod Hofferth and his amazing 3D printable Mac mini tower (that you should totally download) for the idea to solve my desire with some 3D printing! Also, the models are almost certainly not accurate down to the micrometer as I don’t actually have one, they’re based off Apple’s measurements as well as measuring screenshots. But it should be close! If you have a multi-color 3D printer, the linked models have the colors built-in for your ready to go, but if you want to print it in single-colors I also made versions available with the top and bottom separate as well as the logo, so you can print them separately in the individual colors then connect them with a touch of super glue or something.
Hey I'm a developer not an artist Following my last blog post about difficulties surrounding UserDefaults and edge cases that lead to data loss (give it a read if you haven’t, it’s an important precursor to this post!), I wanted to build something small and lightweight that would serve to fix the issues I was encountering with UserDefaults and thus TinyStorage was born! It’s open source so you can use it in your projects too if would like. GitHub link 🐙 Overview As mentioned in that blog post, UserDefaults has more and more issues as of late with returning nil data when the device is locked and iOS “prelaunches” your app, leaving me honestly sort of unable to trust what UserDefaults returns. Combined with an API that doesn’t really do a great job of surfacing whether it’s available, you can quite easily find yourself in a situation with difficult to track down bugs and data loss. This library seeks to address that fundamentally by not encrypting the backing file, allowing more reliable access to your saved data (if less secure, so don’t store sensitive data), with some niceties sprinkled on top. This means it’s great for preferences and collections of data like bird species the user likes, but not for sensitive details. Do not store passwords/keys/tokens/secrets/diary entries/grammy’s spaghetti recipe, anything that could be considered sensitive user information, as it’s not encrypted on the disk. But don’t use UserDefaults for sensitive details either as UserDefaults data is still fully decrypted when the device is locked so long as the user has unlocked the device once after reboot. Instead use Keychain for sensitive data. As with UserDefaults, TinyStorage is intended to be used with relatively small, non-sensitive values. Don’t store massive databases in TinyStorage as it’s not optimized for that, but it’s plenty fast for retrieving stored Codable types. As a point of reference I’d say keep it under 1 MB. This reliable storing of small, non-sensitive data (to me) is what UserDefaults was always intended to do well, so this library attempts to realize that vision. It’s pretty simple and just a few hundred lines, far from a marvel of filesystem engineering, but just a nice little utility hopefully! (Also to be clear, TinyStorage is not a wrapper for UserDefaults, it is a full replacement. It does not interface with the UserDefaults system in any way.) Features Reliable access: even on first reboot or in application prewarming states, TinyStorage will read and write data properly Read and write Swift Codable types easily with the API Similar to UserDefaults uses an in-memory cache on top of the disk store to increase performance Thread-safe through an internal DispatchQueue so you can safely read/write across threads without having to coordinate that yourself Supports storing backing file in shared app container Uses NSFileCoordinator for coordinating reading/writing to disk so can be used safely across multiple processes at the same time (main target and widget target, for instance) When using across multiple processes, will automatically detect changes to file on disk and update accordingly SwiftUI property wrapper for easy use in a SwiftUI hierarchy (Similar to @AppStorage) Uses OSLog for logging A function to migrate your UserDefaults instance to TinyStorage Limitations Unlike UserDefaults, TinyStorage does not support mixed collections, so if you have a bunch of strings, dates, and integers all in the same array in UserDefaults without boxing them in a shared type, TinyStorage won’t work. Same situation with dictionaries, you can use them fine with TinyStorage but the key and value must both be a Codable type, so you can’t use [String: Any] for instance where each string key could hold a different type of value. Installation Simply add a Swift Package Manager dependency for https://github.com/christianselig/TinyStorage.git Usage First, either initialize an instance of TinyStorage or create a singleton and choose where you want the file on disk to live. To keep with UserDefaults convention I normally create a singleton for the app container: extension TinyStorage { static let appGroup: TinyStorage = { let appGroupID = "group.com.christianselig.example" let containerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupID)! return .init(insideDirectory: containerURL) }() } (You can store it wherever you see fit though, in URL.documentsDirectory is also an idea for instance!) Then, decide how you want to reference your keys, similar to UserDefaults you can use raw strings, but I recommend a more strongly-typed approach, where you simply conform a type to TinyStorageKey and return a var rawValue: String and then you can use it as a key for your storage without worrying about typos. If you’re using something like an enum, making it a String enum gives you this for free, so no extra work! After that you can simply read/write values in and out of your TinyStorge instance: enum AppStorageKeys: String, TinyStorageKey { case likesIceCream case pet case hasBeatFirstLevel } // Read let pet: Pet? = TinyStorage.appGroup.retrieve(type: Pet.self, forKey: AppStorageKeys.pet) // Write TinyStorage.appGroup.store(true, forKey: AppStorageKeys.likesIceCream) (If you have some really weird type or don’t want to conform to Codable, just convert the type to Data through whichever means you prefer and store that, as Data itself is Codable.) If you want to use it in SwiftUI and have your view automatically respond to changes for an item in your storage, you can use the @TinyStorageItem property wrapper. Simply specify your storage, the key for the item you want to access, and specify a default value. @TinyStorageItem(key: AppStorageKey.pet, storage: .appGroup) var pet: = Pet(name: "Boots", species: .fish, hasLegs: false) var body: some View { Text(pet.name) } You can even use Bindings to automatically read/write. @TinyStorageItem(key: AppStorageKeys.message, storage: .appGroup) var message: String = "" var body: some View { VStack { Text("Stored Value: \(message)") TextField("Message", text: $message) } } It also addresses some of the annoyances of @AppStorage, such as not being able to store collections: @TinyStorageItem(key: "names", storage: .appGroup) var names: [String] = [] Or better support for optional values: @TinyStorageItem(key: "nickname", storage: .appGroup) var nickname: String? = nil // or "Cool Guy" Hope it’s handy! If you like it or have any feedback let me know! I’m going to start slowly integrating it into Pixel Pals and hopefully solve a few bugs in the process.
Excuse the alarmist title, but I think it’s justified, as it’s an issue that’s caused me a ton of pain in both support emails and actually tracking it down, so I want to make others aware of it so they don’t similarly burned. Brief intro For the uninitiated, UserDefaults (née NSUserDefaults) is the de facto iOS standard for persisting non-sensitive, non-massive data to “disk” (AKA offline). In other words, are you storing some user preferences, maybe your user’s favorite ice cream flavors? UserDefaults is great, and used extensively from virtually every iOS app to Apple sample code. Large amount of data, or sensitive data? Look elsewhere! This is as opposed to just storing it in memory where if the user restarts the app all the data is wiped out. It’s a really handy tool with a ton of nice, built-in things for you: No needing to mess with writing to files yourself, and better yet, no need to coordinate when to persist values back to the disk Easy to share data between your app’s main target and secondary targets (like a widget target) Automatic serialization and deserialization: just feed in a String, Date, Int, and UserDefaults handles turning it into bytes and back from bytes Thread-safe! So it’s no wonder it’s used extensively. But yeah, keep the two limitations in mind that Apple hammers home: Don’t store sensitive data in UserDefaults, that’s what Keychain is for Don’t store large amounts of data in UserDefaults, use something like Core Data or Swift Data Okay, so what’s the problem Turns out, sometimes you can request your saved data back from UserDefaults and it… just won’t have it! That’s a pretty big issue for a system that’s supposed to reliably store data for you. This can amount to an even bigger issue that leads to permanent data loss. Imagine a situation where a user has been meticulously opening your app for 364 days in a row. On day 365, your app promised a cool reward! When the user last closed the app, you stored 364 to UserDefaults. The user wakes up on day 365, excited for their reward: App launches App queries UserDefaults for how many days in a row the user has opened the app App returns 0 (UserDefaults is mysteriously unavailable so its API returns the default integer value of 0) It’s a new day, so you increment that value by 1, so that 0 changes to 1 Save that new value back to UserDefaults Now, instead of your user having a fun celebration, their data has been permanently overwritten and reset! They are having a Sad Day™. It basically means, if at any point you trust UserDefaults to accurately return your data (which you know, sounds like a fair assumption) you might just get incorrect data, which you then might make worse by overwriting good data with. And remember, you’re not meant to store sensitive data in UserDefaults, but even if it’s not sensitive data it might be valuable. The user’s day streak above is not sensitive data that would be bad if leaked online like a password, but it is valuable to that user. In fact I’d argue any data persisted to the disk is valuable, otherwise you wouldn’t be saving it. And you should be always be able to trust an API to reliably save your data. What??? How is this happening? 😵💫 As I understand it, there’s basically two systems coming together (and working incorrectly, if you ask me) to cause this: 1. Sensitive data encryption When using Keychain or files directly, as a developer you can mark data that should be encrypted until the device is unlocked by Face ID/Touch ID/passcode. This way if you’re storing a sensitive data like a token or password on the device, the contents are encrypted and thus unreadable until the device is unlocked. This meant if the device was still locked, and you, say, had a Lock Screen Widget that performed an API request, you would have to show placeholder data until the user unlocked the device, because the sensitive data, namely the user’s API token, was encrypted and unable to be used by the app to fetch and show data until the user unlocked the device. Not the end of the world, but something to keep in mind for secure data like API tokens, passwords, secrets, etc. 2. Application prewarming Starting with iOS 15, iOS will sometimes wake up your application early so that when a user launches it down the road it launches even quicker for them, as iOS was able to do some of the heavy lifting early. This is called prewarming. Thankfully per Apple, your application doesn’t fully launch, it’s just some processes required to get your app working: Prewarming executes an app’s launch sequence up until, but not including, when main() calls UIApplicationMain(::::). Okay, so what happened with these two? It seems at some point, even though UserDefaults is intended for non-sensitive information, it started getting marked as data that needs to be encrypted and cannot be accessed until the user unlocked their device. I don’t know if it’s because Apple found developers were storing sensitive data in there even when they shouldn’t be, but the result is even if you just store something innocuous like what color scheme the user has set for your app, that theme cannot be accessed until the device is unlocked. Again, who cares? Users have to unlock the device before launching my app, right? I thought so too! It turns out, even though Apple’s prewarming documentation states otherwise, developers have been reporting for years that that’s just wrong, and your app can effectively be fully launched at any time, including before the device is even unlocked. Combining this with the previous UserDefaults change, you’re left with the above situation where the app is launched with crucial data just completely unavailable because the device is still locked. UserDefaults also doesn’t make this clear at all, which it could do by for instance returning nil when trying to access UserDefaults.standard if it’s unavailable. Instead, it just looks like everything is as it should be, except none of your saved keys are available anymore, which can make your app think it’s in a “first launch after install” situation. The whole point of UserDefaults is that it’s supposed to reliably store simple, non-sensitive data so it can be accessed whenever. The fact that this has now changed drastically, and at the same time your app can be launched effectively whenever, makes for an incredibly confusing, dangerous, and hard to debug situation. And it’s getting worse with Live Activities If you use Live Activities at all, the cool new API that puts activities in your Dynamic Island and Lock Screen, it seems if your app has an active Live Activity and the user reboots their device, virtually 100% of the time the above situation will occur where your app is launched in the background without UserDefaults being available to it. That means the next time your user actually launches the app, if at any point during your app launching you trusted the contents of UserDefaults, your app is likely in an incorrect state with incorrect data. This bit me badly, and I’ve had users email me over time that they’ve experienced data loss, and it’s been incredibly tricky to pinpoint why. It turns out it’s simply because the app started up, assuming UserDefaults would return good data, and when it transparently didn’t, it would ultimately overwrite their good data with the returned bad data. I’ve talked to a few other developers about this, and they’ve also reported random instances of users being logged out or losing data, and after further experimenting been able to now pinpoint that this is what caused their bug. It happened in past apps to me as well (namely users getting signed out of Apollo due to a key being missing), and I could never figure out why, but this was assuredly it. If you’ve ever scratched your head at a support email over a user’s app being randomly reset, hopefully this helps! I don’t like this ☹️ I can’t overstate what a misstep I think this was. Security is always a balance with convenience. Face ID and Touch ID strike this perfectly; they’re both ostensibly less secure per Apple’s own admission than, say, a 20 digit long password, but users are much more likely to adopt biometric security so it’s a massive overall win. Changing UserDefaults in this way feels more on the side of “Your company’s sysadmin requiring you to change your password every week”: dubious security gains at the cost of user productivity and headaches. But enough moaning, let’s fix it. Solution 1 Because iOS is now seemingly encrypting UserDefaults, the easiest solution is to check UIApplication.isProtectedDataAvailable and if it returns false, subscribe to NotificationCenter for when protectedDataDidBecomeAvailableNotification is fired. This was previously really useful for knowing when Keychain or locked files were accessible once the device was unlocked, but it now seemingly applies to UserDefaults (despite not being mentioned anywhere in its documentation or UserDefault’s documentation 🙃). I don’t love this solution, because it effectively makes UserDefaults either an asynchronous API (“Is it available? No? Okay I’ll wait here until it is.”), or one where you can only trust its values sometimes, because unlike the Keychain API for instance, UserDefaults API itself does not expose any information about this when you try to access it when it’s in a locked state. Further, some developers have reported UserDefaults still being unavailable even once isProtectedDataAvailable returns true. Solution 2 For the mentioned reasons, I don’t really like/trust Solution 1. I want a version of UserDefaults that acts like what it says on the tin: simply, quickly, and reliably retrieve persisted, non-sensitive values. This is easy enough to whip up ourselves, we just want to keep in mind some of the things UserDefaults handles nicely for us, namely thread-safety, shared between targets, and an easy API where it serializes data without us having to worry about writing to disk. Let’s quickly show how we might approach some of this. UserDefaults is fundamentally just a plist file stored on disk that is read into memory, so let’s create our own file, and instead of marking it as requiring encryption like iOS weirdly does, we’ll say that’s not required: // Example thing to save let favoriteIceCream = "chocolate" // Save to your app's shared container directory so it can be accessed by other targets outside main let appGroupID = "" // Get the URL for the shared container guard let containerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupID) else { fatalError("App Groups not set up correctly") } // Create the file URL within the shared container let fileURL = containerURL.appendingPathComponent("Defaults") do { let data = favoriteIceCream.data(using: .utf8) try data.write(to: fileURL) // No encryption please I'm just storing the name of my digital cow Mister Moo try FileManager.default.setAttributes([.protectionKey: .none], ofItemAtPath: fileURL.path) print("File saved successfully at \(fileURL)") } catch { print("Error saving file: \(error.localizedDescription)") } (Note that you could theoretically modify the system UserDefaults file in the same way, but Apple documentation recommends against touching the UserDefaults file directly.) Next let’s make it thread safe by using a DispatchQueue. private static let dispatchQueue = DispatchQueue(label: "DefaultsQueue") func retrieveFavoriteIceCream() -> String? { return dispatchQueue.sync { guard let containerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "app-group-id") else { return nil } let fileURL = containerURL.appendingPathComponent(fileName) do { let data = try Data(contentsOf: fileURL) return String(data: data, encoding: .utf8) } catch { print("Error retrieving file: \(error.localizedDescription)") return nil } } } func save(favoriteIceCream: String) { return dispatchQueue.sync { guard let containerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "app-group-id") else { return } let fileURL = containerURL.appendingPathComponent(fileName) do { let data = favoriteIceCream.data(using: .utf8) try data.write(to: fileURL) try FileManager.default.setAttributes([.protectionKey: .none], ofItemAtPath: fileURL.path) print("File saved successfully at \(fileURL)") } catch { print("Error saving file: \(error.localizedDescription)") } } } (You probably don’t need a concurrent queue for this, so I didn’t.) But with that we have to worry about data types, let’s just make it so long as the type conforms to Codable we can save or retrieve it: func saveCodable(_ codable: Codable, forKey key: String) { do { let data = try JSONEncoder().encode(codable) // Persist raw data bytes to a file like above } catch { print("Unable to encode \(codable): \(error)") } } func codable<T: Codable>(forKey key: String, as type: T.Type) -> T? { let data = // Fetch raw data from disk as done above do { return try JSONDecoder().decode(T.self, from: data) } catch { print("Error decoding \(T.self) for key \(key) with error: \(error)") return nil } } // Example usage: let newFavoriteIceCream = "strawberry" saveCodable(newFavoriteIceCream, forKey: "favorite-ice-cream") let savedFavoriteIceCream = codable(forKey: "favorite-ice-cream", as: String.self) Put those together, wrap it in a nice little library, and bam, you’ve got a UserDefaults replacement that acts as you would expect. In fact if you like the encryption option you can add it back pretty easily (don’t change the file protection attributes) and you could make it clear in the API when the data is inaccessible due to the device being locked, either by throwing an error, making your singleton nil, awaiting until the device is locked, etc. End Maybe this is super obvious to you, but I’ve talked to enough developers where it wasn’t, that I hope in writing this it can save you the many, many hours I spent trying to figure out why once in a blue moon a user would be logged out, or their app state would look like it reset, or worst of all: they lost data.
For those not aware, a few months ago after reaching out to me, YouTube contacted the App Store stating that Juno does not adhere to YouTube guidelines and modifies the website in a way they don’t approve of, and alludes to their trademarks and iconography. I don’t personally agree with this, as Juno is just a web view, and acts as little more than a browser extension that modifies CSS to make the website and video player look more “visionOS” like. No logos are placed other than those already on the website, and the “for YouTube” suffix is permitted in their branding guidelines. I stated as much to YouTube, they wouldn’t really clarify or budge any, and as a result of both parties not being able to come to a conclusion I received an email a few minutes ago from Apple that Juno has been removed from the App Store. Juno was a fun hobby project for me to build. As a developer I wanted to get some experience building for the Vision Pro, and as a user I wanted a nice way to watch YouTube on this cool new device. As a result, I really enjoyed building Juno, but it was always something I saw as fundamentally a little app I built for fun. Because of that, I have zero desire to spin this into a massive fight akin to what happened with Reddit years ago. That’s kind of the opposite of fun. I hope that’s understandable. For those who have Juno, to my knowledge it should continue to work fine until/unless YouTube updates in some fashion that breaks stuff. Sorry it had to end this way, I had some really cool stuff planned for it that I think would have been a lot of fun! It’s been genuinely awesome hearing all the kind words from Vision Pro users who have loved the app. 🫡
More in technology
Talks about the famous Dragon's Lair
totally unreasonable price for a completely untested item, as-was, no returns, with no power supply, no wiring harness and no auxiliary daughterboards. At the end of this article, we'll have it fully playable and wired up to a standard ATX power supply, a composite monitor and off-the-shelf Atari joysticks, and because this board was used for other related games from that era, the process should work with only minor changes on other contemporary Gremlin arcade classics like Blockade, Hustle and Comotion [sic]. It's time for a Refurb Weekend. a July 1982 San Diego Reader article, the locally famous alternative paper I always snitched a copy of when I was downtown, and of which I found a marginally better copy to make these scans. There's also an exceptional multipart history of Gremlin you can read but for now we'll just hit the highlights as they pertain to today's project. ported to V1 Unix and has a simpler three-digit variant Bagels which was even ported to the KIM-1. Unfortunately his friends didn't have minicomputers of their own, so Hauck painstakingly put together a complete re-creation from discrete logic so they could play too, later licensed to Milton Bradley as their COMP IV handheld. Hauck had also been experimenting with processor-controlled video games, developing a simple homebrew unit based around the then-new Intel 8080 CPU that could connect to his television set and play blackjack. Fogleman met Hauck by chance at a component vendor's office and hired him on to enhance the wall game line, but Hauck persisted in his experiments, and additionally presented Fogleman with a new and different machine: a two-player game played with buttons on a video TV display, where each player left a boxy solid trail in an attempt to crowd out the other. To run the fast action on its relatively slow ~2MHz CPU and small amount of RAM, a character generator circuit made from logic chips painted a 256x224 display from 32 8x8 tiles in ROM specified by a 32x28 screen matrix, allowing for more sophisticated shapes and relieving the processor of having to draw the screen itself. (Does this sound like an early 8-bit computer? Hold that thought.) patent application was too late and too slow to stop the ripoffs. (For the record, Atari programmer Dennis Koble was adamant he didn't steal the idea from Gremlin, saying he had seen similar "snake" games on CompuServe and ARPANET, but Nolan Bushnell nevertheless later offered Gremlin $100,000 in "consolation" which the company refused.) Meanwhile, Blockade orders evaporated and Gremlin's attempts to ramp up production couldn't save it, leaving the company with thousands of unused circuit boards, game cabinets and video monitors. While lawsuits against the copycats slowly lumbered forward, Hauck decided to reprogram the existing Blockade hardware to play new games, starting with converting the Comotion board into Hustle in 1977 where players could also nab targets for additional points. The company ensured they had a thousand units ready to ship before even announcing it and sales were enough to recoup at least some of the lost investment. Hauck subsequently created a reworked version of the board with the same CPU for the more advanced game Depthcharge, initially testing poorly with players until the controls were simplified. This game was licensed to Taito as Sub Hunter and the board reworked again for the target shooter Safari, also in 1977, and also licensed by Taito. For 1978, Gremlin made one last release using the Hustle-Comotion board. This game was Blasto. present world record is 8,730), but in two player mode the players can also shoot each other for an even bigger point award. This means two-player games rapidly turn into active hunts, with a smaller bonus awarded to a player as well if the other gets nailed by a mine. shown above with a screenshot of the interactive on-board assembler. Noval also produced an education-targeted system called the Telemath, based on the 760 hardware, which was briefly deployed in a few San Diego Unified elementary schools. Alas, they were long gone before we arrived. Industry observers were impressed by the specs and baffled by the desk. Although the base price of $2995 [about $16,300] was quite reasonable considering its capabilities, you couldn't buy it without its hulking enclosure, which made it a home computer only to the sort of people who would buy a home PDP-8. (Raises hand.) Later upgrades with a Z80 and a full 32K didn't make it any more attractive to buyers and Noval barely sold about a dozen. Some of the rest remained at Gremlin as development systems (since they practically were already), and an intact upgraded unit with aftermarket floppy drives lives at the Computer History Museum. The failure of Noval didn't kill Gremlin outright, but Fogleman was concerned the company lacked sufficient capital to compete more strongly in the rapidly expanding video game market, and Noval didn't provide it. With wall game sales fading fast and cash flow crunched, the company was slowly approaching bankruptcy by the time Blasto hit arcades. At the same time, Sega Enterprises, Inc., then owned by conglomerate Gulf + Western (who also then owned Paramount Pictures), was looking for a quick way to revive its failing North American division which was only surviving on the strength of its aggressively promoted mall arcades. Sega needed development resources to bring out new games States-side, and Gremlin needed money. In September 1978 Fogleman agreed to make Gremlin a Sega subsidiary in return for an undisclosed number of shares, and became a vice chairman. Sega was willing to do just about anything to achieve supremacy on this side of the Pacific. In addition to infusing cash into Gremlin to make new games (as Gremlin/Sega) and distribute others from their Japanese peers and partners (as Sega/Gremlin), Sega also perceived a market opportunity in licensing arcade ports to the growing home computer segment. Texas Instruments' 99/4 had just hit the market in 1979 to howls there was hardly any software, and their close partner Milton Bradley was looking for marketable concepts for cartridge games. Blasto had simple fast action and a good name in the arcades, required only character graphics (well within the 9918 video chip's capabilities) and worked for both one or two players, and Sega had no problem blessing a home port of an older property for cheap. Milton Bradley picked up the license to Hustle as well. Bob Harris for completion, and TI house programmer Kevin Kenney wrote some additional features. 1 to 40 (obviously some thought was given to using the same PCB as much as possible). The power header is also a 10-pin block and the audio and video headers are 4-pin. Oddly, the manual doesn't say anywhere what the measurements are, so I checked them with calipers and got a pitch of around 0.15", which sounds very much like a common 0.156" header. I ordered a small pack of those as an experiment. 0002 because of the control changes: if you have an 814-0001, then you have a prototype. The MAME driver makes reference to an Amutech Mine Sweeper which is a direct and compatible ripoff of this board — despite the game type, it's not based on Depthcharge.) listed with the part numbers for the cocktail, but the ROM contents expected in the hashes actually correspond to the upright. Bipolar ROMs and PROMs are, as the name suggests, built with NPN bipolar junction transistors instead of today's far more common MOSFETs ("MOS transistors"). This makes them lower density but also faster: these particular bipolar PROMs have access times of 55-60ns as opposed to EPROMs or flash ROMs of similar capacity which may be multiple times slower depending on the chip and process. For many applications this doesn't matter much, but in some tightly-timed systems the speed difference can make it difficult to replace bipolar PROMs with more convenient EPROMs, and most modern-day chip programmers can't generate the higher voltage needed to program them (you're basically blowing a whole bunch of microscopic Nichrome metal fuses). Although modern CMOS PROMs are available at comparable speeds, bipolars were once very common, including in military environments where they could be manufactured to tolerate unusually harsh operating conditions. The incomparable Ken Shirriff has a die photo and article on the MMI 5300, an open-collector chip which is one of the military-spec parts from this line. Model 745 KSR and bubble memory Model 763 ASR, use AMD 8080s! The Intel 8080A is a refined version of the original Intel 8080 that works properly with more standard TTL devices (the original could only handle low-power TTL); the "NL" tag is TI's designation for a plastic regular-duty DIP. Its clock source is a 20.79MHz crystal at Y1 which is divided down by ten to yield the nominal clock rate of 2.079MHz, slightly above its maximum rating of 2MHz but stable enough at that speed. The later Intel 8080A-1 could be clocked up to 3.125MHz and of course the successor Intel 8085 and Zilog Z80 processors could run faster still. An interesting absence on this board is an Intel 8224 or equivalent to generate the 8080A's two-phase clock: that's done directly off the crystal oscillator with discrete logic, an elegant (and likely cheaper) design by Hauck. The video output also uses the same crystal. Next to the CPU are pads for the RAM chips. You saw six of them in the last picture under the second character ROM (316-0100M), all 2102 (1Kbit) static RAM. These were the chips I was most expecting to fail, having seen bad SRAM in other systems like my KIM-1. The ones here are 450ns Fairchild 21021 SRAMs in the 21021PC plastic case and "commercial" temperature range, and six of them adds up to 768 bytes of memory. NOS examples and equivalents are fortunately not difficult to find. Closer to the CPU in this picture, however, are two more RAM chip pads that are empty except for tiny factory-installed jumpers. On the Hustle and Blasto boards (both), they remain otherwise unpopulated, and there is an additional jumper between E4 and E5 also visible in the last picture. The Comotion board, however, has an additional 256 bytes of RAM here (as two more 1024x1 SRAMs). On that board these pads have RAM, there are no jumpers on the pads, and the jumper is now between E3 (ground) and E5. This jumper is also on Blockade, even though it has only five 2102s and three dummy jumpers on the other pads. That said, the games don't seem to care how much RAM is present as long as the minimum is: the current MAME driver gives all of them the full 1K. this 8080 system which uses a regulator). Tracing the schematic out further, the -12V line is also used with the +5V and +12V lines to run the video circuit. These are all part of the 10-pin power header. almost this exact sequence of voltages? An AT power supply connector! If we're clever about how we put the two halves on, we can get nearly the right lines in the right places. The six-pin AT P9 connector reversed is +5V, +5V, +5V, -5V, ground, ground, so we can cut the -5V to be the key. The six-pin AT P8 connector not reversed is power-good, +5V (or NC), +12V, -12V, ground, ground, so we cut the +5V to be the key, and cut the power-good line and one of the dangling grounds and wire ground to the power-good pin. Fortunately I had a couple spare AT-to-ATX converter cables from when we redid the AT power supply on the Alpha Micro Eagle 300. connectors since we're going to modify them anyway. A quick couple drops of light-cured cyanoacrylate into the key hole ... Something's alive! An LED glows! Time now for the video connector to see if we can get a picture! a nice 6502 reset circuit). The board does have its own reset circuit, of a sort. You'll notice here that the coin start is wired to the same line, and the manual even makes reference to this ("The circuitry in this game has been arranged so that the insertion of a quarter through the coin mechanism will reset the restart [sic] in the system. This clears up temporary problems caused by power line disturbances, static, etc."). We'll of course be dealing with the coin mechanism a little later, but that doesn't solve the problem of bringing the machine into the attract mode when powered on. I also have doubts that people would have blithely put coins into a machine that was obviously on the fritz. pair is up and down, or left and right, but not which one is exactly which because that depends on the joystick construction. We'll come back to this. Enterprises) to emphasize the brand name more strongly. The company entered a rapid decline with the video game crash of 1983 and the manufacturing assets were sold to Bally Midway with certain publishing rights, but the original Gremlin IP and game development teams stayed with Sega Electronics and remained part of Gulf+Western until they were disbanded. The brand is still retained as part of CBS Media Ventures today though modern Paramount Global doesn't currently use the label for its original purpose. In 1987 the old wall game line was briefly reincarnated under license, also called Gremlin Industries and with some former Gremlin employees, but only released a small number of new machines before folding. Meanwhile, Sega Enterprises separated from Gulf+Western in a 1984 management buyout by original founder David Rosen, Japanese executive Hayao Nakayama and their backers. This Sega is what people consider Sega today, now part of Sega Sammy Holdings, and the rights to the original Gremlin games — including Blasto — are under it. Lane Hauck's last recorded game at Gremlin/Sega was the classic Carnival in 1980 (I played this first on the Intellivision). After leaving the company, he held positions at various companies including San Diego-based projector manufacturer Proxima (notoriously later merging with InFocus), Cypress Semiconductor and its AgigA Tech subsidiary (both now part of Infineon), and Maxim Integrated Products (now part of Analog Devices), and works as a consultant today. I'm not done with Blasto. While I still enjoy playing the TI-99/4A port, there are ... improvements to be made, particularly the fact it's single fire, and it was never ported to anything else. I have ideas, I've been working on it off and on for a year or so and all the main gameplay code is written, so I just have to finish the graphics and music. You'll get to play it. And the arcade board? Well, we have a working game and a working harness that I can build off. I need a better sound amplifier, the "boom" circuit deserves a proper subwoofer, and I should fake up a little circuit using the power-good line from the ATX power supply to substitute for the power interrupt board. Most of all, though, we really need to get it a proper display and cabinet. That's naturally going to need a budget rather larger than my typical projects and I'm already saving up for it. Suggestions for a nice upright cab with display, buttons and joysticks that I can rewire — and afford! — are solicited. On both those counts, to be continued.
Hard data is hard to find, but roughly 100 million books were published prior to the 21st century. Of those, a significant portion were never available in a digital format and haven’t yet been digitized, which means their content is effectively inaccessible to most people today. To bring that content into the digital world, Redditor […] The post This machine automatically scans books from cover to cover appeared first on Arduino Blog.
Are you an educator looking to make coding easier and faster to teach? Join Andrea Richetta, Principal Product Evangelist at Arduino, and Roxana Escobedo, EDU Product Marketing Specialist, for a special Arduino Cloud Café live webinar on July 7th at 5PM CET. You will discover how the new AI Assistant in Arduino Cloud can help […] The post Arduino Cloud Café: Teach real coding concepts with Arduino AI Assistant appeared first on Arduino Blog.