Sorted by Squirrels.

Monday 1 December 2008

Goodbye yellow brick!

I've been reading about de-yellowing old equipment. Quite a few of my older babies have significant discolouration issues. Until recently I followed with the received wisdom, that once a plastic item has yellowed there's no going back. Chemical changes within the plastic produce the effect, and no amount of scrubbing will ever return your creamy commodore, beigey beeb or tawny tandy to its former glory. So imagine my surprise when I stumbled upon this wonderful posting over at the vintage computer forums. 

Old computer enthusiasm and chemistry wizardry have come up with a recipe for undoing the supposedly permanent effects of UV and fire retardants!

I'm definitely going to try this out, as soon as I can get hold of the requisite chemicals...


Saturday 15 November 2008

For your eyes, ears only. Oh, and everyone else's too.

Intrigued by something I've done? Want some? Just ask. I can't post every bit of code that I write, every schematic. But if you want some help or encouragement then just contact me. I'm always pleased if I can be of some use.

Switch.

I was working a few extra hours this weekend as my code finger was a bit slow last week. It's not often this happens, unlike in my previous life - scratch that - sentence, in the games industry (I'm not bitter - just astounded at how long it took me to realise how ugly it is). Bizarrely I had a camera with me so I took the liberty and a photo.


You know that you can click the pic for a higher resolution version, yes?

The museum has grown a little.. The master has been swapped out for a regular B. It has got a second processor attached though, and you can see its tiny appendage - an MMBeeb interface. Marvellous stuff by Martin Mathers. It's sitting on top of, yes it has to be said - a cardboard box - and quite the largest piece of retro peripheral that I own. A dual 5.25 inch floppy drive. Look at the size of that thing! It has an unbelievably long cable attached too - 10 metres at least - which by any stretch of the imagination shouldn't work. That's quantum physics for you! Probably.

OH! How I wish that Jupiter Ace was mine, alas it's not. It belongs to Mr Cliff. B'stard. ;) This one has a few yellow marks but it still works. Especially after the keyboard contacts got a firm rubbing with an eraser.. Win! Only about 8000 of these were sold. We loaded up some software a while ago and it was fairly obvious why this was a niche machine. If you come across an emulator then try 'fish and flutterer'. It's pure class.

Thursday 6 November 2008

It only takes a Jiffy

As I now have the uncanny ability to burn EPROMS using only the power of my mind and, well, err, some equipment, I needed to try it out with something.

Since my SD2IEC (currently broken) supports the JiffyDos turboloader, I decided to upgrade my C= machines with new kernels (Or is it kernals? The two spellings seem interchangable in the world of Commodore, but I'll stick with the former). This upgrady action might give me the impetus required to fix it.

The '128 was easy enough, the ROM chips used were pin-for-pin compatible with modern 27c types. The VIC needed a small adapter board and some leg-bendy, jump-wiry action. The real fun started when I cracked open the '64c. This later model of '64 has serious cost reduction issues - like a lack of sockets for ROM chips. At least the chips they used were, like in the '128 model that followed, pin compatible with modern EPROMs.

Undaunted I considered desoldering the chip and adding the socket, but that wouldn't have been hacky enough, would it? :D

So I got out my side-cutters and removed the power leg. Covered the wound with a small sticking plaster (band-aid for our international readers) and patched a wire from the PCB stub to the power leg of my EPROM.


Next I bent the EPROM's pins slightly under itself, giving it a bow-legged aspect that I found rather pleasing. This newly lamed chip was then spring-loaded piggy-back fashion over the top of the incumbent ROM.


Like so! Isn't that sweet :) It's giving the other one a special cuddle!

I powered the old girl up and ... nothing. Black screen. Panic sets in. Turn it off and on again. Despair. Slump back onto my ample rear and hello! What's this?! I hadn't been waiting long enough for the machine to boot - I forgot that they take a second or two. It worked! Of course I knew it would..;)

I briefly considered holding the new chip in place with some gaffer tape, but that didn't give me a warm feeling. It would also have meant that the gaffer could have escaped. And we didn't want that either. So I decided a semi-permanent arrangement of solder to two diagonally opposite legs would do the job.


Yes I know it's a blob, but it was early in the morning!

Tuesday 21 October 2008

Tier Jerker

The Finished Shield:



40 lines of IO plus a number of the Arduino's lines brought out to header sockets. A couple of status LEDs and we're done. I've taken the PORTC lines, AKA the analog inputs A0-A5 and D2-D7 to the back of the board. As it is, the shield's pass-through connectors are offset by 50 mil, making piggy-back boards impossible to make cleanly out of proto-board. The power lines and D8-D13 have their pass-throughs in place, as they are essential for my MMC sail.


The Linguine Layout:



I love kynar wire! Did I ever mention that?? :D The enamel coated copper wire is great too.


The EEProm Topper:



This is a a scavenged megabit chip. My first application for this board will be to program this. As it slots into the IO card so it will fit into a small adapter board that I have in mind to enable it to emulate a 64k ROM to transfer code to my burner.


The Assembled Wedding Cake:



Silly! But that's what I like about it. An antidote to the misery happening in the world today ;)

Thursday 9 October 2008

Shields ... On?

Up you blaggard! Up!



No, on second thoughts 'on' is definitely more appropriate in this case. As I mentioned in a previous post I'm crystalising the max expander board. It's about time I ruined my eyes and lungs with some close work.



I'll be putting two of the blighters on this board. Seeing as I'm down there anyway I reasoned that 40 lines is better than 20, what.

I didn't see any sense in making this a fixed purpose board, hence I'm opening up the lines with SIL sockets so further adapters can be inserted. The 1st will be a programmer for some of the megabit EEPROMs that I've salvaged from a couple of P2 motherboards.

Saturday 20 September 2008

New Logic Analyser Brainstorm

Twin 4040s. 128k static RAM. A line buffer, probably in the region of the number 244. Something related to Arduino in a vague fashion.

8 data lines connected to the buffer, and from there onto the RAM chip. The 4040s provide the address generation.

Press a button or otherwise glean a clue about an appropriate time to start sampling from some logic or other. Reset the addressing logic. While ram not full:- increment address, latch data into the buffer and then write it to the RAM. When RAM full, await instructions to download via serial.

Could work, non?

Alternatively there seems to be a little logic analyser war happening right now - I'd back this regime. If you earn in dollars, then it's offering quite good value for money. If you earn in English pounds then you're laughing.

Friday 19 September 2008

Programmer Redux

The drop-in centre for stray capacitance was closed by the health and safety after it was shorted by a stream of wee created by a Superintended Nindo. At least I think that's what I heard. I could be wrong. Whatever happened, all the residents must have all come to join with the raving mass I already had, the result of which is that the board resolutely refused to work again after my last witless ramblings.


Pictured: Stray capacitance gathering
ready to frustrate my EEPROMming.


So - I started again!

The code was as much of a lash-up as the board, so I gutted it in favour of a Max6956 IO expander cum LED display driver. 20 gorgeous lines of GPIO, all driven by the Wire library and 2 lines off the Arduino. 12 Address lines, 8 data lines - Pimms o'clock! It's so much cleaner now without having to clock data around scratchy shift registers. I might actually show it to you.

The board came together in about 40 minutes and the code didn't take long at all because I already had a working sketch that drove the beastie. And to my immense relief it worked first time, and reliably every time since. I think I may well solidify this design onto one of the Arduino shield PCBs that I bought from Lady Ada aeons ago 'in case I ever needed one'.



You can see here the new MMC sail, #4 in a series of many. My favorite one so far, this has more than the requisite 6 resistors. I got a smart colleague to recommend a way to get an LED to flash in response to the clock line clocking. He told me that a monostable would do the trick and here it is. Why not just pop an LED on the line? Well, I'd done that already and sometimes that clock line might be left high.

Tuesday 16 September 2008

Lash-up!



What a mess! But it works - sometimes :) All that stray capacitance - well someone's got to give it a home.

This is what I'm forced to do because the Stag PP39 EPROM programmer that I was so generously given by a fellow FreeCycler works well, but for its serial connection. Ho hum. Wanting to burn an EPROM to hold the driver code for the Atom MMC interface was the necessity that was the mother of this frightful invention. In a fit of pique I had the idea that I should create a 3-step process: Burn my code into an EEPROM, transfer this to the programmer's buffer, and thence into an EPROM! Why not just use the EEPROM for my project? An astute question, esteemed reader. Well the type of EPROM expected by the Atom is an olde-fashioned marque with a subtly different pin-out to the more contemporary (read: standard) 27x series. Luckily the PP39 can burn the 2532 that was required. Even luckier I suppose is the fact that I had one of these! An adapter board can be made to facilitate the harmonious interfacing of the disparate breeds, but this would involve less lashing-up, you see? And we all need a jolly good lashing from time to time.

