Sunday, 28 September 2025

Revisiting the Minstrel 4th Enhanced Forth ROM

When I relaunched the Minstrel 4th earlier this year, I added some more ROMs to the standard ROM image. These included the ZX80 and ZX81 BASIC I have talked much about, and the Enhanced Forth ROM which I have not.

This is the work of Alexander Sharikhin, and features additional code by George Beckett (and I think the clock check and BEEP patch might have been by me? or maybe just "Based on an original idea by Tony Warren Dave Curran")

This takes advantage of the extra 5K of ROM space I gave the Minstrel 4th, and adds various new functionality to the Forth ROM, including:

  • Serial communications using the 68B50 RC2014 module
  • LOAD and SAVE via serial using server program at PC end
  • LOAD and SAVE blocks of memory using XMODEM
  • Serial => Keyboard input and Screen => Serial
  • Programming tools to support hex characters and code blocks
  • CASE statements
  • BEEP patched to adjust frequency when run at 6.5MHz

When I came to add that, even though the latest source was 5 years old, I appeared to have an older version, so I though it best to get the latest version and tried to build it.

That didn't go well. It needed the sjasmplus compiler with LUA support, and I couldn't get that working. Not sure what I was doing wrong. I guess something in current version of LUA or sjasmplus has changed to make it incompatible with the other.

At the time, I took the easy option of asking George to build it for me, and that was the version that went into the ROM.

I have been planning to revisit that for a while, I wanted to achieve a few things:

  • Be able to built it (quite important)
  • Add new functionality
  • Integrate the ACE ROM source
  • Rename some words

The current version is a patch. It takes the compiled Jupiter ACE ROM binary and patches a few things in there to graft in the new commands, which are compiled and added onto the end. 

