This information is going to end up being published in parts as and when I have the time to write them. Here's a fair chunk to get started.
Hacking Gamecube Memory Cards For Fun (but no Profit)
'sclaimer: all information presented here is for educational purposes only. Any damage to anything whatsoever caused by implementing any of the techniques in this writeup is at the reader's own risk.
A while ago, I was chatting with Nate on IRC and the idea came up of reverse engineering the Metroid Prime save game format. It was an idea I had after Mills accidentally loaded an NTSC save using that dodgy Korean copy of Prime he has, and all hell broke loose. In particular we wondered if it might be possible to examine the save game data and deduce what the current save time was, so theoretically runners would be able to know the precise metrics of their run by just pulling the relevant data out of a saved game file. Radix was also keen on backing up his Animal Crossing game, something which the Gamecube's default file copier refused to do.
I spent some time wandering around the internet looking for information. The most useful document was this one, as usual penned by an open source team, the Gamecube Linux team in this instance. The section on memory cards revealed that the card basically uses a simple variant of the Serial Programming Interface (SPI), the hardware protocol that is used for applications such as reading and writing from digital camera memory cards such as SD or MMC. The Gamecube cards contained a single Serial Flash ROM chip manufactured by Macronix. Information about this chip was not forthcoming - although the Macronix site provided data sheets for similar integrated circuits, there were none for the precise part number of the chip used by Nintendo. It looked like the chip was a custom one, although the level to which this applied didn't become apparent until later on.
Having read the relevant part of the Gamecube document and the Macronix data sheets, I was feeling confident that I could build something reasonably trivial that would at least have a chance of talking to a memory card extracted from a Gamecube. Because the memory cards use the SPI bus, it would probably be possible to hook them up to a PC MMC card reader or similar, but there was a problem with that approach - the total lack of debugging capacity. I could already see that I would plug my badly-connected card into my buggy piece of homebrew software and would simply be stuck when the two did not work together, with no means of investigating the problem. There are tools such as logic analysers which one can use to tap a bus such as that between the card and its reader and debug what is going on, but - in short - I don't have one. The fact of the matter was that I was going to have to build the interface electronics myself and make it possible to inspect whether the circuit was working by incorporating a set of diagnostic LEDs. I knew from the Macronix information that their serial flash ROM chips had no minimum clock speed, so in theory there was nothing stopping me from clocking the interface at about 0.5 Hertz and seeing what the circuit was doing just by writing down which lights were on.
From research done by the GC Linux team I knew that there would be six connections I would need to make to the memory card. These are: Power (+Volts), Ground (zero Volts), Chip Select (aka CS), Clock (aka CLK), Data In (DI) and Data Out (DO). This meant that whatever interface I built was going to need to have three controllable output lines and one input line. The SPI interface is discussed later on, so don't worry about this yet.
Unfortunately the ports on the PC these days are not well suited to interfacing with homebrew electronics. To build a USB device or PCI card would be far more work than I was prepared to put in (although I did consider it). The PC's RS232 (serial, COM) ports were an option, but since I would be using something that didn't remotely resemble a normal serial interface, it would probably be necessary to resort to some low-level hacking to convince it to speak the language I needed (this practice of abusing a port's handshaking lines to make it behave like something it isn't used to be known as "bit-banging"). The parallel port (Centronics, printer), was also an option, but that also comes with some problems - ignoring modern innovations like EPP, it's a one-way interface that doesn't have much support for reading data. Both of these ports I could probably have persuaded to work, but equally both of them would probably have required some cajoling. I think there were other reasons why I couldn't or didn't use either port, but I've forgotten my justifications now.
What I really needed was a port on a computer that was actually designed for doing what I was doing - an easily controllable general purpose input/output port with multiple input and output lines. It is possible to buy PCI cards for PCs that provide this ability, but I wasn't going to go down that route. It would be nice if we still lived in the days of hobbyist computers that were designed for interfacing to projects, I thought, where the designers didn't actually know yet what the average Joe would want a computer for, and provided for all eventualities.
The British Broadcasting Corporation announced in the very early 1980s in Britain that they would be beginning a Computer Literacy Project, which would consist of television and radio programmes describing the use of a standardised microcomputer that they would endorse. The history of the BBC Micro is long and varied - the winning design was by Acorn, a company who up until then had been manufacturing smallish runs of microcomputers for absolutely hardcore hobbyist geeks. Legend has it that the BBC Micro's original operating system (stored on 16K of ROM, kids) was written in one up-all-night code orgy, the evening before the company were due to show the machine to the BBC. (Just by the way, if you own an iPod or a Nintendo DS, you own microprocessors that have Acorn pedigree - ARM was originally a division of Acorn, and stood for "Acorn Risc Machine".) Ours was one of the first families in the UK to get one of the micros, a "Model A" with 16K of RAM and no disc drive. It is a shame that the original machine we owned broke down and was disposed of, because it was an extremely rare early model with all microchips socketed and a slightly different coloured case, which would no doubt be of interest to a collector today - heck, it would be of interest to me today. The project was far more successful than the BBC expected, originally anticipating uptake of the machines to be in the few thousands range (Acorn's machine, or the "Beeb" as it was known, would eventually shift one million units in the UK). I have extremely fond memories of these distinctive little computers, perfection that they were, and could talk about them literally for hours, but I need to call a halt to this paragraph now.
The BBC Micro had a "user port", which was really a 6522 TTL integrated circuit connected to a socket on the back of the machine. The user port had eight bi-directional general purpose logic lines that could be controlled or read very easily from user software written in either BASIC or assembly language. It also sported an RS423 serial interface, compatible with the PC's RS232 serial interface. I could write an assembly language program on a BBC Micro to talk to the gamecube memory card using the user port, and ferry the resulting data to and from a PC using a completely standard serial interface. It would be slow, as the BBC wouldn't communicate reliably at speeds greater than 9,600 baud, but it would be fast enough, and it would work.
While I was waiting for the computer to arrive (given the number of schools that had Beebs in them, finding them on eBay is like finding leaves on trees), I turned my attention to the interface electronics. One problem is that because the BBC was old, the user port was a five Volt device rather than a three (or 3.3) Volt one. Therefore my electronics needed to do a few simple things - convert the voltage levels in both directions (to 3V for the card and to 5V for the BBC), provide a 3V power supply for the card, and allow me to inspect all the data lines by lighting LEDs.
This was my original circuit, although the design changed later (more on that in a minute). The BBC micro is assumed to be on the left, and the memory card on the right.