I digress. What you see before you (or more correctly above) is an ls299 accompanied by a brace of ls164 shift registers. These in turn are connected to an 8k Atmel EEPROM. In the driving seat you see the Arduino and - naturally - a poor but functional MMC interface. The 164s are in charge of address generation, and the 299 has bi-directional data line duties. It's a simple and effective design which I have referenced before.

I've developed the Atom MMC driver in assembler, naturally. This time I opted for cross assembly. If you saw the code attached to my previous post (my - is that the time?) you may well understand why - the inline assembler is hard work with its terse labelling syntax. I develop and assemble on a PC, using a custom Visual Studio workspace and a freeware 6502 assembler. The resulting binary is debugged as far as possible using Wouter Ras' brilliant though tricksy DOS Atom emulator. When I'm happy with the code it gets put on the MMC card and burned to the EEPROM using a subtle combination of swearing, crossed fingers and sacrificial chickens. The burning process needs to be attempted a fair number of times (the stray capacitance, bless) until the verify step passes and I can be sure the lash-up has worked. Once transferred to the programmer, the code fizzles its way onto a freshly UV-cooked EPROM and then into its warm and welcoming bed - Socket IC24. And so to work:

Presenting - the MMC adapter I built to fit on the venerable machine's expansion port, as visible here.



And in situ:



Goodness - that is the time! Two and a half months this post has taken me! Either that or my post-Sachertorte coma was deeper than usual... Whatever, forgive me. I am off to play Atom Invaders - which now loads in under 3 seconds, a far cry from the original 5 minutes of the tape version!

Tuesday 1 July 2008

He's at it again!

I'm getting back into the Atom swing! After a fruitless hour measuring incorrect logic levels at the Atom's expansion port I was on the verge of giving up. I had been prodding the VIA, according to available documentation, and getting nowhere. All I wanted was a toggly bit. Was that too much to ask for? That's when I decided to do some chip swapping.

It takes me longer than most people to get to this point as I'm a software person at heart and so blame that first ;)

I just happened to have a vintage 6522 VIA lying on my desk so I swapped the chips over. Joy! I was flipping bits like a demon. Naturally my thoughts turned to MMC...

10@=0
20!#208=!#208+3
30V=#B800
40W=#FFF4
50T=#80
60DIM LL(13),C(6)
70F.N=0TO13;LL(N)=#FFFF;N.N
80F.N=1TO2; DIM P(-1)
90P."PASS "N',$21
100[
110\ main
120:LL0 LDA @#0
130STA C+1; STA C+2; STA C+3; STA C+4
140LDA @#95; STA C+0
150JSR LL3
160LDA @#40+#0; JSR LL7
170LDA @CH"1"; SBC @0; JMP W
180\ select card
190:LL1 LDA @0; STA V; RTS
200\ deselect card
210:LL2 LDA @#20; STA V; RTS
220\ init hw and go spi
230:LL3 LDA @0; STA V+#C
240LDA @#FE; STA V+2
250LDA @#20; STA V
260LDY @10
270:LL4 LDA @#FF; JSR LL5; DEY; BNE LL4
280RTS
290\ xferbyte
300:LL5 STX T+0; STY T+1; LDY @8
310:LL6 PHA; AND @#80; STA V
320ORA @#40; STA V
330LDX V; AND @#BF; STA V
340TXA; ROR A
350PLA; ROL A
360DEY; BNE LL6
370PHA; JSR #F7FA; PLA
380LDY T+1; LDX T+0;
390RTS
400\ command
410:LL7 STA C+5; LDY @5
420:LL8 LDA C,Y; JSR LL5; DEY; BPL LL8
430:LL9 LDA @#FF; JSR LL5; AND @#FF; CLC; BPL LL10
440DEY; BNE LL9;
450SEC
460:LL10 RTS
470]
480P.$6
490NE.N
500END



If you're an ex-atommer or you've been around BBC micros you might recognise this. If you don't then I can tell you it's some Atom basic with inline assembler. The Atom's Basic dialect was hard on the eyes but lightning fast. By the standards of the day, naturally ;)

The built-in assembler gave the user of this machine an instant boost when learning to program. No fussing with 3rd party apps, loading the assembler or machine code, or the attendant problems with cassettes. Turn on. Assemble. Bam! (Which is as it happens almost quite literally what happened to my 1st Atom over 20 years ago...) Now if only it was that easy for the kids of today - it was much better in the old days - I remember when all this was fields etc. etc.

The above listing is code to bit-bang SPI to an MMC card attached via the simplest of hardware harnesses to the rear expansion connector of the Atom. I'm looking at making a ROM based solution that hooks into the OS's filing system vectors. It's all quite primitive at the moment but I'm hoping it'll solidify. Get in touch if you have any experience of developing this kind of Atom program. I'd love to talk to you.

Saturday 28 June 2008

It's gone all doubley-spaced!

(edit)
The listings are back!

Apologies about the listings, esteemed reader, they appear to have lost their formatting and become double-spaced. I'm looking into it but in the mean time, please bear with me!

Thursday 26 June 2008

Atom Power!



She's arrived! After many years of fussing and prevaricating I've finally taken the plunge and bought myself an Atom. The prices were rocketing in the bay of E, and I reckoned on the time being nigh. This one was advertised as having a broken key and otherwise with little hope of receiving a working box I decided on taking a punt and fixing it up if necessary. I've always loved this machine. I owned one as a boy although it was only a half-populated board without a case. It still did the business though!

I must admit to being a little saddened when I opened the wrapping. It was grubby and half of the keys appeared to be smashed in, sitting a good 6-7mm lower than their neighbours. The 1st thing was to open it up and check the power supply status. The Atom was infamous for overheating and many owners bypassed the internal regulators in favour of feeding it a regulated 5v diet. As was indeed the case with this one. It had received some tweaks in its time, but I was really happy to see the work was all of a high standard. These were all what I call 'magazine hacks' - the electronic equivalent of a one-liner joke. Single wire patches for enabling an eighth bit on the printer port, joining some lines to an external socket for wiring a joystick, nothing major. I was chuffed to see that it was fully expanded memory and support-chip wise. 12K RAM & 12K ROM plus a utility kit that I'm still having trouble finding any info about. I'll have to resort to a disassembler I think. No colour board but then I wouldn't ever have even contemplated that. Too rare.

The mother and key boards were in a real state. I think it had been stored on top of kitchen cupboards at some point in its life and it had received a good layering of yellow grease and fibres. Some keys were sticking and this was the main reason. I've since scrubbed it with a toothbrush and plenty of warm soapy water and now you'd never know! I was shy of putting it in the dishwasher as some people recommend, this may be ok for generic PC keyboards but rare vintage '80s hardware...

At some point in its history the three keys in the lower left had been replaced. They were replaced with high quality keyswitches, which - well I never - made the keycaps sit higher. What had happened was that the previous owner had raised the original keycaps with superglue to match the height of the replacements. The original mechanism is a cheap spring-based creation which I've only ever seen in Atoms ... and a keypad that I was given recently! That was handy, wasn't it! So I hacked the keypad and replaced the switches with something contemporary and far more suitable. And the same height! The caps all came off and were cleaned and repaired where necessary.

The machine is now clean and tidy, ready for work.

One tough trick was finding a plug to fit the odd power socket dimensions. It's some olde fashioned imperial jack size. Eventually located with the assistance of an Atom owning colleague I was able to juice the old girl. To my immense surprise it eventually reset and presented me with the very comforting words:

ACORN ATOM
>

Joy!

A computer's no good without software, right? Like all good '80s machines this one needs some square-wave goodness. I couldn't locate any software to generate the required signals that worked on a modern PC, so being handy with a compiler I got to work. I'm pleased I did all the work on ACE - the techniques transferred instantly and I had a program to translate raw program dumps into WAVS. And text files into raw dumps. And vice versa.

I'll be happy to pass these on to anyone that wants them. The source can be found here. It's vanilla C++. I'll be happy to help with porting, compiling or whatever.