It also patches the names of LOAD, SAVE, BLOAD and BSAVE to change those to LOAT, SAVT, BLOAT and BSAVT to allow the new routines to use the standard names (the change of letter so not to change the code size, hence why it wasn't changed to TLOAD for example)

I can see why that has been done, but I think I would prefer to leave the standard tape load as LOAD and make the serial load SLOAD (or ULOAD to match things like UINIT which initialises the UART).

That way, I think I would push this as the main ROM, with the option of going to the original unmodified version if you want. A normal user could just run LOAD as normal for a tape load without being aware of the new functionality.

It would be ideal if I could have all the source together in a single place and do one build command to build the whole ROM from source. That is non-trivial, since the ACE source I have is based on Geoff Wearmouth's commented disassembly, and that is written to use the TASM assembler.

That's fine by me as I have been using TASM to write Z80 code since I got a copy on 5¼" floppy from a teacher at school. All of the ZX80 and ZX81 BASIC etc. is written in TASM so it seemed the best bet to stick with that.

All I had to do was manually apply the patches to the Ace source and merge the source together.

Easy.

End of post, take the rest of the day off.

Ah, no. Not quite.

It turns out there are quite a few differences between TASM Z80 and sjasmplus. Many of them were just search and replace, but some required a bit more thought.

Here are some examples I found.

Numbers

The numbers were in several formats, 0x1234 and also #1234 (which I found rather annoyingly Visual Studio Code displayed with a box to the left showing what #1234 would look like if it were an HTML colour).

I am sure there is a way to turn that off, but I need to change them anyway since TASM used $1234 for hexadecimal numbers (and also 1234H, but I rarely use that notation).

RECV_RETRY: equ 0x1000  🠆  RECV_RETRY .EQU $1000

TMP_DICT = #2709  🠆  TMP_DICT .EQU $2709

That was mostly search and replace, but as with all of these changes, a good opportunity to read through the code and find out what it all did (and for me to unnecessarily align all the comments because that's the way I roll).

Local Labels

Local labels save you having to think of a new name for the point you jump back to in a loop every time.

You can't call them all "loop", so you can use a local label that's just called "loop" within this scope of your function. sjasmplus uses ".loop" for this, but TASM uses "_loop" (you can change that, but I'd rather stick with the standard). That's not too bad, just search and replace again.

But the scope is also different. In sjasmplus this seems to be relative to the last proper label only.

main:

    ld b, 42

.loop

    djnz .loop

That .loop is treated as main.loop, which makes sense and is a neat way to do it.

I use ACME for 6502 code, and that also has + and -, so you can do

main:

    lda, 42

-   dec

    bne -

Where - just refers to the previous - label, and + to the next + label (you can do -- and ++ as well for the one before / after that, but it starts to get a bit complicated and is usually better to give things names unless it's obvious)

TASM doesn't have either of those, it needed .MODULE definitions adding where necessary to control the scope.

main:

          .MODULE MAIN       

    ld b, 42

_loop

    djnz _loop

So that is now MAIN._loop.

Instruction differences

There were a few instructions that had to be changed.

ex hl, de

That was used in a couple of places. This isn't an "official" Z80 opcode, but the assembler just accepted it and converted it to the more correct

ex de, hl

TASM didn't, so I had to do that manually.

I wanted to check that to be sure, so I disassembled the Enhanced part of the ROM.

z80dasm -a -g 0x2800 -l -t 2800.bin

That gave me a listing of the code so I could check what had actually been built.

ex de,hl

Yes, the compiler had just replaced the instruction.

The next one confused me, as far as I know, there isn't an ld (hl), de instruction.

.save:

ld (hl), de

ret

Checking the disassembly, it turns out the code actually generated was:

ld (hl),e

inc hl

ld (hl),d

dec hl

ret

Interesting, not sure why you would use an instruction that doesn't exist, particularly if for example, you didn't need the value in hl, so the last dec hl would be unnecessary.

_save:

   ld (hl),e                ;ld (hl), de

inc hl                     ;

ld (hl),d                  ;

dec hl                     ;

ret

I left the original instruction in the comment for reference.

This one also confused me.

dup 4

call loadVar

jr c, _error

edup

I was thinking this was some kind of forth thing, since dup is a forth word to duplicate the item at the top of the stack

But no, it must be a compiler thing. Looking at the disassembly it appears to mean "duplicate this 4 times......end duplicate"

   call sub_2ac6h

jr c,l2ce0h

call sub_2ac6h

jr c,l2ce0h

call sub_2ac6h

jr c,l2ce0h

call sub_2ac6h

jr c,l2ce0h

I just replace that with the unrolled code, again with a comment

RST calls

There were two variations of RST calls, using 0xnn and #nn (both seen in this function)

.cont:

rst 0x08 ; Print character

pop bc

djnz .loop

ld a, CR

rst #08

ld a, e ; Retrieve block type

and a ; Indicate success

ret

Unfortunately it turns out TASM doesn't accept either of those. Furthermore, it doesn't accept what I would have expected

    rst $08

You have to do

    rst 08H

More search and replace and we are getting closer.

Just a side note, got to love Z80 syntax

    cpl  ; Compute 255-A, ones ComPLiment

    cp l ; Compare A to the L register

And

    rra  ; Rotate accumulator right through carry (4 cycles)

    rr a ; Rotate accumulator right through carry (8 cycles)

Oh, for a RISC processor ......

Forth Words

The elephant in the room.

The is probably described better somewhere else, by someone who understands it better that I do, but here goes.

The forth dictionary is a singly linked list. There are two bytes at the end of the original ROM at $1FFD. This points to the first word in the dictionary (after FORTH).

On the original ROM that is UFLOAT, but if you wanted to add a new word into the ROM, you would change the value at $1FFD to point to your new word, and you would set your word to point back to UFLOAT, inserting it into the chain.

(what, you didn't expect me to include a vlist print out? at least I might have surprised you by not using the inverse colour scheme I normally do)

There are lots of forth words added in the enhanced ROM, all of which are linked between FORTH and UFLOAT.

(paused part way through as they normally scroll off the top of the screen - do we need a new vlist word with a ZX Spectrum style SCROLL? function????)

(a benefit of the work I previously did getting the extra 5K supported in EightyOne means I can now run and debug the enhanced ROMs. Not perfect as the save detection has to be disabled to avoid false triggering with the ZX ROMs)

In the original ROM, forth words are defined as follows:

; ---------------

; THE 'BASE' WORD

; ---------------

; ( -- 15423)

; A one-byte variable containing the system number base.

; $3C3F BASE

L0483:  DEFM    "BAS"                   ; 'name field'

        DEFB    'E' + $80

        DEFW    L047F                   ; 'link field'

L0489:  DEFB    $04                     ; 'name length field'

L048A:  DEFW    L044D                   ; 'code field'

The actual code follows elsewhere, pointed to by the code field.

The name is "BASE", the last letter of which has $80 added to it to indicate the end of the string.

There is also a length field, which in some cases has $40 added to indicate an immediate word (for things like DO WHILE, IF THEN ELSE etc.)

The links are all hard coded, from word to word, in the order they appear in the file (not sure if that is true for all of them, but it seems to be the general case).

This was one of the nice features that the enhanced ROM needed sjasmplus assembler with LUA for.

w_uinit:

FORTH_WORD "UINIT"

call uart_init

jp (iy)

.word_end:

FORTH_WORD is a macro which expands out to the type of header above, creating the name, link and length fields automatically.

I couldn't use that, so I had to do it manually, in the same way as the original ROM (or at least the disassembly - maybe they had some equivalent function in whatever assembler was used for the Ace ROM).

w_uinit:

;FORTH_WORD "UINIT"

    .MODULE UINIT

    .BYTE "UINI"            ; 'name field'

    .BYTE 'T'+$80           ;

    .WORD w_uread_link      ; 'link field'

w_uinit_link:    

    .BYTE $05               ; 'name length field'

    .WORD w_uinit_code      ; 'code field'

w_uinit_code:    

call uart_init

jp (iy)

I initially hard coded all the links, setting the link address to the label for the previous word (in this case UREAD), and then setting a new link label for the next word to use.

Right, almost there.

CASE

The last thing to tackle was the code for the case statement construct. This was in a different format to the standard assembly code words. This one used FORTH words (or bits of them), in the same way as a few original words, such as ROT. This rotates the top three bytes of the stack:

; --------------

; THE 'ROT' WORD

; --------------

; (n1, n2, n3 -- n2, n3, n1)


L08F9:  DEFM    "RO"                    ; 'name field'

        DEFB    'T' + $80

        DEFW    L08ED                   ; 'link field'

L08FE:  DEFB    $03                     ; 'name length field'

L08FF:  DEFW    L0EC3                   ; 'code field' - docolon

; ---

L0901:  DEFW    L08D2                   ; >R

L0903:  DEFW    L0885                   ; swap

L0905:  DEFW    L08DF                   ; R>

L0907:  DEFW    L0885                   ; swap

L0909:  DEFW    L04B6                   ; exit

It does that using five forth words, rather than any assembly language.

It appears CASE and related words are the same, but the version in the source is just a list of bytes.

;; CASE

ABYTEC 0 "CASE"

dw LINK

SET_VAR LINK, $

db 0x44

db 0x08, 0x11

dw $ + 0x0f ; 0x93, 0x3C

db 0x11, 0x10, 0x00, 0x00, 0x11, 0x10, 0x09, 0x00

db 0x40, 0x11, 0x00, 0xE7, 0xFF

db 0x42, 0x11, 0x79, 0x08, 0xB6, 0x04

To get building, I just pasted in the bytes from the disassembly (code and header).

;; CASE

.BYTE $4F, $C6, $69, $29

.BYTE $42, $08, $11, $A1, $29, $11, $10, $09, $00

.BYTE $46, $29, $60, $04, $11, $10, $00, $00, $4E

.BYTE $0F, $11, $10, $07, $00, $40, $11, $02, $DF

.BYTE $FF, $42, $11, $D2, $08, $12, $09, $4A, $0C

.BYTE $DF, $08, $85, $08, $83, $12, $09, $00, $79

.BYTE $08, $79, $08, $71, $12, $0B, $00, $B3, $08

.BYTE $DF, $08, $D2, $0D, $D2, $08, $A4, $12, $B6

.BYTE $04

OK I think it's there, so time to build it.

wine TASM.EXE -80 -b -L minstrel4th.asm minstrel4th.rom

I am on linux, but still using the old DOS command line TASM, via wine.

I had a few typos to fix here and there, but finally got it built.

I use HxD hex editor (again because I have been using that since the year dot), and that brought up a few differences, usually cut and paste errors with links in the word definitions.

A few more tweaks and I finally got it to build binary identical.

Great.

End of post, take the rest of the day off.

Ah, no. Not quite.

The challenge now was to fix the links, and do something with the case code.

Automatic linked list

The FORTH_WORD macro used a variable called LINK, which was initialised to the address of UFLOAT (first word after FORTH in the original ROM). This was then updated each time to the current link.

Once I worked out the syntax for TASM, I was able to use the same sort of thing, so I went through and replaced all the hard coded links with this new automated link code. E.g.

w_hex:

    .TEXT "HE"              ; 'name field'

    .BYTE 'X'+$80           ;

    .WORD LINK            ; 'link field'

LINK .SET $ ;   

    .BYTE $03               ; 'name length field'

    .WORD w_hex_code        ; 'code field'

w_hex_code: 

ld (IX + $3F), $10

jp (iy)

That was neater and will also allow me to change things more easily.

I rebuilt it and it was still binary identical. Great.

Making the case

I now had various different sources for case the version in the enhanced ROM with a header and just bytes. The version from the disassembly that was just bytes.

I now had found a third, from Geroge's github

https://github.com/markgbeckett/jupiter_ace/tree/master/case

This was in the form of the forth words.

0 COMPILER CASE

  0 ( MARKER ON STACK, USED BY ENDCASE TO CHECK DONE )

  9 ( SYNTAX GUIDE, MUST MATCH WITH 'OF' )

RUNS>

  DROP ( NOTHING TO DO EXCEPT DROP ADDR OF PARAMETER FIELD )

;

With a bit of searching through the various versions, I was able to create something a bit more readable than the list of bytes (but still binary identical)

.TEXT "CAS" ; 'name field'

.BYTE 'E' + $80

.WORD LINK            ; 'link field'

LINK .SET $ ;

.BYTE $04 + $40 ; 'name length field' (immediate word)

.WORD L1108 ; 'code field' - compile

.WORD w_case    ;

.WORD L1011 ; stack next word

.WORD $0000 ; 0

.WORD L1011 ; stack next word

.WORD $0009 ; 9

.WORD L1140 ; (part of) RUNS>

.BYTE $00, $E7, $FF ; ???

w_case:

.WORD L1142 ; (part of) RUNS>

.WORD L0879     ; DROP

.WORD L04B6 ; exit

I think I understand most of that, although I am not sure about the code that makes up the "RUNS>" bit.

The important thing is that it still builds binary identical, and I do not think there are any absolute links, so the code could move around and still build and work.

What next?

I have a few things to add in terms of functionality and also hardware support. If you think of anything else that would be useful, there is still about 2.5KB remaining.

I am also thinking about spiting up the source further into separate files for each word, grouped in folders, rather than all the related words in a single file etc. Not sure if that is necessary or beneficial, but it might make reordering things a bit easier. I will try it for the new words, and if it works out, I will probably go back and reformat the existing code (I will go back to this version and make it build identical before I add back the new code).

I also hope none of that comes off as critical or negative, that wasn't my intention, this was just my choice to give myself a lot of work translating someone else's code to an archaic assembler that I happen to like, to theoretically make my life easier in future.


Adverts

The Minstrel 4th is available form my Tindie store:

I can still ship worldwide.

Currently it looks like Royal Mail to the US is working although it seems I now have to pay the 10% tariff, so hopefully that will be plain sailing and no problems with customs. We're all pawns in a petulant child's political games, but we've got to just keep going and try to make this stuff work.

I have also built some more Mini PET 40/80 Internal boards, since the Mini PET II is still in development but people keep asking, so here they.



Patreon

You can support me via Patreon, and get access to advance previews of development logs on new projects like the Mini PET II and Mini VIC and other behind the scenes updates. This also includes access to my Patreon only Discord server for even more regular updates.

Sunday, 21 September 2025

SD2PET ?Device Not Preset Error

This is a problem we see every now and then.

Someone buys an SD2PET, plugs it into their PET and tries it and they get the dreaded ?Device Not Preset Error.

(I should apologise now as all the screen photos in this post are going to be rubbish as they are taken from an 8032 CRT and I am clearly incapable of doing that)

Now the first thought is probably "the SD2PET is faulty", but honestly, that is probably the least likely scenario.

They way I try to think of it is you are attaching a brand new bit of recently built and tested kit to a vintage computer in unknown condition.

Imagine you have a 1980 Ford Cortina. It might work fine for everything you use it for. Then one day, you need to carry more stuff around, so you attach a brand new trailer. That should go OK, but you might find it can't make it up hills any more, the tail light hookup might not work (it may never have been used before, or not this century anyway). Someone might have fitted a non-standard connector that the trailer designed to attached to a 1980 Cortina might not fit onto. The towbar mountings might be rusty and snap off as soon as you try to pull anything with it it.

None of which would be the trailers fault, but you wouldn't have known about those issues until you tried to attach one.

It may have been "fully refurbished" when you bought it, but did they try it with a trailer?

So what can you do?

Well, various things, check it's plugged into the correct connectors, the correct way up.

Check the edge connectors are clean and shinny, with no dust or corrosion or lumps of solder where someone tried to attach something sometime in the past.

Another thing to try early on is to remove the SD card.

With no SD card installed, it should return a listing of "EEPROMFS".

Until you see that, don't put the SD card back in, it just helps to narrow things down, keep it simple.

If that works without the SD card installed, check the SD card contacts or try another card.

If you don't have the TFW8b preloaded SD card, then you should get the oldest, slowest, lowest capacity card you can as that has the best chance of working. And go for an SD card, rather than microSD card in an adapter. They are not the same.

It should be formatted FAT32, although if it is formatted incorrectly (i.e. VFAT or NTFS or if it been near a Mac that has helpfully repartioned it to be unreadable), that it will more likely return "?file not found error"

If you have another PET, you can test the SD2PET on there and make sure it works (as we suspect it will).

If you have a known working IEEE-488 disk drive, you can test that out and you would expect to also see ?Device Not Preset Error.

Self Testing

The design of the PET uses two IO pins for every pin on the IEEE-488 bus (well, almost every pin, one is output only, one is input only, one is hard wired to ground).

For each of the main pins, there is an "out" and an "in", the former sets what appears on the bus, the later reads the state of the bus.

That means that the PET can actually test itself by writing to the bus and then reading back to see if that matches what was written.

I won't go into the full details here, it is described in the second half of this blog post:

If what is read back does not match, then it could be down to a few things:

  1. The output pin not sending the output
  2. The input pin not reading the input
  3. The buffer IC that sits between those and the bus
  4. Some conflicting device (bad ROM or RAM or IO chip or decoding logic - yes I know that's pretty much everything)

On the PET, the inputs and outputs are generated mostly by the 6520 nearest the IEEE-488 port, but also by the other 6520 nearest the keyboard connector and also the 6522.

The bus interface is by three MC3446 chips which connect the inputs and outputs to the bus.

If the 6520 and 6522 chips are socketed, try reseating those first, if that makes no difference, you can rule those out by swapping in known working 6520 and 6522 chips.

If that does not help, you could try something like my PET IEEE-488 Diag board (https://www.tindie.com/products/tynemouth/pet-ieee-488-diagnostics-tester/)

This shows what is happening on the bus, so it you set a signal, you can see if the LED changes. If it does, then the output side is working, but if the PEEK doesn't change, then the problem is on the input side.

If the LED does not change, you can test the input side by bridging the signal pin to ground.

Don't panic, that isn't going to damage anything. The bus should only ever be in two states. Either floating high because nothing is driving it, or driven low by the PET or the disk drive (or a bit of wire). Nothing should ever drive the bus lines high.

Note the REN LED is always on as that is hard wired to 0V inside the PET, so it's sort of a power LED, it will always be on.

Fixing the problem

On the bench today, I have an 8032 board.

The owner has gone through all the POKEs and PEEKs described in the blog post above and has found the ATN line is not reading back.

Checking that with the PET IEEE-488 Diag board, the REN LED is lit, but nothing from ATN. Everything else seems to be OK.

I tried the wire link and it did not read that back correctly, so the MC3446 is likely bad.

I thought it would be interesting to check this on the logic analyser.

I used the leads I made up recently to clip onto the suspect IC. (http://blog.tynemouthsoftware.co.uk/2025/09/logic-analyser-replacement-clip-leads.html)

I also added one on the bus, from the PET IEEE-488 Diagnostics board on the LED side so it would be 5V logic, and one on the IO chip select line, just so I could see when that was active in case both IO pins did nothing.

The results were clear, ATN out was toggling correctly, but nothing was making it to the bus.

I wrote a little test program to toggle the pin, to check.

And again, you can see ATN toggling, but nothing else.

The suspect chip is the MC3446N at UA20.

Time to get the desoldering gun out.

The desoldering gods were on my side today, that came out nice and cleanly.

A new old stock MC3446AP replaces it.

Time to retest.

Oh.

I repeated the POKE and PEEK test and it was still bad.

I repeated the wire link test and it was now reading that pin correctly. So that 3446 was bad, but there must be another problem.

I poked around with the scope and found the drive signal from the 6522 was very weak, it was only going between about 2V and about 3.5V. That's not right (but unfortunately it was good enough for the logic analyser).

The 6522 is socketed, so it was easy to try a replacement.

Ah, that looks better.

That is now swinging between 0V and 5V as it should.

And there it is on the logic analyser, Out is driving the Bus is that is being read back to In. Perfect.

But does it work?

Yes it certainly does, let's try with an SD card

Looking good

I tried a few more things, and everything seems to be working nicely.

Only the lack of dust on the new chips gives away any work has been done.

So, two bad chips, I would normally have suspected one or the other, but both have tested bad on another board.

I suspect the 3446 failed and damaged the output of the 6522, but it could have been the other way around.


Adverts

The PET IEEE-488 Diagnostics LED board is available form my Tindie store:

I can still ship worldwide.

Currently it looks like Royal Mail to the US is working without having to pay tariffs (computer parts are excluded from the 10% so there is just a handling fee, which I pre-pay). There still may be delays or charges. We're all pawns in a petulant child's political games, but we've got to just keep going and try to make this stuff work.


The SD2PET is available from The Future Was 8 bit, with optional pre-loaded SD card:


Patreon

You can support me via Patreon, and get access to advance previews of development logs on new projects like the Mini PET II and Mini VIC and other behind the scenes updates. This also includes access to my Patreon only Discord server for even more regular updates.

Sunday, 14 September 2025

New Microcontroller Features Part 1

I first used microcontrollers in the early 1990s, with I think the PIC16C54, a homemade parallel port programmer and one of the versions of this book constantly on my desk.

Looking at the features of that now it rather surprisingly minimal.

  • It has one 8 bit timer.
  • 512 bytes of code and 25 bytes of RAM.
  • That's it.

No 16 bit timers, no UART, SPI, I2C, Analogue functions, SRAM, touch sensors, USB, LCD drivers etc.

The technology of those microcontrollers goes back to the 1980s. There was a earlier version of that chip in the ZX Spectrum+ external keypad (back before Microchip split from GI).

Those were meant to be developed with ceramic EPROM versions with glass windows to UV erase the contents. The production chips were one time program.

It feels like the stories you hear of people writing programs on punched cards and then getting one shot to run them on the actual mainframe, and it may or may not have worked.

I had one shot. I bought the chip, wrote the code, programmed it and hoped it would work.

If it didn't, I had to raid my pocket money and buy another chip. I think I finally got some LEDs flashing on the second or third attempt, but I was very proud at the time.

PIC16C84

A few years later the PIC16C84 came along with double the program memory, and it was now EEPROM program memory which was electrically erasable, and so could be programmed time and time again.

My first PIC16C84 programmer was built from design by Derren Crome in Everyday Practical Electronics magazine (February 1996, pp 102).

I am sure the original veroboard programmer with four rectangular red LEDs on it is still around somewhere, but I can't find it at the moment. I later replaced that with a kit version based on the same design, which I still have.

The introduction of the electrically erasable versions brought this to hobbyists like the teenage me to be able to develop code and try it in real time.

This version did now had all the following features:

  • 1K EEPROM program memory
  • 36 registers
  • A single 8 bit timer
  • An external interrupt

I built lots of projects with those, including my final year degree project. That was a demonstration system for a way of controlling car lights with a single power bus and an optical cable. My test setup was a 4x3 sheet of plywood with two mini Metro taillights from the scrapyard and a few coils of fibre. I wish I had a camera in those days, no records of any of it unfortunately.

I still have a few bits of old project boards. I wonder where I salvaged the chips on this one from? 74LS04 from 1977, a ceramic 54LS138 from 1985. Only the PIC would have been new, the later PIC16F84.

I think this was a clock control board - I built a lot of clocks in those days. These ones used the larger PIC16F874 I think.

But we did an awful lot with those minimal features. It was a similar discipline to writing on an 8 bit computer. Every byte and every cycle counted. Every bit of lazy coding wasted time and space, both of which were precious.

When you think about it, the PICs were RISC processors, Reduced Instruction Set Computer, with 35 instructions. That's not too different to the standard 6502 with 56 instructions, and fewer registers and not even the single 8 bit timer.

I am sure writing under those constrains breeds better programmers. The old approach to "my code is running slow" was "rewrite it to be faster and more efficient". The modern approach is "specify more RAM and a faster processor".

Arduino

Roll on to the next generation, something like the ATmega328P found in Arduinos and the like.

That seemed to bring on a whole new wave of possibilities with it's neatly packages microcontroller and USB programmer all in one.

The ATmega328 and chips of that generation had a while load of more peripherals.

  • Two 8 bit times
  • A 16 bit timer
  • 6 channel ADC
  • USART
  • SPI
  • I2C (or rather TWI which is compatible as I2C is a licenced protocol)
  • Analog Comparator
  • 32K program memory
  • 2K SRAM

Such a world of possibilities.

Although, again, I built clocks.

One thing to note here is that each of those features had their own dedicated pins. There was one pin that could be RS232 TX. Most could be an IO pin, some could also be analogue inputs or external interrupt sources.

When designing things, you had to be aware that if you wanted to use SPI, then you had to use pins 16, 17, 18 and 19. That was sometimes a problem, specially when I often need an 8 bit wide port and usually find they are broken up with pins I need for serial or SPI etc.

AVR series

Things have moved on again. Microchip bought Atmel and brought a lot of PIC style things to the latest round of AVR microcontrollers, which now carry a Microchip logo.

They also have their own combined programmer and dev board, the Curiosity Nano series.

These have been quite useful, since I have never got on with their programmers like the MPLAB (should be) Snap (ped in half).

They are also small and cheap enough (~ £15) that you can just wire them into a project during development.

These have an embarrassment of peripherals compared to the lowly PIC16C84 and even the ATmega328P.

  • 128K program
  • 16K RAM
  • 512 bytes EEPROM
  • 4 x 16 bit timers
  • 12 bit timer
  • Real time clock
  • 3x USART
  • 2x SPI
  • I2C (TWI)
  • 10 Analogue inputs
  • 3 Analogue comparators
  • 1 Zero crossing detector
  • 18 channel touch controller

However, there is an important difference, these features are no longer tied to particular pins.

If you want to use an RS232 port, there are now three, so you can have your RS232 TX on pins 2, 10, 12, 16, 22 or 26.

The datasheets now have massive tables showing the various multiplexing options.

This is a bit of a double edged sword, all this flexibility means there is a lot more to configure to map the signals to the right pins.

There are also two new internal systems which can be incredibly powerful.

The Event System and the CCL (Configurable Custom Logic) system allow you to wire up some of those peripherals and pins in different ways.

Configurable Custom Logic

In it's simplest form, the CCL is a few gates of configurable logic, sort of like a mini GAL chip. You have multiple three input, one output blocks. You can do things like take three input pins and set an output high when one is low and the other two are high, or whatever, according to a pre-programmed truth table.

You can also add sequential logic like flip flops to these signals.

That can be useful, and can save a logic chip or two. I used this to remove the need for one of the flip flops in the Minstrel 4th (although I then used it for something else).

Where it becomes more interesting is there are some other inputs you can add to the logic, such as the output of timers etc.

Events

The event system is another way of wiring thing together, again, all behind the scenes requiring only setup code and stealing no cycles after that.

You can do things like get a timer to trigger the ADC to start an analogue conversion, you can get an external pin to trigger a timer, or count a pulse.

When I was first investigating the Mini VIC, I had one of these setup with timers generating the video sync, all does with this linked logic and no code other than the original setup. The horizontal sync counter would output the pulse at the start and clock the vertical sync counter, which would output it's own sync pulse at the appropriate point.

You can use also wire the event and CCL system together to do quite complex tasks, for example, you could have three pins that were ANDed together in the CCL and then sent to a timer via the event system to increment a counter.

You can also change things on the fly from code, so in the Minstrel 4D, I had the signal sent to the EAR pin configured as a CCL output. This could either be wired to the analogue comparator output, or it could be controlled by the output of a shift register clocking through data from SD card or loaded over serial.

But ....

With something like the ATmega328, it was fairly easy to set things like that up, as there were limited options, so once you enabled the RS232 USART, you know it was going to use pins 2 and 3 (although as is the law in this situation, there is no point in checking which is which as 50% of the time you it will be wrong anyway, RX-RX or RX-TX etc.)

Because of all the options in the AVR chips, various tools are provided to configure things for you.

I don't want to put you off here, but they don't always work. There seems to be an update every time you start up, and there is a whole new system every couple of years, and unfortunately none of them are perfect.

Having tried many of these at different times, and haven't found one that suits me yet (which I am sure is partly my fault).

The main issues I have found are:

  • Life cycle - as they move to new systems, the old ones stop being updated to add support for new devices
  • Bugs - with so many devices, and so many combinations of options, there are bound to be mistakes
  • Bloat - in order to support all these things, the code generated tends to be quite large and inefficient

Those may not be a problem for you (or indeed me), depending on the application. Often I want to be able to flip IO pins in a single cycle. However if the timing is not that important, the framework will generate nice commands like

  • LED_set_level(true);

Depending on the compile options, that might be optimised, but otherwise there is a lot of overhead with C function calls, saving lots of registers and them restoring them at the end.

I have found the best way for me is to use the tools to generate some code, and then try to pick through the dozens and dozens of files generated to find which bits I need to add to my simple C or assembler projects.

I think a good way to demonstrate this would to run through a real world example, with increasing complexity, to get to the functionality that I needed, and then condensing that to the few lines of assembler are actually important.

This post is already getting a bit long, so I will do that as a part 2.


Adverts

Lots of things built using microcontrollers are available from my Tindie Store. 

I can still ship worldwide. 

Currently it looks like Royal Mail to the US is working without having to pay tariffs (computer parts are excluded from the 10% so there is just a handling fee, which I pre-pay). There still may be delays or charges. We're all pawns in a petulant child's political games, but we've got to just keep going and try to make this stuff work.


Patreon

You can support me via Patreon, and get access to advance previews of development logs on new projects like the Mini PET II and Mini VIC and other behind the scenes updates. The part two to this post should be appearing there shortly. This also includes access to my Patreon only Discord server for even more regular updates.