IC1 is a voltage regulator, and VR1 (a trimmer) is adjusted until the 3V line is at three Volts. CLK, CS and Serial In are all lines carrying data left to right; Serial Out carries data right to left (as indicated by the arrows). IC2 is a 3V/5V logic buffer that steps the three Volt output of the memory card up to five Volts to drive the BBC's user port input. All the 5V lines are connected to NPN transistors (I used 2N2222A) enabling them to drive the diagnostic LEDs. To step the user port voltage down from 5V to 3V, I opted for the most basic solution of using voltage dividers (such as the R3/R4 pair). This did not quite work as I expected. I was making the assumption that when the BBC's output was at five Volts (logic high) it would be connected directly to the BBC's internal +5V supply (as it would in CMOS). Because the BBC used old TTL (transistor-transistor logic), a logic high was actually connected to +5V through a 2K resistor, which messed up my divider calculations, but the problem was easily solved simply by replacing R3, R5 and R7 with pieces of wire, so I was using the BBC's 2K resistors instead of my own as the top half of the divider.
I designed the circuit to be built on stripboard. Again, modifications occurred after this was drawn, so it's not quite what was eventually used.

(X is a track break)
The intention was to connect the memory card to a socket at the top-right of the layout. I had already experimented a little with ways to make connections to a memory card, preferring if possible to build something that could be used with any card. My first attempt looked like this:

