• Rapid PCB prototyping - update (part 1)

    Dawid Buchwald4 days ago 3 comments

    What is your excuse?

    This header title of this section was supposed to be separate post on its own. One of the first things that surprised me when I started working on DB6502 version 1 design was the general hesitation for SMT soldering among beginner EE community. I did feel a bit intimidated by it as well, but I couldn't find UART->USB converter chip in DIP package, so I settled for not-so-beginner-friendly FT230XS in TSSOP-16 package. Got myself couple of chips, several adapters and while waiting for the delivery started watching YouTube videos about the process.

    OK, it didn't seem trivial, but didn't seem that hard either. Then I came across this excellent post (which I, obviously, lost) where somebody proposed slightly different procedure. Since it seemed the most reasonable to me, I decided to give it a try.

    Then the chips arrived. Much smaller than I expected :) Not to worry, I got to work. My first attempt at TSSOP-16 was horrible, I almost burned the chip, adapter and my apartment.

    That being said, it actually turned out OK. After about an hour of experimenting with flux, wick, solder and different soldering tips it looked fine and worked as expected.

    That's not hard, I thought to myself, and started with a second chip. Took me only five minutes. Later got myself SMT practice board (they are cheap and contain various footprints) and spent some time with it as well.

    Recently, when playing around with the idea for DB6502 I wanted to try out some specific chips that, sadly, are not available in DIP package. I figured it would make nice post for my blog, where I would show the process. To get better light I moved to terrace on a sunny, exceptionally warm day. Long story short - I was sweating like a pig, cursing like a seaman, but hell-bent on making it happen. Took plenty of photos, soldered several chips. Moved inside to check out the pictures just to find out that most of them were utter shit. It was so bright I didn't notice they were out of focus... Here are some before and after:

    Oh, and out of the above, only one pin was not connected correctly. There were no bridges. All in under an hour.

    What's with the excuses then?

    Well, actually two things: for one, my crappy pictures aside, there is no excuse really to give up on SMT soldering. You should try it, and I will demonstrate the process I use below. Now, I understand that some people can't do it due to physical conditions, sometimes caused by age or other diseases, so if you suffer from them, don't take it personally. I'm addressing here the healthy majority, especially younger engineers - guys, man up, try this. It might turn out easier than you think.

    But I do have second takeaway from the story: as usual, I want to emphasise the importance of sharing with others. Working on a project? Share your stories. People will notice and they will give back. Recently I was contacted by another Hackaday user and he informed me that there are actually UART->USB adapter chips in DIP package. It's MCP2221A, and I will write more about it soon. Bottom line: what's your excuse to not share your work?

    Back to business, what about these PCB prototypes?

    Again, this post is not sponsored by PCBWay, but I just have to recommend their service. The whole process (order validation, manufacturing, shipping and delivery) took under a week. This is really fast, and it ties perfectly in the "rapid prototyping" flow.

    What about quality? Glad you asked:

    Lovely :)

    First things first: FT230XS soldering

    So, what's the secret procedure I use?

    First, soldering station: doesn't have to be something fancy. I use Zaoxhin 936DH with 1C Black tip. 340 degrees Celsius. Flux pen. Thinnest solder you can find - I use 0,56mm. That's it, really.

    Now about the process up close:

    This is clean PCB, nothing happened yet. Sorry for pictures quality, but these are really small, and pictures have been taken with my...

    Read more »

  • Rapid PCB prototyping and what can possibly go wrong?

    Dawid Buchwald09/18/2020 at 18:32 0 comments

    Breadboards - the worst of best inventions ever...

    When I first started contemplating getting into field of hobbyist electronics, it was the discovery of breadboards that sparked my interest. I understood that they are the magical devices that are perfect for all hackers and tinkerers alike. Devices from alien galaxy that allow you to build complex circuits that are pretty robust and yet completely modifiable over time. I loved the idea, but then I came across some book about beginner Arduino projects and couldn't understand pull-up resistor concept. It just didn't make a lot of sense, so I got into ceramics instead and spent couple of years on it.

    Then one day I stumbled across first Ben's video about 6502 and I decided to give electronics another try. Maybe it was because he made me understand what the bloody pull-up is, or maybe it was simple childhood nostalgia about my Atari 800XE with tape drive.

    One way or another, I started playing with breadboards. I really considered them the best invention ever! And they probably are, until, that is, you purchase Ben Eater's 6502 kit and try to wire CPU, ROM and RAM and Arduino Mega debugger on a single breadboard with AWG22 wire. And then fish out the ROM chip to program it and put back in while keeping all the connections intact.

    This was my trauma with breadboards, and I believe we all have our own horror stories. All these loose wires, ZIF sockets slipping out of place, shorts and forgotten connections between power rails.

    The thing is that there is no realistic alternative to breadboard, right? I knew I want to go full-on PCB one day and I spent literally months to design my very first board. It turned out nice, and it worked just fine to my honest surprise. OK, I might have made it faster, some of the time I spent perfecting it was because of COVID lockdown of my Chinese PCB manufacturer - lead times were like three or four weeks, so I figured I would rather spend this time checking my design over and over, improving it to perfection. Found couple of mistakes (mostly cosmetic, though), fixed all of them and one day the factory reopened.

    Still, it was very long process. The main take-away from the situation was that you never ever put on PCB something that hasn't been tested on the breadboard. And PCBs take forever to make.

    With that universal wisdom in mind, after couple of months I started toying with the idea of DB6502 v2.

    Enter nightmares...

    OK, so I had some basic stuff figured out. I knew I want AVR (specifically ATmega644PA) to act as 6502 debugger and ROM programmer. I knew I want to use ATF22V10 PLD for address decoding, two RAM chips and one ROM. I knew I wanted to use SC26C92 for dual channel UART interface. I wanted selectable clock frequency (great idea if you want to start with slow clock, copy slow ROM contents into fast RAM, disable ROM and increase clock frequency to much higher), and many, many other features. Problem is that my "universal wisdom" was to test it all on breadboard.

    I knew that was the right way to go, but man... Doing all that again?

    I did build the SC26C92 DUART interface on breadboard and connected it to version one of my 6502 PC. It worked, kind of, and I know what that means - there is some very nasty bug in my design, and I still need to find it.

    I did build the EEPROM programmer based on ATmega on breadboard, and it worked just fine. It was actually faster than the minipro software with TL866 II+.

    But still, to add CPU and RAM and all these other components? No, seriously, I just couldn't force myself to wire the 6502 address/data busses to additional AVR and extra RAM chip. I was looking at the picture, and I was like "nope". I just can't go down that rabbit hole once again...

    Alternate idea

    I recently played a bit with protoboards, or perfboards or whatever they are called. I made a simple POV toy for my kid, based on the awesome AVR programming book:

    This was fun to make! I was thinking...

    Read more »

  • Field testing PLD and how to test your test

    Dawid Buchwald09/18/2020 at 17:21 0 comments

    Putting it all together

    After having completed implementation of my PLD-based address decoder, I finally decided to try and program it. Obviously I expected issues, because these were supposed to be programmed by dedicated hardware. I read about many, many issues with chips available on the market, so I came prepared. Got myself decent cup of coffee, locked myself and told my family to not disturb me for another three to four hours.

    Grabbed my JED file, inserted ATF22V10 chip into my TL866 II+ and tried to guess proper minipro syntax:

    dawid.buchwald@PL-2LGX4M2 ~/Documents/Personal/Development/6502/WinCUPL
    $ ls -l
    total 48
    -rwx------+ 1 dawid.buchwald Domain Users 2777 Sep 10 15:40 DB6502.abs
    -rwx------+ 1 dawid.buchwald Domain Users 4196 Sep 10 15:40 DB6502.doc
    -rwx------+ 1 dawid.buchwald Domain Users 4261 Sep 10 15:40 DB6502.jed
    -rwx------+ 1 dawid.buchwald Domain Users 3605 Sep 10 15:40 DB6502.pdf
    -rwx------+ 1 dawid.buchwald Domain Users 2903 Sep 10 15:40 DB6502.PLD
    -rwx------+ 1 dawid.buchwald Domain Users 1732 Sep 10 15:40 DB6502.si
    -rwx------+ 1 dawid.buchwald Domain Users 1011 Sep 10 15:40 DB6502.sim
    -rwx------+ 1 dawid.buchwald Domain Users 5287 Sep 10 15:40 DB6502.so
    -rwx------+ 1 dawid.buchwald Domain Users 2343 Sep 10 15:40 DB6502.wo
    
    dawid.buchwald@PL-2LGX4M2 ~/Documents/Personal/Development/6502/WinCUPL
    $ minipro -p 'ATF22V10C' -w DB6502.jed
    Found TL866II+ 04.2.112 (0x270)
    Warning: Firmware is out of date.
      Expected  04.2.118 (0x276)
      Found     04.2.112 (0x270)
    
    VPP=12V
    Declared fuse checksum: 0x67A5 Calculated: 0x67A5 ... OK
    Declared file checksum: 0x910D Calculated: 0x910D ... OK
    JED file parsed OK
    
    Use -P to skip write protect
    
    Erasing... 0.33Sec OK
    Writing jedec file...  5.01Sec  OK
    Reading device...  0.41Sec  OK
    Writing lock bit... 0.35Sec OK
    Verification OK
    
    dawid.buchwald@PL-2LGX4M2 ~/Documents/Personal/Development/6502/WinCUPL
    $
    

    Aaaaand that was it. Can't say I was disappointed to see it work the first time, but maybe I was? You know, just a little. I guess no troubleshooting for me now. Or so I thought.

    Next step, obviously, was field testing the thing. The way I wanted to do this was to write simple Arduino program that would check all the possible combinations (64KB addresses accessed in 8 modes - CLK high/low, RW high/low, EXRAM high/low) and compare them against expected output.

    I already implemented something similar in the past, but this time it was a bit more complicated. More vectors to test and a bit more outputs to verify.

    The code I came up was along these lines:

    #define IN_CLK         0
    #define IN_RWB         1
    #define IN_EXRAM       2
    
    #define OUT_WEB        0
    #define OUT_OEB        1
    #define OUT_RAM1_CSB   2
    #define OUT_RAM2_CSB   3
    #define OUT_ROM_CSB    4
    #define OUT_IO_CS      5
    
    #define MODES_COUNT    3
    #define ADDR_COUNT     16
    #define OUTPUT_COUNT   6
    #define MAX_FAIL       1000
    
    const char MODES[]   = {53, 51, 49};
    const char ADDR[]    = {52, 50, 48, 46, 44, 42, 40, 38, 36, 34, 32, 30, 28, 26, 24, 22};
    const char OUTPUTS[] = {47, 45, 43, 41, 39, 37};
    
    uint16_t BIT_MASK[ADDR_COUNT];
    
    bool ADDR_VAL[ADDR_COUNT];
    
    uint16_t currentAddress;
    uint8_t  currentMode;
    uint8_t  currentOutput;
    
    uint32_t passCount;
    uint32_t failCount;
    
    void setup() {
      setupPinModes();
      Serial.begin(57600);
      runTest();
      displayTestResult();
    }
    
    void setupPinModes() {
      uint8_t i;
      
      for (i=0; i<ADDR_COUNT; i++) {
        pinMode(ADDR[i], OUTPUT);
        digitalWrite(ADDR[i], LOW);
        ADDR_VAL[i]=false;
        BIT_MASK[i]=1 << i;
      }
    
      for (i=0; i<MODES_COUNT; i++) {
        pinMode(MODES[i], OUTPUT);
        digitalWrite(MODES[i], LOW);
      }
    
      for (i=0; i<OUTPUT_COUNT; i++) {
        pinMode(OUTPUTS[i], INPUT);
      }
    }
    
    void loop() {
    }
    

    This is basic init - setting Arduino pins to input/output, populating basic values (like bitmask)  and so on. Nothing fancy here.

    This is also pretty basic:

    void displayTestResult() {
      char output[128];
      Serial.println("Test results:");
      sprintf(output, " Scenarios passed: %lu", passCount);
      Serial.println(output);
      sprintf(output, " Scenarios failed: %lu", failCount);
      Serial.println(output);
     Serial.println(...
    Read more »

  • Address decoding... and how to get it right

    Dawid Buchwald09/10/2020 at 18:13 0 comments

    But it just works...

    What I find really amazing is how small, seemingly insignificant experiences can have enormous impact on us, how they can shape our path. For me one of those things was rather not-that-important article written by one of my professors, when I was still at the university. It was great piece called "The essay about Fibonacci Sequence", and the idea was pretty simple: to start with the simplest working algorithm to calculate n-th Fibonacci number, and improve the performance step by step.

    Some of the tricks were trivial, others less so, and in general, there was nothing groundbreaking about the method. As far as I recall, the optimal solution was to calculate n-th power of some matrix, but that doesn't matter really. What does, however, was that the person who wrote the article didn't focus simply on the result, but deliberately made all the silly mistakes along the way to illustrate how they compound to impressive performance drop.

    Following that path taught me one of the first important lessons in IT: it doesn't matter if it works. Everybody (or almost everybody) can make it work; the thing is to make it right, or at least try your best to do so.

    Address decoding for 6502

    One of the things I absolutely love about 6502 is how simple this CPU is: just a handful of registers, basic system bus and memory-mapped I/O. Everything is just an address, be that RAM, ROM or serial interface. This makes programming model very simple and easy to read. I would even argue that this architecture is best suited to teaching basics of programming. And yes, I do know how it sounds - using assembler to learn programming - but try to think about it for a moment. There are just several variables, no data types, no abstractions, memory management and such. Simple instructions, data fetched from uniform cells and saved the same way.

    Unfortunately, there is a downside to this simplicity. Most basic 6502 builds (like Ben Eater's BE6502 or my own DB6502 v1) use simple logic gates to map memory addresses to specific hardware devices (reads/writes at address 0x0100 go to RAM, while 0xFFFC usually to ROM). This model is usually sufficient for most of the applications, but there is some inefficiency there. Vanilla BE6502 wastes 16KB (25%) of available memory just to access single VIA chip. My own build wastes 8KB (12.5%) to provide access to several I/O devices (up to 10), so it's a bit better, but honestly - all you need is 16 bytes per device, so you should not waste more than 256 bytes (one page) for the whole I/O region.

    By the way, if the above is not perfectly clear, please let me know in the comments, I can explain it more, but truth be told - Ben's explanation in his video should be sufficient.

    Can it be done better?

    Sure thing. Anyone looking for better solution will sooner or later stumble across the invaluable 6502 primer. There is whole section about address decoding there. I strongly suggest you take a look, there are some nice ideas and important details to keep in mind (mostly - importance of propagation delay).

    You usually end up with two possible options: use 74xx logic gates (and quite a lot of them) to narrow down I/O memory range, or use PLD. The former is perfectly sufficient for plenty of applications (remember - many of the original computers from the 80s didn't even use the full 64KB range and they were powerful enough to run plenty of software), while the latter, while providing the best results (high performance and low memory range waste), might be intimidating for beginner user. There are some strange tools to be used with weird syntax and ancient interface. There must be a way to navigate those, right?

    How not to share knowledge

    I really admire all the people who spent countless hours trying to figure out all the weird stuff about 8-bit computers. They did amazing job, and the fact that they found the time to document all this is awesome. That being said, there are some things I...

    Read more »

  • Retrospective - interfacing to FT230XS

    Dawid Buchwald08/31/2020 at 16:24 0 comments

    Serial connection over USB for retro computer? Why not!

    I was asked by someone in /r/beneater community to describe how to build USB -> UART interface using FT230XS chip, as this exercise might seem a bit intimidating for someone doing it for a first time. It was first time for me, and it did seem way more difficult than it really was. I'm happy to share what I have learned about how to do it (and what not to do). Also, I think it makes sense to explain even the more basic blocks of serial communication to make understanding of the whole flow easier.

    Understanding what the USB serial connection means for your computer

    First things first - what do you need the serial connection for, and how it can be used. 

    Well, obviously you can use this for simple shell connection. There is number of programs, including picocom for Unix/Linux/MacOS and PuTTy for Windows, that simply allow you to connect to the device and "talk" to it as a regular terminal app.

    Please note, however, that you are not limited to such use! There are other use cases to consider, which can significantly improve the functionality of your homebrew machine:

    1. File transfer protocol
    2. Custom binary protocol

    FIrst one is fairly standard: there is number of protocols invented back in a day that allow for file transfer using serial line. XMODEM is the one that is the simplest to implement and suitable for simple, single file transfers. I used this one for loading software (compiled on a PC) to my homebrew 6502 computer and it was pretty easy to use. Please note: for Windows machines you need to use specific fork of PuTTy called ExtraPuTTy.

    Second use case is less obvious, but with serial protocol you can do whatever you please. For instance you can turn your PC into graphics display - have simple Python program read custom graphics data protocol from 6502 and display it on PC screen. You can even use this to route HTTP over TCP/IP traffic (after some simplification) to homebrew machine and react to REST calls over WiFi. That's cool, right?

    Basically - having serial protocol support enables you to do all sorts of things!

    Some basic terms for start

    When I first started investigating options to connect my Ben Eater's 6502 computer to my PC it was literally overwhelming. There are so many standards, terms, schematics that it seemed like something very, very difficult. As with most things IT related it turns out that the complexity is rooted in history, and to navigate it you need to understand how things came to be. I don't want to spend too much time on it, but let's take a look.

    RS-232 serial protocol

    This single concept is responsible for most of the confusion out there. RS-232 was a very popular communication standard for serial data transmission, and it was designed for standard communication between PC and external devices. These devices might have been computer mice, phone line modems, printers, etc. Unfortunately, due to hardware limitations (connection quality) it required pretty high voltages to send reliable signal. Also, since it was used to communicate over strange media (phone modems where you had to pay for the time spent in connection), it required complex circuitry to indicate all the conditions. 

    The important part to know about RS-232 is that it's serial communication protocol from the old days, and you might need it if you need to talk to very old hardware that supports it. If you don't (and you intend to use serial over USB for instance), then most of the old stuff is irrelevant and some signal lines can be ignored (grounded or left unconnected). Examples of such lines are RI (ring indicator used to inform CPU that phone to be used for modem transmission is ringing) or DCD (data carrier detect). Sometimes you will find them in modern serial adapters, but don't worry, you don't have to use them.

    If you do need to use RS-232, you can use MAX232 chip that will take care of translation...

    Read more »

  • High speed AVR based EEPROM programmer (part 1)

    Dawid Buchwald08/28/2020 at 20:43 0 comments

    How to lose your mind in three really simple steps

    We have all been there, more than we care to remember. 

    You are in the middle of creative frenzy, writing your code as the Devil himself. Compile, link, grab the programmer. Fish out that ROM chip carefully from breadboard, place in the socket. Take another look, notice the notch wrong side. Take a deep breath, rotate the chip, burn. Plug it back in, nervously connect power.

    Trembling with anticipation you notice that nothing happens. Check power. Check reset switch, no it didn't fall out this time. Try one more time. Nope. Sigh, spend couple of minutes attaching Arduino debugger, step over first couple of instructions, but it all seems good for a while and fails only after several hundred cycles. Repeat - maybe it was freak occurrence.

    After another half an hour you finally start checking wires and there it is - one of the suckers got loose. No, not so much to slide out of the breadboard, that would be just too convenient, just enough to make bad connection. Push it in, restart, it works, it works, it works, no it doesn't. Code bug again.

    Where was I again? You no longer remember what you were doing, got distracted too much with silly wire not making good enough connection. Coming back to the point where you left off will take some time and impact your motivation.

    How to stay sane for a bit longer

    If you search /r/beneater for ZIF, you will find small sample of what people tried to do. Hell, even I did that for a while - as it turns out, you can make it fit in breadboard with simple hack (add header female pin header to the socket) so it doesn't loose connection each time you play with it.

    For me the solution was to move to PCB, and put my EEPROM in secondary socket like so:

    While this works surprisingly well (tooled sockets are perfectly matching each other, and are much more sturdy than the regular IC pins), it still frustrated me how often I have to pull the ROM out and flash it in my programmer. It was really annoying, so the next part of the code I wrote was bootloader functionality that was supposed to reduce the need for ROM flashing, and it worked for some time.

    Unfortunately, with the limited resources of 64K address space I had to store at least some code in ROM and my OS was depending on it. Shortly after I added the option of static linking these routines in loadable modules so I could change them and experiment without EEPROM flashing. Again, it worked for a while, but when I started experimenting with higher clock frequencies and had to troubleshoot resulting issues it just wasn't enough.

    At least I didn't have to worry about loose wires :)

    Does it have to be like this

    Sorry to digress so much, but part of the experience for me is to see how my hobby ties into my day job in IT consulting company. One of the things that never ceases to amaze me is that people in my area of expertise are so clever when it comes to coming up with new ideas, and still they do so many things in such awkward way as if they have never known any better. Manual build processes. Awkward release management. Cumbersome testing procedures. Lack of any kind of DevOps culture. Nonexistent source code management procedures. We've all been there.

    So, general word of advice would be: if you feel that your team is doing something in quite strange way and you suspect it could be done better - investigate. Challenge. Question. It's quite probable that this one thing can (and should) be done differently. Somebody out there might have came up with much better idea, and chances are you are the last ones to catch up :)

    So, going back to the original issue: ROM programming. Does it have to be like that? Sure it doesn't, there is bloody Arduino sitting in your drawer, how many times did you have to fish out the effing EEPROM out of this thing to program it?!

    Now that we have established that, let's consider our options here.

    ICSP options for 6502-based...

    Read more »

  • Brainstorming DB6502 version 2

    Dawid Buchwald08/28/2020 at 19:45 0 comments

    General design guidelines (part 1)

    I was a bit silent recently - went on vacation to Greece with my family and had to catch up with work after returning home. I hope to write a bit more often here now that I'm back to pandemic-normal :)

    It’s time to write a bit more about the project being documented here – mostly about the purpose, goals and rationale behind them.

    DB6502 was never intended to be commercial product, and definitely not a mass-market thing. I designed the first version as a stop-gap solution for BE6502 evolution. The idea was to get off the breadboards, design robust and flexible development toolchain to experiment with 6502 CPU and basic operating system design. To be perfectly honest, I have reached these goals already, and I could really stop here, but the journey has been fun all along, and I just want more. What could I add to my first design then, besides fixing some small issues in the original one?

    Self-contained, hackable 6502 development ecosystem

    The problem with BE6502 and derivatives (including DB6502) is that it depends heavily on external hardware. You need Arduino Mega for very limited embedded debugger. You need TL866 II+ for ROM burning and we all hate chip flashing. You have to add serial connection on your own, and we all do it differently, using different chips.

    I would like to combine all of these in one board: have 6502 run in its own environment with RAM and ROM with two VIA chips for general port based I/O and dual channel UART for high-speed external interface (serial terminal and file transfer separately if needed).

    On top of that I want AVR chip connected to the system bus permanently that will act as debugger/monitor and fast ROM uploader. This design should enable very efficient software development without the hassle of OS recompilation and ROM flashing each time you need to use debugger.

    AVR with its own serial channel will be able to stop, reset, single step CPU and run it at pretty high speed (up to 300KHz) in debug mode with limited stack trace and breakpoint functionality.

    This combined with current state of my toy operating system should provide environment to experiment with all things 6502 is capable of with pretty much single board. It will also eliminate need for ROM removal/flashing using external programmer.

    Expandable bus

    Based on the experiences with first version of DB6502, I consider expansion bus very important part of any successful build – it allowed for easy testing of new ideas without necessity to revert to breadboards. It also helps with hardware troubleshooting and analysis.

    One of the mistakes in first revision was to disconnect all the “mystery” pins of 6502 (like MLB, RDY, BE or SYNC), since I have never used them in my simple breadboard experiments. Unfortunately, these turned out to be critical for proper debugging/monitoring by external tool, so in future revision these will be exposed via the system bus even if I can’t see any use for them now (MLB being perfect example here).

    With proper expansion bus it should be possible to experiment with really advanced topics like video signal generation, DMA interfaces or multi-CPU designs, and the idea is to ensure that hardware design doesn’t limit my options here too much.

    Another thing I considered was fully modular bus design where specific components could be made on separate PCBs and replaced without the need to redesign whole board. Currently, however, I don’t plan to implement anything like this.

    Better address decoder

    My first invention ever for BE6502 was introduction of improved address decoder to enable better usage of available memory – instead of 16K RAM and 32K ROM in BE6502, DB6502 has 32K RAM and 24K ROM. It seemed to be good enough for anything I might ever do with it, but I actually managed to run out of ROM space and hit a few roadblocks with the RAM as well. Sure, most of these issues could be solved with clever optimisations of...

    Read more »

  • Amazing upside to sloppy coding (HD44780 part 2)

    Dawid Buchwald08/06/2020 at 13:28 0 comments

    What do I mean by "sloppy coding"

    Well, each one of us has own definition of that term, right? Having had over 15 years of professional experience in enterprise applications maintenance and development I have a very specific definition of "sloppy": for me any code obfuscated for any kind of performance is "sloppy". Code should be easy to read, easy to test, easy to maintain years after it has been written. No magical side effects, no assumptions about call order, reentrancy, interrupt handling, database result ordering and so on.

    However, for this particular post, I want to use a different definition. The one that I'm referring to right now is the "Superstar Hacker" mindset of the 80's: code that is not super efficient, super small, super optimised - it's just "sloppy".

    Now, I don't agree with that approach, but I do accept the term for the purpose of this post. Hopefully it will become clear soon :)

    Waiting for your turn

    When you start with Ben's video with slow clock it's all pretty nice and simple: you write characters to VIA output port and they magically show up on the screen. Everything works, no problem whatsoever. This is like speed 0 - several Hz at best. Your code doesn't have to worry about latency of the controller, internal memory operations and so on.

    However, when you move to higher clock speed (like 1MHz), things become complicated - you need to start checking busy flag of the LCD controller to ensure that you don't issue new command while the last one is still running.

    In Ben't videos it's also very well explained, how the busy flag checking should be done - basically, before each write operation you should first read current status to ensure that previous write has completed. Easy? Sure!

    print_char:
      jsr lcd_wait
      sta PORTB
      lda #RS         ; Set RS; Clear RW/E bits
      sta PORTA
      lda #(RS | E)   ; Set E bit to send instruction
      sta PORTA
      lda #RS         ; Clear E bits
      sta PORTA
      rts

    What happens is that each time you want to write character, you loop until previous operation is completed, indicated by 0 on the most significant bit of the status value:

    lcd_wait:
      pha
      lda #%00000000  ; Port B is input
      sta DDRB
    lcdbusy:
      lda #RW
      sta PORTA
      lda #(RW | E)
      sta PORTA
      lda PORTB
      and #%10000000
      bne lcdbusy

    Now, I was ambitious. I wanted to use larger LCD: 20x4 characters. Soon I realised the crazy addressing scheme of this weird device. This is how the addressing is organised:

    0x000x010x020x03...0x13
    0x400x410x420x43...0x53
    0x140x150x160x17...0x28
    0x540x550x560x57...0x68

    When you write consecutive characters, they will fill the first row, then wrap to third row, and when this one is full, move to second row followed by fourth.

    I don't know how anybody came up with this idea, this must have been the easiest to do while maintaining backwards compatibility. Anyway, you have to handle it somehow. I noticed something nice: when reading for busy flag you get additional information - bits 0 thru 6 contain new cursor position (address in memory), after last read/write operation.

    What I came up with was, in my opinion, pretty brilliant: instead of reading busy flag before each write, I read it afterwards. 

    This seemed as perfect solution. While keeping the same number of read operations, I would get two things at once: I would know when the last write operation was completed, and I would get new cursor position after each character written. The algorithm was pretty simple: write single character, keep polling for busy flag, and when done, take the retrieved cursor position, compare against each of the possible line wrap points and when encountered, move cursor to proper line.

    That way, when I wrote to address 0x13 (last address of first line), I would get back address 0x14 (first character of the third column). This means that I have to move to address 0x40 (beginning of second line). Lovely in its simplicity!

    I was so proud of myself - after all, I had...

    Read more »

  • Adventures with HD44780 LCD controller

    Dawid Buchwald08/05/2020 at 16:34 7 comments

    That can't be hard, can it?

    If you watched Ben Eater's video about interfacing with HD44780 LCD controller, you might be thinking - why bother writing about it? It's dead simple.

    Well, as usual, yes and no. When you finally figure it out, it's actually pretty obvious, but it takes a while to get there. And please note: I don't mean to criticise Ben's work - his videos are awesome, and I would have never started my project without them. Having said that, he did omit certain details that might or might not cause issues with your build. Let's review them one by one.

    Initialisation sequence

    There are two major modes of operation for this controller: 8-bit and 4-bit. Ben, in his videos, chose the former, and for a good reason: it's much easier to understand, programming model is less complicated and, last but not least, it's the default operation mode.

    When you check out his code, this is what you will notice:

      lda #%00111000 ; Set 8-bit mode; 2-line display; 5x8 font
      sta PORTB
      lda #0         ; Clear RS/RW/E bits
      sta PORTA
      lda #E         ; Set E bit to send instruction
      sta PORTA
      lda #0         ; Clear RS/RW/E bits
      sta PORTA

    This is just beginning of the initialisation sequence, and it selects one of the modes of operation - the 8-bit interface. I chose to use 4-bit mode in my build (which, by the way, was pretty dumb on my part, but more on that later, in next "Lessons learned" entry), and I thought all I need is to change the code so that it initialises LCD with 4-bit mode, 2-line display and 5x8 font. Seems logical, right?

    Now, if you check schematic, it even provides example of how this needs to be done. See Table 12 on page 42. Looks simple:

    And the funny thing is - it does work... sometimes. It works after internal reset, and this is where the catch is. Internal reset is special "gizmo" for a lack of better word, that will set up the internals of the LCD controller after powering it up. It has specific electrical requirements (page 50):

    So, for your LCD to operate correctly, you need to ensure that it reaches required power supply voltage in between 0.1ms and 10ms. In most cases this requirement will be met and LCD will initialise properly upon power-up. When this initialisation is completed, sequence described in Table 12 (page 42) will correctly toggle operation to 4-bit mode and everything will work just fine.

    Problem is that you have to reset your CPU sometimes. When you do, strange things start happening: text on LCD appears corrupted or disappears altogether. The only way to fix this is to power it all off, wait a second, power it back up. What's the problem?

    Well, once you figure it out, it's pretty obvious:

    1. When you power-up your computer first, internal reset sets up LCD to work in 8-bit mode,
    2. During initialisation routine your CPU sends command to toggle to 4-bit mode - and this is what LCD expects: single strobe of four bits 0b0010, so this works,
    3. Everything keeps working nicely until you reset the CPU,
    4. After reset, your CPU starts invoking initialisation sequence again, sending single 4-bit mode enable command (just 4 bits 0b0010),
    5. Problem is that LCD is already in 4-bit mode, and it doesn't understand this single 4-bit command anymore. It expects to receive two nibbles 4-bit each this time.

    It all goes south from there - everything you send to LCD is corrupted.

    How to solve this issue?

    Well, this is also pretty well explained in the datasheet, but you need to read it carefully to make sure you implement it correctly (Figure 24, page 46):

    This is backup procedure that will take care of proper LCD initialisation when power-up reset conditions are not met. Why not use it every time just to be safe then?

    The weird thing about this process is that you have to send three consecutive single 4-bit instructions (as in: single 4-bit strobe, instead of two 4-bit strobes, as usual) to set the interface to 8-bit mode. It's not a typo! To enable 4-bit mode you have to say three times "I want 8-bit...

    Read more »

  • Lessons learned (part 1)

    Dawid Buchwald08/03/2020 at 17:11 3 comments

    Part one of many

    There is no way I could fit all the things I learned, even if just using bullet points, in single blog entry. I will probably share these as I go, mixing them with general descriptions and new experiments. Starting with one that is most obvious: always have some reference point, working revision that you can revert to.

    Version control

    Use it for anything and everything. Keep your sources, even if they are temporary, one-off experiments. Save your schematics, even if you design them for temporary ad-hoc device. Use branches to separate developments that might or might not go into final design – it will be easier to merge them with release branch than to remove them from it.

    This was obvious, but what else? Well, in general it’s not recommended to store binary results of any build, it should be possible to generate them easily from sources (schematics, configuration, etc.) stored in repository. That being said, I was asked number of times to provide PDF of the schematic or HEX file with firmware by people who didn’t want to install the whole software stack just to take a look at how I did certain things.

    What I also decided to keep was the datasheets of used chips – reason being: you can find different versions of datasheet when looking for it online and sometimes they differ significantly. Git handles large binary files that don’t change often very well, so don’t worry about repo size.

    If you intend to go to PCB at some point, make sure your design your circuit first, build in on breadboard second, test third and move to PCB layout at the end. Things that are not documented properly will be missed when designing PCB. And no, you will not remember that one little obvious detail next week. You will forget it, trust me :)

    Fail often and fail early – but understand what happened

    One of the most difficult parts at the beginning of the project was writing LCD interface. If you watched Ben Eater’s videos about it, you might be wondering what was so hard, he made it look easy.

    Problem is that Ben uses the most “default” version of the interface, where I decided to go for more complex one (4 bit instead of 8 bit; 4x20 LCD instead of 2x20).

    I built my LCD interface on breadboard and it worked. Sort of. Sometimes it did, sometimes it didn’t. I blamed instability of breadboards and moved on. I was lucky, it was only my software that was busted, and after migrating to PCB, when issue occurred again, I had to look closer at the code and fixed it. Then, couple of months later fixed it again :)

    Similar thing happened when I started playing with new serial interface. It all seemed to work pretty well, with some exceptions. I blamed ribbon cables, was about to move on, but this time stopped and started looking at it again. And again. And again. What I almost attributed to ribbon cables was serious hardware design flaw that, when moved to PCB, would render it useless. There was no way of fixing that in code this time.

    So, when it fails, even if rarely – don’t just rewire or shrug and move on. Try and understand what happened. It might have been freak accident, but most likely there is something you didn’t consider.

    Use the right tool for the job

    So you want to build 6502 based computer? Or any other embedded hardware? It’s pretty obvious how to do it: prototype the whole thing on breadboard, write operating system for it, upload, test and then design PCB reflecting your breadboard connections. Easy-peasy :)  

    Except it’s not. Not even close. I have tried and learned a lot from it, but the most important lesson is you don’t really want to do it. Do small things in isolation, and always, always, always use the right tool for the job.

    No, you don’t need expensive 500$ 100MHz 4-channel 1GS/s scope for it. You might need 13$ cheap Saleae Logic8 clone. Sure, proper scope might come in handy, and it will help here and there, but you can easily do without it. Since you...

    Read more »