There looks to be a new site emerging that could eventually be a must-see for any Atom fans. I'm speaking in the future tense as it's not there yet but I've spoken with the masters and they're promising big things. Watch this space.

If you're an Atom fan, get in touch.

Thursday 15 May 2008

IO, IO it's off to work we go!


This is my 1st attempt at getting a PCB made. It was rushed so please excuse any glaring stupidity :)

A colleague was having some PCBs manufactured and had a little space left on the plate so I knocked this together during a lunch hour. I'm pretty pleased so far - the boards have come back and, despite being less than optimally packed, they're quite neat.



This is a ZX81 IO board. As I'm sure you've already worked out, esteemed reader! The 8255 is a general purpose IO controller and it's mapped to the Z80 IO space at ports 0x07 and 0x17. I'll be attaching the MMC card to it later :)

I've included links to the schematic and board files in case you want to fire up Eagle and have a play. This is a package with a very steep learning curve. I've been playing with it for about a year and I can only now say that I'm beginning to become confident. Version 5 has just been released which adds some polish to the mix. It wouldn't be uncharitable to say that the package weas a little rough around the edges before..! I haven't pushed V5 much - and it's likely I never will :) - but you can just tell that there's been some love and attention lavished. Hurrah for CadSoft!

Click the schematic for a very high res version. Or get the binaries from HERE.

I'll let you know how it works out when the SMT 74s arrive!

Friday 2 May 2008

Average bits 10.2



May I present some of my non-human babies. Continued thanks go to my employers - you know who you are - for allowing the museum to exist on my desk.

The C128 rear-left has a proto-board built SD2IEC card installed inside the tape drive. I shaped the PCB like a cassette. Press eject and you can insert an SD card into the presented slot. Cheesy but fun :)

The Apple //e front left is my latest baby. I'm awaiting a prototype board from the states so I can build an IDE card. They're ridiculously simple to build, it seems.

The centre column contains a QL, an STe, a Spectrum 128 and a BBC Master 128 with MMBeeb interface. They're nice, but not my favourites. Especially as the Sinclair machines require attention due to defective membranes. A pathology known around these parts as the curse of Clive.

No, the favourite machine honour belongs to the Atari 800. This is another machine with a SD card interface secreted away in a hacked tape drive. I dreamed.. no.. seriously fantasised about this machine when I was pecking away at my ZX81. (Not pictured - it's on the bench at the moment having an SD card and keyboard upgrade, natch)

One thing tends to be common amongst these machines and that's the addition of SD card interfaces.. My hat goes off to the engineers linked in this post. MMBeeb was my first exposure to the practice, which gave me enough of a kick-start to design my own interface for the zx81. Monster maze loads in 6 seconds. Bliss!

I guess it remains to be said - I AM 8-BIT

And proud :)

Sunday 27 April 2008

Like a record baby!

I've been inspired by someone or other's homebuilt spinner. I probably saw it on Make or Hack-a-day, I forget. If you aren't familiar with the aforementioned input device it's like, well you know, like an arcade game control that could be found on such classics as Tempest, Arkanoid, Cameltry and many many more. You spin it. Hence the name.

I had to have a go. I have a project stack 100 deep at the moment and no time to do anything, so I needed a quick fix, a micro-project if you will. And this seemed like the perfect candidate.

So the obligatory VCR head was pulled from a dead Matsui. This particular model had a wonderful head motor mechanism that came apart very easily and cleanly, leaving no random sprue like some I've been near. It also furnished me with convenient screw holes. The motor shaft has a metal disk at the end which was just the right size for some idle tweaking. There's something very relaxing and .. zen I suppose .. about VCR head motor mechanisms. I suggest everyone try hefting one and idly spinning away a few minutes :)

An old USB mouse was inspected and found to be of the correct vintage - ball era. The optical encoder disks were removed and the pcb ritually prepared. The left/right encoder was severed and the traces replaced with kynar wire. Did I already say that this stuff has changed my life? I did have to modify my wire strippers to nip away that micro-fine insulation and I must admit the very act made me feel quite hardcore!

So with the emitter/receiver pair free to do their thing I started to try and mount the encoder disk on the base of the motor. What a nightmare. I tried different adhesives and when the little black brolly wasn't teetering drunkenly on the shaft or resolutely embracing my fingers it was wholly and utterly eccentric. In the end I liberated one of the tiny rare earth magnets from a sneakily swiped Magnetix stick. To this I super-glued the encoder on its truncated shaft. This could then be pinged onto the end of the shaft and repositioned easily whilst still holding position. When it was rotating as centrally as I thought it could, out came the hot glue. One quick spin draged a glistening thread of glue from the gun like silk from a spider's arse. And there was the disk in place, semi-permanently fixed. Result!

To mount the spinner I chose a CD 'cake box'. I'll leave the details of mounting the motor to your imagination as I am wont to do. All I will say is knife and screws were involved, but not my friend the rocket scientist.

So with the motor mounted all that needed to be achieved was the solid mounting of the transceiver pair. Tip #2: Don't use stiff wire. I thought that this would be simple, quick and give me plenty lattitide to position the payload precisely in space with little play. Wrong. It bounced around like Katie Price on a trampoline. So I did what I always do at moments like this - reach for the Lego. A small 1x2 hinge, a 1x8 plate and some more hot glue later I had the PCB shard mounted in the right position. More hot glue saw it fixed and locked. Game on!

[Click for a much, much bigger view! Erk!]


Cuts on your fingertips are painful. Rough-edged spinning steel disks attached to a momentum powerhouse cause cuts. Thinks needed :) The rubber 'tyre' that encased the middle mouse wheel was promptly repurposed. An almost perfect fit! I was understandably quite chuffed. While in the heightened state of chuff I figured some buttons might be useful too, in order to save splitting control between the keyboard and spinner when necessary. So off to the bits box and one feisty rummage later we have control! And the old BBC Master computer has 2 less keys.

One leisurely wiring of these buttons to the mouse PCB switch pads and there we have it. One spinner. It may not be pretty, but it was a project! And it works great with Tempest :D

[Click for a much, much bigger view! Zoinks!]

Sunday 13 April 2008

u-Fat filesystem

Not a truncated attempt at a verbal slight, but a solution to PC compatible file access for micro-controllers!

Many FAT file system implementations are, by necessity, too large to take a place in micro-controller applications. Long file names, fragmented files, the FAT tables - no wonder a 'proper' solution weighs in at many K of code and buffers. One solution is to carry out raw access to the card, but this then involves compromises when getting the data off the card onto the desktop.

My solution is to reduce weight of code by applying stringent use restrictions:

Only 16 files (potentially), named in 8.3 format, pre-allocated and contiguous.

This enables us to locate the start sector of the file and read/write to it as we wish. The files can be freely swapped between PC and micro controller application using standard operating system commands. To reach this state of nirvana all that needs to be done is format the card before copying the pre-allocated files to it.

The only external requirement for servicing the code is a function that reads sectors, using logical block addressing, from your chosen FAT device. If you look in the ACE post previously the code zip there includes a complete MMC solution.

I found a schematic on the arduino site that is almost identical to my own and comes very highly recommended. If you haven't got a card reader that you can cannibalise then edge connector works well too. It's a common way of doing it and there are many good examples around - you can find one here.

Here's the header which shows just how micro micro can be!

 /*
/ Microfat.
/
/ Developed by Charlie Robson in 2008.
/
/ This is the bare minimum* functionality required to read and write data
/ to a FAT formatted device. In this case, an MMC card.
/
/ Rather than build a fully-featured FAT implimentation this will allow
/ the user to locate sectors associated with a file on the device, and
/ read and write to them in a brute force manner. No error checking, no
/ long filenames, not even any support for fragmented files :) This is
/ raw access, we only deal with existing files.
/
/ What you do get is a very lightweight module for extremely limited
/ environments. Arduino has 2k of SRAM. Holding the sector of FAT data
/ needed to traverse a fragged file would instantly gobble up 1/4
/ of that. Enough said. Another benefit to not using the FAT is that
/ there's no need to worry about whether a device is FAT12 or 16.
/
/ To use:
/ Format a card.
/ Make a file big enough to hold all the data you expect to write.
/ Copy to the card.
/
/ With the MMC module started up, call initialise. This will cache some
/ relevant information about the card such as the sector location of the
/ root directory and data area.
/
/ Call locateFileStart, passing the name of the file which is providing
/ the backing store for your transfers, and assuming the file is found
/ in the directory you'll receive its start sector and file size.
/
/ Now feel free to read and write data to and from the sectors allocated
/ to the file. Code carefully though - stray writes can twat your card!
/
/ Enjoy :)
/
/ *Massive understatement!
*/

#ifndef __MICROFAT_H__
#define __MICROFAT_H__

// some data structures we'll be encountering on our travels.

typedef struct
{
byte bootable;
byte chsAddrOfFirstSector[3];
byte partitionType;
byte chsAddrOfLastSector[3];
uint32_t lbaAddrOfFirstSector;
uint32_t partitionLengthSectors;
}
partition_record;

typedef struct
{
byte jump[3];
char oemName[8];
uint16_t bytesPerSector;
byte sectorsPerCluster;
uint16_t reservedSectors;
byte fatCopies;
uint16_t rootDirectoryEntries;
uint16_t totalFilesystemSectors;
byte mediaDescriptor;
uint16_t sectorsPerFAT;
uint16_t sectorsPerTrack;
uint16_t headCount;

uint32_t hiddenSectors;
uint32_t totalFilesystemSectors2;
byte logicalDriveNum;
byte reserved;
byte extendedSignature;
uint32_t partitionSerialNum;
char volumeLabel[11];
char fsType[8];
byte bootstrapCode[447];
byte signature[2];
}
boot_sector;

typedef struct
{
char filespec[11];
byte attributes;
byte reserved[10];
uint16_t time;
uint16_t date;
uint16_t startCluster;
uint32_t fileSize;
}
directory_entry;


namespace microfat
{
// Cache some relevant info about the card.
//
bool initialise(byte* buffer);

// Get start sector, file size for given filename. Returns false if the file
// is not found in the directory.
//
bool locateFileStart(const char* filename, unsigned long& sector, unsigned long& size);
};


#endif // __MICROFAT_H__



Now for the meat:

#include <wprogram.h>
#include "microfat.h"
#include "mmc.h"

// Data which remains constant over one session. Re-initialise if
// the card is changed.
//
static struct
{
uint16_t sectorsPerCluster;

uint32_t rootDirSect, cluster2;

byte* buffer;
}
vars;


bool microfat::initialise(byte* buffer)
{
vars.buffer = buffer;

if (RES_OK != mmc::readSectors(vars.buffer, 0, 1))
{
return false;
}

partition_record* p = (partition_record*)&vars.buffer[0x1be];
long bootSector = p->lbaAddrOfFirstSector;

if (RES_OK != mmc::readSectors(vars.buffer, bootSector, 1))
{
return false;
}

boot_sector* b = (boot_sector*)vars.buffer;

if (BYTESPERSECTOR != b->bytesPerSector)
{
return false;
}

vars.sectorsPerCluster = b->sectorsPerCluster;
vars.rootDirSect = bootSector + b->reservedSectors + (b->fatCopies * b->sectorsPerFAT);

long dirBytes = b->rootDirectoryEntries * 32;
long dirSects = dirBytes / BYTESPERSECTOR;

if (dirBytes % BYTESPERSECTOR != 0)
{
++dirSects;
}

vars.cluster2 = vars.rootDirSect + dirSects;

return true;
}



// Get start sector for given filename.
//
// Returns false if the specified file wasn't found in the directory.
// Short filenames only. Be aware that the directory fills up with 'deleted'
// filenames. Rather than deleting too many files try a quick reformat.
//
bool microfat::locateFileStart(const char* filename, uint32_t& sector, uint32_t& size)
{
if (RES_OK == mmc::readSectors(vars.buffer, vars.rootDirSect, 1))
{
// The filenames are stored in [8][3] format. No dot, all upper case.
// Names shorter than 8 chars are padded with spaces.
//
// Cook the supplied name. fred.txt -> FRED----TXT
//
char cookedName[11];
for(int i = 0; i < 12; ++i)
{
cookedName[i] = 0x20;
}

for (int i = 0, j = 0; i < 12 && filename[i]; ++i)
{
if (filename[i] != '.')
{
// Force char to uppercase. Cheesy I know :)
//
cookedName[j] = filename[i] >= 96 ? filename[i] - 32 : filename[i];
++j;
}
else
{
// Continue cooking chars at the extension position
//
j = 8;
}
}

for (int i = 0; i < BYTESPERSECTOR; i += 32)
{
directory_entry* de = (directory_entry*)&vars.buffer[i];

// Don't match with deleted or system/volname/subdir/hidden files
//
if (de->filespec[0] != 0xe5 && (de->attributes & 0x1e) == 0 && memcmp(cookedName, de->filespec, 11) == 0)
{
sector = vars.cluster2 + ((de->startCluster-2) * vars.sectorsPerCluster);
size = de->fileSize;
return true;
}
}
}

return false;
}




Which as you can see is smaller than the header - due in no small measure to the size of the FAT structure definitions.

As it stands this code could be improved by using non-static file info structures - only one is supported right now but the change would be trivial. Also allowing the filename search to traverse the entire root directory space would allow access to more files. However I don't really see the utility in ths -wherever I've used this code I've never needed more than one file :)

As usual any Qs are welcome.

Sunday 6 April 2008

More ACEness



Here's a more portable command-line driven utility to generate wav files ready for ACE's ears. There's a tiny enhancement that ramps up the volume during the lead-in which should prevent the kind of pants-wetting shock that I keep delivering to myself :)



/*
* Support code for ACE - the arduino cassette engine
*
* Example code to write a wav file which can be played to
* a microcontroller running the ACE software.
*
* Should be fairly portable unlike the original example.
*
* Enjoy.
*
*/

#include <iostream>
#include <fstream>

typedef unsigned char uint8_t;

// adjust these to control endiannesses.
//
static const int ENDIAN0 = 0;
static const int ENDIAN1 = 1;
static const int ENDIAN2 = 2;
static const int ENDIAN3 = 3;

static const int ENDIAN0_S = 0;
static const int ENDIAN1_S = 1;

// desired wave structure - AHEM THESE ARE READ-ONLY
// - THEY CAN'T BE CHANGED WITHOUT FUTZING THE CODE. SORRY.
//
static const int CHANNELS = 2;
static const int SAMPLERATE = 44100;
static const int BITSPERSAMPLE = 16;

// this is as loud as we can get. not quite 11,
// but approaching it.
//
static const double maxLevel = (double)SHRT_MAX * 1.0;
static const short maxLevelS = (short)maxLevel;
static const double minLevel = (double)SHRT_MIN * 1.0;
static const short minLevelS = (short)minLevel;


// number of samples per cycle. 60 samples = 1 low or 2 high cycles.
//
static const int f1 = 60;
static const int f1cyc = 1;
static const int f2 = f1 / 2;
static const int f2cyc = f1cyc * 2;

// useful values, maybe
//
static const int usPerCycle = (int)(((double)f1 / 44100) * 1000000.0);
static const int usPerHalfCycle = (int)(((double)f1 / 44100) * 500000.0);
static const int usPerQuarterCycle = (int)(((double)f1 / 44100) * 250000.0);
static const int usPerEighthCycle = (int)(((double)f1 / 44100) * 125000.0);

static const int baudrate = (1000000 / usPerCycle);

// 8 + stop + start;
//
static const int bitsPerFrame = 10;

static const int samplesPerBit = f1 * f1cyc;
static const int samplesPerFrame = bitsPerFrame * samplesPerBit;


static const int LOW_BIT = 0;
static const int HIGH_BIT = !LOW_BIT;

static const int LEADIN_BIT = LOW_BIT;
static const int START_BIT = !LEADIN_BIT;
static const int STOP_BIT = !START_BIT;


// NASTY HACK FOR WRITING WAVS. PLEASE LOOK AWAY NOW.

void CreateWaveFile(std::ostream& output)
{
char chars[4];

output << "RIFF";
*(int*)chars = 16;
output << chars[ENDIAN0] << chars[ENDIAN1] << chars[ENDIAN2] << chars[ENDIAN3];

output << "WAVE";
output << "fmt ";

*(int*)chars = 16;
output << chars[ENDIAN0] << chars[ENDIAN1] << chars[ENDIAN2] << chars[ENDIAN3];

*(short*)chars = 1;
output << chars[ENDIAN0_S] << chars[ENDIAN1_S];

*(short*)chars = CHANNELS;
output << chars[ENDIAN0_S] << chars[ENDIAN1_S];

*(int*)chars = SAMPLERATE;
output << chars[ENDIAN0] << chars[ENDIAN1] << chars[ENDIAN2] << chars[ENDIAN3];

*(int*)chars = (int)(SAMPLERATE*(BITSPERSAMPLE/8*CHANNELS));
output << chars[ENDIAN0] << chars[ENDIAN1] << chars[ENDIAN2] << chars[ENDIAN3];

*(short*)chars = (short)(BITSPERSAMPLE/8*CHANNELS);
output << chars[ENDIAN0_S] << chars[ENDIAN1_S];

*(short*)chars = (short)BITSPERSAMPLE;
output << chars[ENDIAN0_S] << chars[ENDIAN1_S];

output << "data";
*(int*)chars = 0;
output << chars[ENDIAN0] << chars[ENDIAN1] << chars[ENDIAN2] << chars[ENDIAN3];
}


void FinaliseWaveFile(std::ostream& output, size_t bytesWrit)
{
char chars[4];

output.seekp(40);

*(int*)chars = (int)bytesWrit;
output << chars[ENDIAN0] << chars[ENDIAN1] << chars[ENDIAN2] << chars[ENDIAN3];

output.seekp(4);

*(int*)chars = (int)(bytesWrit + 36);
output << chars[ENDIAN0] << chars[ENDIAN1] << chars[ENDIAN2] << chars[ENDIAN3];
}

// OK IT'S SAFE TO LOOK AGAIN



// here we represent:
//
// 0 bit as 1 cycle of low frequency,
// 1 bit as 2 cycles of high frequency
//
// where the high frequency is double that of the low.
//
void OutputBit(std::ostream& output, int bit, size_t& written,
short maxval = maxLevelS, short minval = minLevelS)
{
// default to low
//
int cycles = f1cyc;
int samplesPerCycle = f1;

if (bit != 0)
{
cycles = f2cyc;
samplesPerCycle = f2;
}

// generate square pulse, start with rising edge
//
for (int i = 0; i < cycles; ++i)
{
for (int j = 0; j < samplesPerCycle / 2; ++j)
{
// high [1] period
//
output << uint8_t(maxval & 0xff);
output << uint8_t(maxval >> 8);
output << uint8_t(maxval & 0xff);
output << uint8_t(maxval >> 8);
written += 4;
}
for (int j = 0; j < samplesPerCycle / 2; ++j)
{
// low [0] period
//
output << uint8_t(minval & 0xff);
output << uint8_t(minval >> 8);
output << uint8_t(minval & 0xff);
output << uint8_t(minval >> 8);
written += 4;
}
}
}


// outputs a start bit, 8 data bits and a stop bit
//
void OutputByte(std::ostream& output, uint8_t value, size_t& written)
{
OutputBit(output, START_BIT, written);

for (int i = 0; i < 8; ++i)
{
OutputBit(output, (value & 0x80) ? HIGH_BIT : LOW_BIT, written);
value <<= 1;
}

OutputBit(output, STOP_BIT, written);
}


// outputs a lead train of bits, terminated with a start bit.
//
void OutputLeader(std::ostream& output, int milliseconds, size_t& written)
{
int microseconds = (milliseconds * 1000) - usPerCycle;;

// this is a bit of a decoration -
// ramp up the volume during the 1st half of the leader. this might
// prevent some unexpected eardrum-rupture events.. and yes, i know
// that volume should be ramped non-linearly :) this will do for
// the time being.
//
double vol = 0, ramp = 1.0 / (microseconds/(usPerCycle*2));

while (microseconds >= 0)
{
OutputBit(output, LEADIN_BIT, written, (short)(vol * maxLevel), (short)(vol * minLevel));
vol += ramp;
vol = __min(vol, 1.0);

microseconds -= usPerCycle;
}

OutputBit(output, START_BIT, written);
}


void main (int argc, char **argv)
{
if (argc < 3)
{
std::cout << "Usage: makewav [input file] [output file]" << std::endl;
exit(1);
}

std::ifstream inner(argv[1], std::ios_base::binary);
if (!inner.is_open())
{
std::cout << "Could not open input file." << std::endl;
exit(1);
}

std::ofstream outer(argv[2], std::ios_base::binary);
if (!outer.is_open())
{
std::cout << "Could not open output file." << std::endl;
exit(1);
}

// write wav header
//
CreateWaveFile(outer);

// determine input file size
//
inner.seekg(0, std::ios_base::end);
size_t size = (size_t)inner.tellg();
inner.seekg(0);

size_t pc = 0;
size_t written = 0;

// file is structured:
// some seconds of leader
// size msb
// size lsb
// {
// data[512]
// some milliseconds of leader
// }

OutputLeader(outer, 2000, written);

OutputByte(outer, uint8_t(size >> 8), written);
OutputByte(outer, uint8_t(size & 0xff), written);

while (pc != size)
{
// dummy data - replace this with either read from buffer indexed by [pc]
// or a byte get from the file to be encoded
//
OutputByte(outer, inner.get(), written);

++pc;
if (pc % 512 == 0 && pc != size)
{
OutputLeader(outer, 50, written);
}
}

FinaliseWaveFile(outer, written);

outer.close();
}

Thursday 3 April 2008

No, really!


Mrs. Edna Welthorpe of Hatheringon under Nicely wrote in to let me know that she'd seen through 'my little April fool joke'.

Well, Mrs. Wellthorpe, the joke is indeed on you. The ACE posting is 100% genuine :)

Enjoy!

Tuesday 1 April 2008

Awesome mix #6

I've been laid up with a succession of minor bugs of the viral kind and subsequently there's been no movement on the hardware front. I think it's time to post something though and with that in mind please be upstanding for the one - the only - ACE!

That's the Arduino Cassette Engine to me and you.

Hankering after some old-skool interfacing? Got the urge for PCM? Here's some Arduino code to read a data stream encoded in a frequency shift keyed manner. As usual I'm presenting this in its completely raw form. There are plenty of comments in the code and enough information to allow any intermediate level programmer to get started.

Also included in the archive is the c# app that creates demo data to be read. It was adapted from another application I wrote to get data from binary images into my zx81 which explains the cockamamy structure. And the icon! If you have problems downloading let me know.

Beware of the output from SquareWave - it's as loud as it can possibly be. Make sure that volumes are turned right down before running it. You have been warned!

Enough blabber - here's the skinny ripped right from the code:

 /*
 /  'Arduino Cassette Engine'.
 /
 /  Developed by Charlie Robson in 2008.
 /
 /  This is half of a solution for getting data into an arduino. The other
 /  half being code to generate frequency shift keyed (FSK) data in the form
 /  of WAV files which should be widely playable. These can be recorded or
 /  otherwise electronically distributed. Why do it this way? 1. Because it's
 /  funny. 2. Having given an updatable arduino-powered 'toy' to someone, I
 /  can't expect them to re-program the device by traditional means in order
 /  to update it. Everyone has a means of playing a sound!
 /
 /  On with the show.
 /
 /  The data is encoded like so:
 /
 /    Lead-in
 /    High byte of data length
 /    Low byte of data length
 /    {
 /     Data[0 < size <= 512]
 /     (Lead-in)
 /    }
 /
 /  Lead-in is a train of bits terminated with a start bit. This means the lead-in
 /  bit needs to be the logical complement of the start bit.
 /
 /  Every byte is represented by 10 bits - a start bit, 8 data bits, and a stop
 /  bit. The start and stop bits are complementary.
 /
 /  Data arrives in 512 byte blocks. Each block except the last is followed by a
 /  short period, about 25 milliseconds, of lead-in. This is to allow processing time
 /  on each block as it arrives.
 /
 /  Decoding a data bit is done something like so:
 /
 /    Await a rising edge then take 2 samples at a period which will allow us
 /    to distinguish a 0 or 1 bit. Sampling 2 identical values yields a 0 bit.
 /    Delay so the next bit read will occur when the signal is in a low period.
 /
 /  Following is a picture of a '0' bit, or low frequency cycle, followed by
 /  a '1' bit or high frequency cycle, with some important times noted.
 /
 /    The X shows a rough expected time at which we start the bit reading.
 /    The 0 shows relative time zero, when we detect the rising edge.
 /    The + signs show the times at which we sample.
 /    The * shows the earliest time at which we can await a new bit. IOW, X.
 /
 /        __`_____`__             __
 /       |  `     `  |           |
 /       |  `     `  |           |
 /       |  `     `  |     `     |
 /     ` |  `     `  |     `     |
 /   __`_|  `     `  | ____`_____|
 /     ` `  `     `        ` 
 /     X 0  +     +        *
 /     ` `__`__   `   _____`
 /     ` |  `  |  `  |     |
 /     ` |  `  |  `  |     |
 /       |     |     |     |
 /       |     |     |     |     |
 /   ____|     |_____|     |_____|
 /       `
 /       `
 /       |-----------------------| = usPerCycle        = 1360uS
 /       |-----------|             = usPerHalfCycle    = 680uS
 /       |-----|                   = usPerQuarterCycle = 340uS
 /       |--|                      = usPerEighthCycle  = 170uS
 /
 /
 /  It's assumed that the audio signal is connected to PORTB-0 which is
 /  digital pin 8. If you'd like to change that, then replace occurrences
 /  of...
 /
 /      PINB & 1
 /
 /  ... with the direct bit-access for your implementation. I do it this
 /  way because it's slightly faster than digitalRead()ing. If you do change
 /  the port you'll also need to change the logic in readBit as this relies
 /  on the fact that we work with bit 0. 
 /
 */



Enjoy!

Wednesday 26 March 2008

Itnrtaenrslfeearvse!d

Some back-of-the-envelope calculations later I'm looking at about an 8-10khz playback rate. At 10khz I'll be needing to feed the DAC a new sample about every 100uS. Each byte transfer over the SPI bus takes up to 40uS. By breaking up the transfer process into interleavable chunks we should be able to achieve the required rate - but waaiiiit a minute. The DAC requires a 16bit transfer. So I guess I'm looking at 8khz. I know I could get better by using serial EEPROM, FRAM, SRAM, BLUEJAM or whatever. But that isn't the point! I always like to just have a go and make what I've got work.

If you're familiar with transferring data from MMC you'll know that there are command blocks involved in the process. These are 48bit monsters. It might be that with the latency involved in sending the commands and the MMCs reply delay we are looking at even less than the 8khz as a replay rate. We'd need to be pushing a sample out as fast as we can read them which means the time taken for sending command blocks would cause glitches.

I'll have to do some more sums and see how long we can sustain continuous playback with some buffering. With the Arduino's 2k of SRAM we could possibly hold 1024 pre-loaded samples whilst reading a new sector from the card... My head is saying about 3.5 seconds - roughly 50 sectors of 512 bytes.

Tuesday 25 March 2008

SPI vs SPI

I've taken the software spi master code as published by Atmel and converted it into c++ to allow it to be modified. I spent a frustrating couple of days, a few moments at a time, trying to work out why MMC card access was broken when using the software master. It turned out to be that I was sampling the input on the falling edge of the clock. This was most likely due to some subtle detail lost when reading the original AVR ASM code, or dense stupidity :) I found it using, my now favourite tool in the box, the parallel port logic analyzer.

I've upgraded the software so that the grabber runs under 2K, XP and (probably) up. This is way more convenient than having to have a machine booted into DOS as I'm sure you can imagine. I've also worked on the viewer app so that you can zoom in to waveforms and see information like sample position and time - both absolute and relative to a marker.

Features, creepy in their nature, keep suggesting themselves and I'm having a hard time keeping off the code, which is presented in source and precompiled form, here.

Usual caveats apply, use at own risk, no responsibility taken for damage to potted plants ahem ahem.

Here's a couple of shots showing both the soft and hardware waveforms as snaffled up by the analyser. The ordering of channels is different as the pin assignments are different in the two modes. The cursor in both cases is poised at the moment that we expect our first byte of data back from the card. As you can see if sampling is done on the falling clock edge then a mis-read will occur. The incoming bits will be read as 0xfe,0x03 instead of 0xff,0x01. Bugger!

Soft:


'Ard:


As always, these posts are just a little flavour of the cake that is the tortured analogy for my development hobby. If you want the full recipe, then an email or comment will do the job.

And while you're considering that, there's this...

Monday 17 March 2008

Sampling

I've had a weekend away from thinking about maladjusted fuses, and now I'm feeling much, much, better. I had occasion to add some flashing pulsing coloured lighting to the cardboard rocket that I built with my boys on Saturday morning so naturally the Arduino came out. It's amazing how something as inconsequential as some modicum of lighting can transform the lifeless cardboard tube into a dynamic play area.

While I was at it I thought that it would be easy to add some silver-foil capacitive switches which would trigger sound effects. With my collection of freebie chips in front of me now I can see that I have a brace of MCP4922 DACs that I ordered from MicroChip a while ago that remained unused. These are SPI compatible 12bit DACs which should be trivial to connect up and drive.

I'm imagining a circuit with both a DAC and some form of persistent storage on an SPI bus. I'll probably go for SD/MMC as I'm familiar with driving these and I have some solid code to work with. Most likely I'll be using a software SPI master as the DACs are unbuffered. This way I can read from the MMC and write to the DACs in an interleaved fashion which will give me greater flexibility in coding and control over playback rate.

MicroChip have a relaxed attitude to people requesting samples that I wish other companies would follow. They must go by the assumption that the more people experience using their parts the better. Word of mouth is a powerful advertising medium after all. If you've never thought of doing this I recommend giving it a try. What's to lose? :)

Another company that's sample-friendly is Dallas/Maxim - makers of the ever-so useful Max232/3 serial level converters amongst other devices.

The trick with sampling is to have the time to sit and browse the entire product range. After all you might be inspired when you see that digital potentiometer...

Friday 14 March 2008

More poking...no more joy.

Hmm. The waveforms look about right.

There wasn't any software that would drive the parallel port breakout cable that I built. (Yes, I know it's ironic given my previous statement about how I wasn't prepared to solder up another DB25!!)

So I ended up crashing together an app in OpenWatcom for DOS to do the grabbing, and a C# program to display the data. Talk about a technology clash! I think at this point unless anyone can point out the problem with the attached code I'm going to concede defeat! For now ;)

ChaN's programmer is looking a lot more like the next project. I have a couple of old TTL-stuffed boards that I've been meaning to get the hot-air gun aimed towards. The need for a '299 for the programmer means I might just do that this weekend.

All this to recover a £3.25 microcontroller? No! Just for the sheer amusement!!

Here's the final draft of the code. I've been over it so many times now I've gone codeblind so if you see any howlers please keep your derision to good-humoured banter :)


// XA1 XA0   Action when XTAL1 is Pulsed
// --- --- ---------------------------
// 0 0 Load Flash or EEPROM Address (High or low address byte determined by BS1)
// 0 1 Load Data (High or Low data byte for Flash determined by BS1)
// 1 0 Load Command
// 1 1 No Action, Idle
//
const byte XA_LOAD_ADDR = B00;
const byte XA_LOAD_DATA = B01;
const byte XA_LOAD_CMND = B10;
const byte XA_NOP = B11;

const byte CMD_ERASE = B10000000;
const byte CMD_WRITE_FUSE = B01000000;
const byte CMD_READ_SIG = B00001000;
const byte CMD_READ_FUSE = B00000100;

#define DLY delayMicroseconds(100)

inline void SETB(byte x)
{
PORTB |= _BV(x);
DLY;
}
inline void SETD(byte x)
{
PORTD |= _BV(x);
DLY;
}

inline void CLRB(byte x)
{
PORTB &= ~_BV(x);
DLY;
}
inline void CLRD(byte x)
{
PORTD &= ~_BV(x);
DLY;
}


// PORTB

#define PIN_BS1 0
#define PIN_XA0 1
#define PIN_XA1 2
#define PIN_BS2 3
#define PIN_PAGEL 4
#define PIN_POWERTOCHIP 5

#define SET_BS1 SETB(PIN_BS1)
#define CLR_BS1 CLRB(PIN_BS1)

void SET_XA(byte xtal_action)
{
PORTB &= ~(_BV(PIN_XA1)|_BV(PIN_XA0));
PORTB |= xtal_action << PIN_XA0;
DLY;
}

void CLR_XA()
{
PORTB &= ~(_BV(PIN_XA1)|_BV(PIN_XA0));
DLY;
}

#define SET_BS2 SETB(PIN_BS2)
#define CLR_BS2 CLRB(PIN_BS2)

#define SET_PAGEL SETB(PIN_PAGEL)
#define CLR_PAGEL CLRB(PIN_PAGEL)

#define SET_POWERTOCHIP SETB(PIN_POWERTOCHIP)
#define CLR_POWERTOCHIP CLRB(PIN_POWERTOCHIP)


// PORTD

#define PIN_XTAL1 2
#define PIN_RDYNOTBSY 3
#define PIN_OE 4
#define PIN_WR 5

// <- PORTD3 rdy/bsy
//
void AWAIT_READY()
{
while ((PIND & _BV(PIN_RDYNOTBSY)) == 1);
long time = millis();
while ((PIND & _BV(PIN_RDYNOTBSY)) == 0);
Serial.println(millis()-time,DEC);
}

#define SET_OE SETD(PIN_OE)
#define CLR_OE CLRD(PIN_OE)

#define SET_WR SETD(PIN_WR)
#define CLR_WR CLRD(PIN_WR)

void PULSE_WR(bool wait = true);
void PULSE_WR(bool wait)
{
PORTD &= ~_BV(PIN_WR);
delayMicroseconds(100);
PORTD |= _BV(PIN_WR);
if (wait)
{
AWAIT_READY();
}
}

#define SET_XTAL1 SETD(PIN_XTAL1)
#define CLR_XTAL1 CLRD(PIN_XTAL1)

void PULSE_XTAL(bool wait = true);
void PULSE_XTAL(bool wait)
{
PORTD |= _BV(PIN_XTAL1);
delayMicroseconds(100);
PORTD &= ~_BV(PIN_XTAL1);
if (wait)
{
AWAIT_READY();
}
}

// <-> PORTD6...7 will be used as DATA6...7
// <-> PORTC0...5 will be used as DATA0...5
//
void SET_DATA_IN(void)
{
DDRC &= B11000000;
DDRD &= B00111111;
}

byte GET_DATA()
{
return (PORTC & B00111111) | (PORTD & B11000000);
}

void SET_DATA(byte data)
{
DDRC |= B00111111;
PORTC = (PORTC & B11000000) | (data & B00111111);

DDRD |= B11000000;
PORTD = (PORTD & B00111111) | (data & B11000000);

DLY;
}

//
//
//


void LOAD_COMMAND(byte command)
{
SET_XA(XA_LOAD_CMND);
SET_DATA(command);
PULSE_XTAL();
CLR_XA();
}

void LOAD_ADDRESS(byte address)
{
SET_XA(XA_LOAD_ADDR);
SET_DATA(address);
PULSE_XTAL();
CLR_XA();
}

void LOAD_DATA(byte data, bool low)
{
SET_XA(XA_LOAD_DATA);
if (!low)
{
SET_BS1;
}
SET_DATA(data);
PULSE_XTAL();
CLR_XA();
CLR_BS1;
}

byte GET_SIG(byte offset)
{
LOAD_COMMAND(CMD_READ_SIG);
LOAD_ADDRESS(offset);
SET_DATA_IN();
CLR_OE;
byte x = GET_DATA();
SET_OE;
}

inline void PROG_ENABLE(void)
{
SET_POWERTOCHIP;
delayMicroseconds(100);
}

inline void PROG_DISABLE(void)
{
CLR_POWERTOCHIP;
}

inline void PROG_ENABLE_PROPER(void)
{
SET_XTAL1;
CLR_XTAL1;
SET_XTAL1;
CLR_XTAL1;
SET_XTAL1;
CLR_XTAL1;

SET_POWERTOCHIP;
}

//
//
//

void setup(void)
{
Serial.begin(115200);

// PORTB 5...0 all outputs. Zero the ports before setting them as outputs
// to prevent spikes.
//
PORTB = 0;
DDRB |= B00111111;

// Not interested in DATA pins, they'll get set later as required
//
// PORTD0/1 are rx/tx: leave alone
// PORTD3 is input
// D6/7 are set on demand
// the rest are output
//
PORTD = 0;
DDRD = (DDRD & B11110111) | B00110100;

// bring the active low pins to their inactive state.
//
SET_OE;
SET_WR;
}


void loop(void)
{
Serial.println("Fuser 1.0 Ready.");
while(!Serial.available());
if (Serial.read() == 'p')
{
Serial.println("Sig: ");

PROG_ENABLE_PROPER();
Serial.println(GET_SIG(0),HEX);
Serial.println(GET_SIG(1),HEX);
Serial.println(GET_SIG(2),HEX);
}
else
{
Serial.println("Burn: ");

PROG_ENABLE();

delay(2);

Serial.println("Erase: ");

LOAD_COMMAND(CMD_ERASE);
PULSE_WR();

Serial.println("Lo fuse: ");

LOAD_COMMAND(CMD_WRITE_FUSE);
LOAD_DATA(0xE1, true);
PULSE_WR();

Serial.println("Hi fuse: ");

LOAD_COMMAND(CMD_WRITE_FUSE);
LOAD_DATA(0x99, false);
PULSE_WR();
}

PROG_DISABLE();
}

Thursday 13 March 2008

Heroic failure!

I've slapped it all into shape and electrically things look solid - but no joy.

Result: Heroic failure!

I'll probably poke around a bit more before consigning this to the 'nearly works' pile as I get the same nonsense results with a known good chip. So you never know.

This might be the time to get my cheap-as-chips parallel-port logic analyzer built... It's easy to see how my project stack grows :)

Spaghetti

Here's the basic harness wired into the Arduino board. The three-pin header is the power connector, ready to accept +12, gnd and +5v connections from the transistor switch contraption that I'll put together on a breadboard later.

I used very fine enamelled wire cohabiting the protoboard holes with the socket pins. A good application of heat and solder later - bingo!

To connect the wire to the header pins I put a tiny blob of solder on the iron tip and insert the cut end of the wire. Within a second or so the enamel melts and you can see the solder flow around the wire. It's then ready to fix it to its destination.

And what better to listen to whilst soldering than some very fine oldskool belgian skizzo??

Wednesday 12 March 2008

Arduino to planet fuser

Here's how I plan to wire the fuser board to my Diecimila:

[Apologies to some viewers: I've just looked here using IE7 and the diagram is flummoxed. Adjust the font size downwards and things will look a little better]


Target - atmega32
____ ____
| pb0 =| U |= pa0 | bs2
| 1 =| |=
/ | 2 =| |=
DATA | 3 =| |=
\ | 4 =| |=
| 5 =| |=
| 6 =| |=
| 7 =| |=
reset =| |= aref
vcc =| |= gnd
gnd =| |= avcc
=| |=
xtal1 | xtal1 =| |=
pd0 =| |=
rdy/~bsy | 1 =| |=
~oe | 2 =| |=
~wr | 3 =| |=
bs1 | 4 =| |=
xa0 | 5 =| |=
xa1 | 6 =|_________|= pd7 | pagel




Diecimila - pin assignment

ANA-IN5 |o| DATA5
4 |o| 4
3 |o| 3
2 |o| 2
1 |o| 1
0 |o| 0

D0 [rx] |o| -
1 [tx] |o| -
2 |o| xtal1
3 |o| rdy/~bsy
4 |o| ~oe
5 |o| ~wr
6 |o| DATA6
7 |o| DATA7

D8 |o| bs1
9 |o| xa0
10 |o| xa1
11 |o| bs2
12 |o| pagel
13 |o| juice
gnd |o| -
aref |o| -


The power switching is going to be implemented identically to how ChaN implements his. I'm lazy that way! Which is to say, The Good Way. Why reinvent?!

Tuesday 11 March 2008

The plan to blow all the fuses

The aims of this mini project are quite humble: Build an out-of-circuit parallel mode programmer lite which can whip a borked avr back into something like its factory condition ready for in-circuit programming. At the moment I'm going to concentrate on one chip only and if it all works, and there's sufficient interest, I'll think about expanding the scope.

I'm not going to go into depth - this is just really a sketch of what's happening in my bonce right now. I'll be abstracting quite heavily and using my own terms because it doesn't make sense to just repeat all the detail in the datasheets. Code and schematics will be posted as and when available.

All the information I needed for V0.1 is in the atmega32 data sheet. It looks like it'll be quite easy to reset the fuses.

I've come across a design for a parallel port programmer which looks like it would be simple to build but I don't relish the thought of soldering up yet another DB25 connector. So I've decided to drive this project with an Arduino. I'm imagining nothing more than a socket on some stripboard connected to some header pins that will mate with my Diecimila, some power regulation and switching. I would have built a shield but for whatever reason the designers of the board decided to use a non-standard spacing between 2 sets of sockets which makes building shields on protoboard difficult. Grrrr.

The procedure will simply be:- Erase chip, reset fuses. Job done. The chip erase step has the effect of resetting any lock bits. As one of the locks controls our ability to write fuses this is essential. The state of the EEPROM will be indeterminate after this step, however, as there is a fuse controlling whether the erase cycle also clears EEPROM. And we don't know how this is set.

There are two ways to enter high voltage programming mode. The first involves gently tickling a port but it relies upon the clock settings for the chip being set to external. I think this might have been how I borked my chip, but as I couldn't ressurect it with an attached clock signal this could well not not be the case. So I'm going to go for the second method. Brute force!

Powering up the target with 12v @ 250uA on its reset line, logic low on four specific pins and a wait of 100uS should be enough for it to become willing to accept programming commands. Which are loaded thus:

  • Set the 'mode select' pins to 'load command'.
  • Present the desired command to the DATA pins.
  • Pulse the clock pin to latch the command. All pulses need to last at least 250nS.

Commands are executed by giving a pulse to ~wr. Progress can be tracked by monitoring the rdy/~bsy pin, which will go low until the action has finished.

Addresses are loaded in the same manner, we just substitute 'load address' as the mode.

All the dirty details of the required pulsing is clearly outlined in the timing waveform diagrams in the data sheet.

In order to check how the hardware and code is functioning I'll read the signature bytes of the chip as my first step.

  • Load the 'read signature' command.
  • Load the address of the desired signature byte, 0-2, to be accessed.
  • Set ~oe low.
  • The target presents the requested byte on the DATA bits.
  • Set ~oe high.

When this is returning what I expect then we'll start on the fuses. First though - some erasing.

  • Load 'erase chip' command.
  • Pulse ~wr.
  • Wait for the command to complete. This will take between 7-9mS.

Once this is done happily then we're ready to blow the fuses! We'll do low, then high.

  • Load the 'write fuse' command.
  • Load DATA with the low fuse byte.
  • Give ~wr a pulse and wait for rdy/~bsy to indicate the process has completed.
For general write operations this will take between 3-5mS.

  • Load the 'write fuse' command.
  • Load DATA with the high fuse value.
  • Give ~wr a pulse and wait for rdy/~bsy.

And that should be that!

I'll let you know how I get on :)

Here's an untested arduino sketch that I've hacked up, which should give a better idea of the specifics. It should be read in concert with the data sheet chapter 'Memory programming'.


/* CUT DUE TO ITS TEMPORARY NATURE - PLEASE SEE LATER POST */

Friday 7 March 2008

Retro Heaven



I've just hacked together an MMC2IEC interface on my breadboard. Thanks Lars!


It's a serial device that pretends to be a commodore 1541 disk drive. It uses SD card storage and disk images to deliver retrogaming goodness to my '128. This is #4 of 4 in my old computers that I've added flash storage to series. Seriously - if you have old computers and a few spare hours it's worth the solder burns.

Having got that device working in its original Lars form I noticed that the open source design has been picked up by a couple of other bods who have taken it to another level. Details are scant, unless you speak German, but I've managed to scrape enough information together to upgrade Lars' original hardware design with some improved firmware called SD2IEC. This supports many more features such as high capacity cards, disk switching, some fastloaders and JiffyDos which should speed things up considerably. Which if you've ever used a C64 disk system you'll know is a must. Some details in English are here.

During the process of upgrading I managed to half-brick an AVR chip. Unlike PICs they are easy to render unprogrammable by changing their configuration fuses in an an unfavourable way. My mistake was not reading the output of this avr fuse calculator carefully enough. Instead of making the AVR desire an external resonator, I made it lust for Clock. Which is different. I've done this a couple of times before with some 168s. In the aftermath of that embarrassing mistake I did some research and found that usually these chips can be resurrected.

If you program a chip to expect a clock, then it should be possible to reprogram it as long as the relevant signal is present. I usually program chips in a little home-made ISP cradle which doesn't have a clock available. For the 168s it proved possible to get them going again by dropping them in my Deicimilia board before attempting to reset the fuses.

The mega32 hasn't responded to this trick though. I used an Arduino to provide what I thought was a good clock to no avail. It's going to need something a little more ... high voltage. AVRs can be programmed using SPI in-circuit, or by more conventional parallel means out-of-circuit. Programming serially you need a well configured support circuit for the chip. Using a parallel programmer you can reset fuses that would prevent SPI from working. So here's my next project - an avr fuse reset device. A cut-down high voltage parallel programmer that only has one job - to reset the fuses to a factory fresh state. Insert AVR, press button, bingo! At least in theory :)

I'm off to grab some of Atmel's brilliant application notes. Then it'll be time for a nice sit down and some strawberry cheesecake.

Saturday 1 March 2008

Hello, world!


I want somewhere to doodle about the mucking around I'm doing with Arduino. This, as you might well have guessed by now, is it.

If you don't dig digital, if you aren't amorous with analog... Oh forget it. It's going to be spoddy!

I'm off to town for a vanilla slice, but when I get back I'll play with this blog thing a little more and start posting about the microcontroller hackery I've been getting up to in the last few months or so.

If you like POV toys, LED tokens of love, zx81s, shoehorning data on and off MMC cards and FSK cassette interfaces then I'm sure you'll be riveted!


If not - well, there will be cake :)

Contact Form

Name

Email *

Message *

This is how we do it

MMC (9) acorn atom (7) zx81 (7) sord m5 (6) arduino (5) multicart (5) Sord (4) tatung einstein (4) Atari 800 (3) Chuckie egg (3) M5 (3) M5Multi (3) c128 (3) sd card (3) sd2iec (3) sio2sd (3) 6502 (2) Max6956 (2) QL (2) RCM (2) assembler (2) avr (2) c64 (2) cadsoft eagle (2) eeprom (2) einSDein (2) mmbeeb (2) multi-cart (2) spi (2) system 80 (2) ufat2 (2) vic20 (2) video genie (2) 6502 second processor (1) 6522 (1) 8255 (1) Acorn BBC Micro (1) Apple 2e (1) Apple ][ 2 two (1) BBC 6502 second processor (1) BBC micro (1) DevicePrint (1) Double Choc Chip Muffins (1) FAT (1) IO (1) Jupiter Ace (1) LED (1) Master 128 (1) PCB (1) PIC (1) POV (1) PROGMEM (1) Pineapple (1) ST (1) Spectrum 128 (1) antex (1) arcade spinner (1) arduino shield (1) atari (1) atmel (1) bakewell tart (1) beer (1) bird's nest (1) bitbucket (1) brokenated XC special (1) cake (1) cassette (1) cassette interface (1) colecovision (1) compact flash (1) convert (1) dac (1) de-yellowing (1) dev cart (1) eaca (1) efficient (1) einsdein. z80 (1) esp32 (1) esp8266 (1) eye strain (1) failosophy (1) filesystem (1) finally (1) fram (1) french polishing (1) fuse (1) fuses (1) games (1) gaming (1) github (1) glue (1) google chrome (1) heroic failure (1) high voltage programming (1) hot irons (1) if (1) jiffydos (1) joey beltram (1) lego robot (1) library (1) lying (1) machine code (1) matron (1) microcode (1) mmc interface (1) mmc2iec (1) mmm (1) mouse guts (1) oscilloscopes (1) pcm (1) pic32mx (1) porn (1) proto shield (1) purple (1) repo (1) retro computer museum (1) retro hard-on (1) rom box (1) sd (1) sd-x (1) sd2mmc (1) seadragon (1) silliness (1) small (1) software master (1) soldering (1) spi software master (1) stray capacitance (1) string (1) techadventure (1) test equipment porn (1) ts1000 (1) turtle cheesecake (1) tweaking (1) vc20 (1) video head (1) video ram replacement (1) weewee (1) wingasm (1) wire library (1) wodges of IO (1) xilinx cpld (1) yellowing (1) z80 (1) zx spectrum (1) zxpander (1)
Unless otherwise stated all of the original work presented here is:

Creative Commons License
Licensed under a Creative Commons Attribution-Noncommercial 2.5 Generic License.

The work of others where referenced will be attributed appropriately. If I've failed to do this please let me know.