and consisted of an old piece of plastic with a layer of aluminium foil glued to it, and cut with a knife to match the positions of the tracks on a memory card. It had pieces of rubber glued to the back to attempt to keep the contacts firm. After hours of prodding with a resistance meter I declared it a total failure and from that point on I decided to just crack open the cards and add my own external connector soldered via a cable direct to the memory card's PCB. Large numbers of my memory cards now, as a result, have appendages. Nintendo's cards use some horrible proprietary screw head, too, so many are also bound closed with sticky tape.
The computer arrived eventually, although I nearly broke it an hour after opening the box. It turned out to be a relic from some sort of cheap TV studio, as it had been modified to allow video genlocking from an external source, and had an evil ROM fitted. Removing the modification broke the computer, so I had a rather unpleasant hour trying to figure out why (a track had been cut on the motherboard to allow installation of the genlock mod). The BBC was connected to the serial port of my PC via a special cable I had already made (not easy - you try finding somewhere that sells 5-pin "domino" DIN plugs these days). Here's a pretty picture of the device.

Once I'd built and debugged the circuit (can you debug a circuit ?) and had tested it with some simple BASIC programs, it was time to think about writing some full-blown software to communicate with the card at speed. Because the BBC's CPU was 8-bit and only ran at a few MHz (it was in fact a very similar CPU to that used in the NES), and because C implementations on such old home computers were rare, expensive and crap, I would have to use 6502 assembler.
This is a BBC BASIC program which, when run, will assemble a machine code routine into memory at address &5C01. Because I had no disc drive for the BBC, the easiest way to get this program into its memory was to just do a "*FX2,1", which told it to accept all input from the serial port instead of the keyboard. I could then just pipe my program to /dev/ttyS0 on my Linux PC. This also made editing the code much easier than if I'd had to do it on the micro. Control is returned to the keyboard by the *FX2,0 right at the end of the script above. The BASIC program would be RUN and then the resulting machine code routine invoked using CALL &5C01.
The software essentially listens to the serial port (lines 50-70) and waits for a byte to arrive over the RS232 from the PC (the command byte). Once a byte is received, the code goes through a switch/case style construction (lines 140-410) to decode the command byte and decide what to do. The command bytes correspond to the instruction bytes for talking to the memory card via its SPI interface: clear status (0x89); read status (0x83); read block (0x52); write block (0xF2) and get ID (0x85). Depending on the byte received, a subroutine is called. Some of them, such as readblock (line 4000) will expect extra bytes to arrive over the serial port (in this case, the PC must tell the software the block that it wishes to be read). The subroutines make heavy use of other routines such as sendcmd (line 6000) which sends a byte to the memory card; getsout (line 6300) which reads a byte from the memory card and putserial (line 8000) which sends bytes received from the memory card over the serial interface where they can be collected by the PC. The subroutine delay at 7000 is used to introduce varying levels of artifical delay so that the diagnostic LEDs may be inspected.
To understand what it does to the signal lines, it is necessary to have a look at how the SPI bus works.
... to be continued !
Hacking Gamecube Memory Cards For Fun (but no Profit)
'sclaimer: all information presented here is for educational purposes only. Any damage to anything whatsoever caused by implementing any of the techniques in this writeup is at the reader's own risk.
A while ago, I was chatting with Nate on IRC and the idea came up of reverse engineering the Metroid Prime save game format. It was an idea I had after Mills accidentally loaded an NTSC save using that dodgy Korean copy of Prime he has, and all hell broke loose. In particular we wondered if it might be possible to examine the save game data and deduce what the current save time was, so theoretically runners would be able to know the precise metrics of their run by just pulling the relevant data out of a saved game file. Radix was also keen on backing up his Animal Crossing game, something which the Gamecube's default file copier refused to do.
I spent some time wandering around the internet looking for information. The most useful document was this one, as usual penned by an open source team, the Gamecube Linux team in this instance. The section on memory cards revealed that the card basically uses a simple variant of the Serial Programming Interface (SPI), the hardware protocol that is used for applications such as reading and writing from digital camera memory cards such as SD or MMC. The Gamecube cards contained a single Serial Flash ROM chip manufactured by Macronix. Information about this chip was not forthcoming - although the Macronix site provided data sheets for similar integrated circuits, there were none for the precise part number of the chip used by Nintendo. It looked like the chip was a custom one, although the level to which this applied didn't become apparent until later on.
Having read the relevant part of the Gamecube document and the Macronix data sheets, I was feeling confident that I could build something reasonably trivial that would at least have a chance of talking to a memory card extracted from a Gamecube. Because the memory cards use the SPI bus, it would probably be possible to hook them up to a PC MMC card reader or similar, but there was a problem with that approach - the total lack of debugging capacity. I could already see that I would plug my badly-connected card into my buggy piece of homebrew software and would simply be stuck when the two did not work together, with no means of investigating the problem. There are tools such as logic analysers which one can use to tap a bus such as that between the card and its reader and debug what is going on, but - in short - I don't have one. The fact of the matter was that I was going to have to build the interface electronics myself and make it possible to inspect whether the circuit was working by incorporating a set of diagnostic LEDs. I knew from the Macronix information that their serial flash ROM chips had no minimum clock speed, so in theory there was nothing stopping me from clocking the interface at about 0.5 Hertz and seeing what the circuit was doing just by writing down which lights were on.
From research done by the GC Linux team I knew that there would be six connections I would need to make to the memory card. These are: Power (+Volts), Ground (zero Volts), Chip Select (aka CS), Clock (aka CLK), Data In (DI) and Data Out (DO). This meant that whatever interface I built was going to need to have three controllable output lines and one input line. The SPI interface is discussed later on, so don't worry about this yet.
Unfortunately the ports on the PC these days are not well suited to interfacing with homebrew electronics. To build a USB device or PCI card would be far more work than I was prepared to put in (although I did consider it). The PC's RS232 (serial, COM) ports were an option, but since I would be using something that didn't remotely resemble a normal serial interface, it would probably be necessary to resort to some low-level hacking to convince it to speak the language I needed (this practice of abusing a port's handshaking lines to make it behave like something it isn't used to be known as "bit-banging"). The parallel port (Centronics, printer), was also an option, but that also comes with some problems - ignoring modern innovations like EPP, it's a one-way interface that doesn't have much support for reading data. Both of these ports I could probably have persuaded to work, but equally both of them would probably have required some cajoling. I think there were other reasons why I couldn't or didn't use either port, but I've forgotten my justifications now.
What I really needed was a port on a computer that was actually designed for doing what I was doing - an easily controllable general purpose input/output port with multiple input and output lines. It is possible to buy PCI cards for PCs that provide this ability, but I wasn't going to go down that route. It would be nice if we still lived in the days of hobbyist computers that were designed for interfacing to projects, I thought, where the designers didn't actually know yet what the average Joe would want a computer for, and provided for all eventualities.
The British Broadcasting Corporation announced in the very early 1980s in Britain that they would be beginning a Computer Literacy Project, which would consist of television and radio programmes describing the use of a standardised microcomputer that they would endorse. The history of the BBC Micro is long and varied - the winning design was by Acorn, a company who up until then had been manufacturing smallish runs of microcomputers for absolutely hardcore hobbyist geeks. Legend has it that the BBC Micro's original operating system (stored on 16K of ROM, kids) was written in one up-all-night code orgy, the evening before the company were due to show the machine to the BBC. (Just by the way, if you own an iPod or a Nintendo DS, you own microprocessors that have Acorn pedigree - ARM was originally a division of Acorn, and stood for "Acorn Risc Machine".) Ours was one of the first families in the UK to get one of the micros, a "Model A" with 16K of RAM and no disc drive. It is a shame that the original machine we owned broke down and was disposed of, because it was an extremely rare early model with all microchips socketed and a slightly different coloured case, which would no doubt be of interest to a collector today - heck, it would be of interest to me today. The project was far more successful than the BBC expected, originally anticipating uptake of the machines to be in the few thousands range (Acorn's machine, or the "Beeb" as it was known, would eventually shift one million units in the UK). I have extremely fond memories of these distinctive little computers, perfection that they were, and could talk about them literally for hours, but I need to call a halt to this paragraph now.
The BBC Micro had a "user port", which was really a 6522 TTL integrated circuit connected to a socket on the back of the machine. The user port had eight bi-directional general purpose logic lines that could be controlled or read very easily from user software written in either BASIC or assembly language. It also sported an RS423 serial interface, compatible with the PC's RS232 serial interface. I could write an assembly language program on a BBC Micro to talk to the gamecube memory card using the user port, and ferry the resulting data to and from a PC using a completely standard serial interface. It would be slow, as the BBC wouldn't communicate reliably at speeds greater than 9,600 baud, but it would be fast enough, and it would work.
While I was waiting for the computer to arrive (given the number of schools that had Beebs in them, finding them on eBay is like finding leaves on trees), I turned my attention to the interface electronics. One problem is that because the BBC was old, the user port was a five Volt device rather than a three (or 3.3) Volt one. Therefore my electronics needed to do a few simple things - convert the voltage levels in both directions (to 3V for the card and to 5V for the BBC), provide a 3V power supply for the card, and allow me to inspect all the data lines by lighting LEDs.
This was my original circuit, although the design changed later (more on that in a minute). The BBC micro is assumed to be on the left, and the memory card on the right.

IC1 is a voltage regulator, and VR1 (a trimmer) is adjusted until the 3V line is at three Volts. CLK, CS and Serial In are all lines carrying data left to right; Serial Out carries data right to left (as indicated by the arrows). IC2 is a 3V/5V logic buffer that steps the three Volt output of the memory card up to five Volts to drive the BBC's user port input. All the 5V lines are connected to NPN transistors (I used 2N2222A) enabling them to drive the diagnostic LEDs. To step the user port voltage down from 5V to 3V, I opted for the most basic solution of using voltage dividers (such as the R3/R4 pair). This did not quite work as I expected. I was making the assumption that when the BBC's output was at five Volts (logic high) it would be connected directly to the BBC's internal +5V supply (as it would in CMOS). Because the BBC used old TTL (transistor-transistor logic), a logic high was actually connected to +5V through a 2K resistor, which messed up my divider calculations, but the problem was easily solved simply by replacing R3, R5 and R7 with pieces of wire, so I was using the BBC's 2K resistors instead of my own as the top half of the divider.
I designed the circuit to be built on stripboard. Again, modifications occurred after this was drawn, so it's not quite what was eventually used.

(X is a track break)
The intention was to connect the memory card to a socket at the top-right of the layout. I had already experimented a little with ways to make connections to a memory card, preferring if possible to build something that could be used with any card. My first attempt looked like this:

and consisted of an old piece of plastic with a layer of aluminium foil glued to it, and cut with a knife to match the positions of the tracks on a memory card. It had pieces of rubber glued to the back to attempt to keep the contacts firm. After hours of prodding with a resistance meter I declared it a total failure and from that point on I decided to just crack open the cards and add my own external connector soldered via a cable direct to the memory card's PCB. Large numbers of my memory cards now, as a result, have appendages. Nintendo's cards use some horrible proprietary screw head, too, so many are also bound closed with sticky tape.
The computer arrived eventually, although I nearly broke it an hour after opening the box. It turned out to be a relic from some sort of cheap TV studio, as it had been modified to allow video genlocking from an external source, and had an evil ROM fitted. Removing the modification broke the computer, so I had a rather unpleasant hour trying to figure out why (a track had been cut on the motherboard to allow installation of the genlock mod). The BBC was connected to the serial port of my PC via a special cable I had already made (not easy - you try finding somewhere that sells 5-pin "domino" DIN plugs these days). Here's a pretty picture of the device.

Once I'd built and debugged the circuit (can you debug a circuit ?) and had tested it with some simple BASIC programs, it was time to think about writing some full-blown software to communicate with the card at speed. Because the BBC's CPU was 8-bit and only ran at a few MHz (it was in fact a very similar CPU to that used in the NES), and because C implementations on such old home computers were rare, expensive and crap, I would have to use 6502 assembler.
Code:
*FX3,6 PAGE=&E00 *TAPE NEW REM reserve 8k for machine code routines HIMEM=&5C00 10MODE 7 15*FX2,2 16ZP=&70 20FOR PASS=0 TO 3 STEP 3 30P%=&5C01 40[OPT PASS 43.start 44CLD \ ensure decimal reg is cleared 45LDA #2:LDX #2:JSR &FFF4 \ enable serial port 46LDA #15:LDX #0:JSR &FFF4 \ flush buffers 47LDA #7:STA &FE62 \ user port DDR 48JSR resetuserport 49JSR &FFE7:LDX #(ssready AND &FF):LDA #(ssready DIV &100):JSR print:JSR &FFE7 \ print "ready" line 50.idleloop \ wait for something to happen on the serial port 60LDX#1:LDA#&91:JSR&FFF4 \ OSBYTE call 70BCS idleloop \ carry flag set, continue looping 100LDX #(ssgotbyte AND &FF):LDA #(ssgotbyte DIV &100):JSR print \ print "got byte" line 130TYA:PHA \ preserve byte read from serial port 135JSR bytetoasc:TXA:JSR &FFEE:TYA:JSR &FFEE:JSR &FFE7 \ print it 136PLA \ get byte back 140CMP #&89 \ clear status 150BNE bytecheck1 160JSR clearstatus 170JMP finish 180.bytecheck1 190CMP #&83 \ read status 200BNE bytecheck2 210JSR readstatus 220JMP finish 230.bytecheck2 240CMP #&52 \ read block 250BNE bytecheck3 260JSR readblock 270JMP finish 280.bytecheck3 290CMP #&F2 \ write block 300BNE bytecheck4 310JSR writeblock 320JMP finish 330.bytecheck4 340CMP #&85 \ get id 345BNE failed 350JSR readid 360JMP finish 400.failed 410LDX #(ssunrecognisedbyte AND &FF):LDA #(ssunrecognisedbyte DIV &100):JSR print:JSR&FFE7 \ print error 490.finish 495JMP start 500RTS \ return to BASIC 1030.print \ expect high byte in A, low byte in X 1050STA temp \ save A 1060TYA:PHA \ preserve Y-reg 1070LDA temp \ retrieve A 1080STA ZP+1:STX ZP \ address of string now stored in zero page 1090LDY #0 \ counter 1100.printloop 1110LDA (ZP),Y \ get character (zero page indirect indexed) 1115BEQ printend \ null terminator ? quit 1120JSR &FFEE \ OSWRCH call 1130INY \ Y++ 1140JMP printloop 1150.printend \ clean up, RTS 1160PLA:TAY:RTS 1300.bytetoasc \ convert byte to two-byte ASCII string, take byte in A and return results in X then Y 1305PHA:PHA 1310AND#&0F:CMP#10:BMI asclodone:ADC#6 1320.asclodone 1330ADC#&30:TAY 1340PLA:LSR A:LSR A:LSR A:LSR A:CMP#10:BMI aschidone:ADC#6 1350.aschidone 1360ADC#&30:TAX:PLA:RTS 2000.clearstatus 2010LDX #(ssclearstatus AND &FF):LDA #(ssclearstatus DIV &100):JSR print:JSR &FFE7 \ print message 2020LDA #&89:JSR sendcmd \ send command to uport 2040RTS 3000.readstatus 3005LDX #(ssreadstatus AND &FF):LDA #(ssreadstatus DIV &100):JSR print:JSR &FFE7 \ print message 3010LDA #&83:JSR sendcmd \ send command to uport 3020LDA #0:JSR sendcmd \ send dummy byte to uport 3030JSR getsout \ read byte of serial data 3035PHA 3040LDX #(sscardbyte AND &FF):LDA #(sscardbyte DIV &100):JSR print \ print message 3045PLA:PHA 3050JSR bytetoasc:TXA:JSR &FFEE:TYA:JSR &FFEE:JSR &FFE7 \ print it 3060PLA:JSR putserial \ send to RS423 3200RTS 4000.readblock 4010LDX #(ssreadblock AND &FF):LDA #(ssreadblock DIV &100):JSR print \ print message 4015LDA#&52:JSR sendcmd \ send the 0x52 command byte 4016LDY #4 \ counter 4020.readblockbyteloop \ expect four more bytes on serial port 4030TYA:PHA \ preserve Y 4040.readblockwaitloop \ wait for something to happen on the serial port 4050LDX#1:LDA#&91:JSR&FFF4 \ OSBYTE call 4060BCS readblockwaitloop \ carry flag set, continue looping 4062STY temp \ write the byte we read to temporary storage 4063TYA:JSR bytetoasc:TXA:JSR &FFEE:TYA:JSR &FFEE \ print byte 4064LDA temp:JSR sendcmd \ send offset byte to memory card 4066PLA:TAY \ restore counter 4080DEY 4090BNE readblockbyteloop 4095JSR &FFE7 \ print newline 4100LDA #0:JSR sendcmd:JSR sendcmd:JSR sendcmd:JSR sendcmd \ send four null bytes 4110LDX#2 4120.readblockouterloop 4130LDY#0 4135.readblockinnerloop 4136TYA:PHA:TXA:PHA 4140JSR getsout \ read a byte from card 4150JSR putserial \ send to RS423 4155PLA:TAX:PLA:TAY 4160DEY 4170BNE readblockinnerloop 4180DEX 4190BNE readblockouterloop 4500RTS 4600.readid 4610LDX #(ssreadid AND &FF):LDA #(ssreadid DIV &100):JSR print \ print message 4620LDA#&85:JSR sendcmd:LDA#0:JSR sendcmd \ send command bytes 4630LDY#2 \ counter 4640.readidloop 4645TYA:PHA 4650JSR getsout 4660PHA \ preserve result 4670JSR bytetoasc:TXA:JSR &FFEE:TYA:JSR &FFEE \ print it 4680PLA:JSR putserial \ restore and send to RS423 4690PLA:TAY \ get counter value back 4700DEY \ decrement 4710BNE readidloop \ loop 4715JSR&FFE7 \ newline 4720RTS \ quit 5000.writeblock 5005LDX #(sswriteblock AND &FF):LDA #(sswriteblock DIV &100):JSR print:JSR &FFE7 \ print message 5010RTS 6000.sendcmd \ send command to uport, expect cbyte in A 6010STA temp \ save A 6020TYA:PHA:TXA:PHA \ preserve regs 6030LDA &FE60:AND#5:STA &FE60 \ deassert CS (if asserted, doesn't matter if not) 6040LDY temp \ retrieve byte (in Y) 6041LDX#8 \ counter 6042.sendcmdloop 6045JSR delay 6050TYA:ASL A:TAY \ move top bit into carry 6060BCC sendcmdcarrylo:LDA #4:STA &FE60:JMP sendcmdcont \ load a 1 onto the serial in 6065.sendcmdcarrylo 6070LDA #0:STA &FE60 \ or load a 0 onto the serial in 6080.sendcmdcont 6081JSR delay 6082LDA &FE60:ORA#1:STA &FE60 \ assert clock 6083JSR delay:JSR delay 6084LDA &FE60:AND #&FE:STA &FE60 \ deassert clock 6187DEX 6190BNE sendcmdloop 6200PLA:TAX:PLA:TAY:LDA temp:RTS \ clean up and return 6300.getsout \ read byte of card data (returned in A) 6310TXA:PHA \ preserve regs 6320LDX#8 \ counter 6321LDA#0:STA temp \ target 6330.getsoutloop 6335LDA temp:ASL A:STA temp \ shift result left one 6337LDA &FE60:ORA#1:STA &FE60 \ assert clock 6338JSR delay:JSR delay 6340LDA &FE60:AND #&FE:STA &FE60 \ deassert clock and read data at the same time 6341AND #8:LSR A:LSR A:LSR A:ORA temp:STA temp \ OR the read bit with result and place back in result 6345JSR delay:JSR delay 6360DEX 6370BNE getsoutloop 6380PLA:TAX \ fix X reg 6390LDA temp \ get result 6400RTS 7000.delay \ what it says on the tin 7010PHA:TXA:PHA:TYA:PHA 7020LDX #&0F 7030.delayloop2 7040DEX:NOP 7050BNE delayloop2 7060PLA:TAY:PLA:TAX:PLA:RTS 8000.putserial \ put byte in A into RS423 buffer, if buffer is full then wait 8010STA temp:TXA:PHA \ store A, preserve X 8014JMP putserialwaitloopshortcut 8015.putserialwaitloop \ wait for space 8016LDX #(ssbufferfull AND &FF):LDA #(ssbufferfull DIV &100):JSR print:JSR &FFE7 \ print message 8017.putserialwaitloopshortcut 8020LDA#&80:LDX#253:JSR&FFF4 8030CPX#0 8040BEQ putserialwaitloop 8050 \ have some free buffer space now 8060LDA#3:LDX#7:JSR&FFF4 \ select RS423 8070LDA temp:JSR&FFEE \ OSWRCH 8080LDA#3:LDX#4:JSR&FFF4 \ back to screen output 8090PLA:TAX:LDA temp:RTS \ clean up and RTS 8400.resetuserport 8405PHA:LDA #6:STA &FE60:PLA:RTS \ user port CS=1,CLK=0,SI=1 10000.temp \ general purpose temp word storage 10010EQUW &0000 10020.ssgotbyte 10030EQUS "Got command 0x":EQUB 0 10035.ssunrecognisedbyte 10040EQUS "Unrecognised command.":EQUB 0 10050.ssclearstatus 10060EQUS "Clear status.":EQUB 0 10070.ssreadstatus 10080EQUS "Read status.":EQUB 0 10090.ssreadblock 10100EQUS "Read block 0x":EQUB 0 10110.sswriteblock 10120EQUS "Write block 0x":EQUB 0 10130.sscardbyte 10140EQUS "Got byte from card 0x":EQUB 0 10150.ssreadid 10160EQUS "Read ID 0x":EQUB 0 10200.ssready 10210EQUS "(*) Ready.":EQUB 0 10220.ssbufferfull 10230EQUS "(W) RS232 buffer full.":EQUB 0 20000]NEXT PASS *FX3,4 *FX2,0
This is a BBC BASIC program which, when run, will assemble a machine code routine into memory at address &5C01. Because I had no disc drive for the BBC, the easiest way to get this program into its memory was to just do a "*FX2,1", which told it to accept all input from the serial port instead of the keyboard. I could then just pipe my program to /dev/ttyS0 on my Linux PC. This also made editing the code much easier than if I'd had to do it on the micro. Control is returned to the keyboard by the *FX2,0 right at the end of the script above. The BASIC program would be RUN and then the resulting machine code routine invoked using CALL &5C01.
The software essentially listens to the serial port (lines 50-70) and waits for a byte to arrive over the RS232 from the PC (the command byte). Once a byte is received, the code goes through a switch/case style construction (lines 140-410) to decode the command byte and decide what to do. The command bytes correspond to the instruction bytes for talking to the memory card via its SPI interface: clear status (0x89); read status (0x83); read block (0x52); write block (0xF2) and get ID (0x85). Depending on the byte received, a subroutine is called. Some of them, such as readblock (line 4000) will expect extra bytes to arrive over the serial port (in this case, the PC must tell the software the block that it wishes to be read). The subroutines make heavy use of other routines such as sendcmd (line 6000) which sends a byte to the memory card; getsout (line 6300) which reads a byte from the memory card and putserial (line 8000) which sends bytes received from the memory card over the serial interface where they can be collected by the PC. The subroutine delay at 7000 is used to introduce varying levels of artifical delay so that the diagnostic LEDs may be inspected.
To understand what it does to the signal lines, it is necessary to have a look at how the SPI bus works.
... to be continued !
Thread title: