so i finally got around to finishing up the schematic for the current state of the hardware for the 8-bit project. here it is (click for a larger version): [i apologize for how crappy this image looks here. it looks fine in wordpress’ editor, so i don’t know why it’s messed up here…]
i created the schematic using the freeware version of eagle, including creating my own library items for the apple slot and lcd connector. while the free version is obviously limited (1 sheet, max 4″x3.2″ 2-layer board size), eagle is nice because even the free version includes an autorouter, which should make the eventual task of laying out boards for this project much easier.
the block on the left represents the apple’s expansion slot, which i pull out to a breadboard via a system i’ll have to post a photo of because it’s a bit difficult to explain.
on the right side, from top to bottom, are the keyboard output, keyboard input, and lcd panel. the keyboard itself is a chiclet keyboard from a tandy color computer. i chose it because (a) i had it sitting around and (b) it has a nice single-row, 0.100″-spacing pin connector, which made it easy to connect an IDE cable to bring it over to the breadboard.
glue logic is really minimal – all four OR gates of a 74*32 and a single inverter off a 74*06. the inverter is used to create a /RD signal from the apple’s R/W as the lcd requires separate read and write strobes. /RD is also used in the logic for triggering reading vs writing the keyboard.
everything ultimately depends on the apple /IOSEL line, which is pulled low when addressing any of the 16 bytes of form 0xC0Nx, where N is the slot number plus 8. Those 4 address lines are currently being used as follows:
- A0 – command/data (C/D) input for lcd
- A1 – feeds into /CE for lcd when ORd with /IOSEL
- A2 – ORd with /IOSEL to form the keyboard enable. that output is then further ORd with /RD and R/W (i.e. /WR) to determine which half of the keyboard logic to enable.
- A3 – still unused. i can’t use it for the planned IDE connection because that will need 4 lines. i also can’t use it for creating my own serial port as that will require 3 or 4 lines as well.
the only other thing i can think of to note is that the apple’s -12V supply is run through a potentiometer to supply the lcd bias/contrast input.
yesterday, while reporting on my success with the LIST command, i noted that i was going to try to add the byte values in front of the disassembled commands. well, that’s done now too…
as noted, the output of LIST is now formatted thusly:
$aaaa: bb cc dd mmm <addr>
with ‘aaaa’ being the address of the instruction being disassembled, ‘bb’ (and ‘cc’ and possibly ‘dd’ if needed) are the actual byte values that form the command. ‘mmm’ is the 3-letter mnemonic for the command, and ‘<addr>’ is the addressing or argument info if appropriate.
so all of the memory-related commands for the 8-bit project are now complete and working. the remaining baseline commands to be implemented are all storage related – loading/saving files and so forth. since i don’t have the filesystem completely designed yet, i probably won’t be writing any more code for a bit. just as i feel like i’ve really gotten the hang of it and enjoying it, too… 8(
i finally completed the LIST command for the 8-bit project by adding argument printing. getting the branch address calculation correct held me back for a week, but fell quickly when i finally realized that a backward branch was actually the same calculation as a forward branch, just with a pre-decrement of the MSB to account for crossing page boundaries. i had been trying to use a different code path for it, subtracting rather than adding, etc.
for the mnemonics themselves, i set up a 1024-byte table – 4 bytes for each of the 256 possible values. the first three bytes of each entry are the three-character opcode (or ??? for illegal opcodes) and the fourth byte is the address mode. this allows me to simply use the actual opcode (multiplied by 4) as an index into the table. the address mode byte is similarly used as an index into a table of jump addresses for each of the address mode output handlers. i’m sure all of that could have been done in some fancy way that would save a lot of memory (at the expense of extra clock cycles), but as i’ve mentioned several times, i’m aiming for something that works right now rather than the most elegant solution.
i did add a bit of elegance by clearing the screen before i start outputting. this prevents the lcd from having to scroll, which is a fairly slow operation. so the output is not only complete now, but it finishes faster and looks better while it’s outputting. 8)
for the next step, i think i’m going to improve LIST a bit more by adding the actual byte values to the output, i.e. instead of the output being
$aaaa: mmm <addr>
it will instead become
$aaaa: aa bb cc mmm <addr>
where of course ‘bb’ and ‘cc’ may actually be just spaces depending on the number of bytes that the command actually uses. this will bring the maximum length of a line up from 23 to 32-34 characters (depending on how wide i make the gaps), which is not a problem for my 40-character-wide lcd.
i’ve decided to work on the LIST command rather than the hardware on the 8-bit project. the LIST command shows a disassembly of several instructions, equivalent to the ‘L’ command in the apple 2 monitor. so i’ve had some pretty good success this morning – i’m able to output the address and 3-letter opcode mnemonic. no argument printing yet, but that’s next.
command format is
as usual, the address argument is optional. if omitted, listing will start at the address byte following the end of the previous LIST. this allows the user, after an initial LIST command with address, to simply continue hitting return to show additional pages of disassembly.
output format for each line is
aaaa: mmm [args]
where ‘aaaa’ is the 4-hex-character address of the instruction, ‘mmm’ is the 3-character opcode mnemonic, and [args] is the arguments for the opcode as appropriate. arguments will be displayed in the same format as the apple 2’s disassembly listing, e.g.” (zz),X” or “#$xx”.
since i’m using an LCD with 8 lines of text, only 7 lines of disassembly can be shown at once. the eighth line is needed for the prompt for the next command line.
i intend to make my computer capable of using an IDE hard drive to store and load data and programs. while technically a 16-bit interface, it is possible to interface to a drive in a purely 8-bit manner. my primary references for this so far have been retroleum and andrew quinn. these methods only allow access to fairly small drives, but for an 8-bit computer with a 64K memory space, that’s not really an issue. i’ve actually already picked out the drive i intend to use, an ~500MB seagate drive i had laying around. i won’t be using anywhere near its full capacity though – i will be limiting myself to 16 bits worth of block addresses. at 256 bytes per block, this means a maximum capacity of 16MB.
in addition to the hardware, i intend to create my own file system rather than use something like FAT, even though information is readily available on that. doing so would go against my desire to create everything i can by myself. i’ve done a little thinking about the file system already, but i’ll talk about that in another post.
the main point of this post is to talk about the firmware commands that will be needed to support disk i/o. i am planning on a simple set of 5 commands that will need to be created:
LOAD – to load a file from disk into memory
SAVE – to save data from memory to a file on disk
DEL – to delete a file from disk
LS – to provide a simple, multi-column list of the names of files on the disk
DIR – to provide a more detailed listing of the names of files on the disk along with their length and default loading address.
the obvious command missing here is for initial formatting of the drive. that command will of course exist, but i expect that to be software rather than part of the firmware because it’s something that will be needed so infrequently and firmware space will be limited. if i can fit it into the firmware space, then it will probably move there.
together with the creation of the filesystem itself and the hardware aspects, this is obviously a large and complex subsystem. but i’m sure it will be an interesting learning experience as i go through it, and that’s the important thing, right?
just finished tested the SET command for the 8-bit project, which sets the contents of memory to new values and takes the form
SET [A$xxxx,] aa bb cc…
where as usual square brackets indicate that the address parameter is optional, and (if present) the address must be 4 hex characters long and followed by a separating comma. if the address parameter is omitted, then input begins at the current address. If the previous command was SET, this is the byte after the last byte modified by the previous SET command. If the previous command was SHOW, this is the byte after the last byte shown.
after the comma, the byte values to be set are entered in hex. each byte must be two hex characters long and separated from the next byte by a space. the number of bytes entered in a single SET command is limited only by the 255 character limit on the length of an input line (not 256 because the 256th character will be the CR). so this allows you to enter up to ~80 bytes with a single command.
as i’ve mentioned previously, the ‘A’ is actually completely optional at this point – i scan directly for the ‘$’ to look for an address parameter. so in reality, you could enter something like
SET adfsa8093ja;klncvd$1000,01 02 03
and the code would set the 3 bytes starting at $1000 to 01, 02, and 03 without issuing any error message. i’ll work at making it stricter once everything is working. (although, at least for the basic set of commands, i think i’m actually approaching that point…)
not sure where i’ll go next. other than the disk commands, the only primary command left is LIST, which is intended to provide a full disassembly of several lines of code starting at the target address.
just finished testing the MOVE command for the 8-bit project, which copies a block of memory from one location to another, and takes the form
where S is the Source address, D is the destination address, and L is the length of the block to copy. as usual the addresses and length must each be exactly 4 hex characters long. note that the S/D/L characters are, strictly speaking, optional. i locate hex arguments by searching only for the $ and then looking at the next four characters. i know this is a bit of a shortcut, but it works for now. if/when everything is working, i can go back and be more strict about such things.
memory is copied in full 256-byte pages, plus a final partial page if needed. full pages are copied top to bottom, i.e. starting at offset 0x00 and going to 0xFF. the partial page, by contrast, is copied bottom to top, i.e. starting at offset 0xzz and going to 0x00. i did this because it allows me to use the LSB of the stored length parameter as both the counter and the offset index for the LDA/STA commands.
note that their is no argument checking done – so if xxxx < yyyy < xxxx + zzzz, part of the memory to be copied will be overwritten, possibly before it has been copied. this would obviously lead to unexpected results, and should be avoided. at some point in the future i hope to improve this function to detect such cases and change the order of the move to produce the intended results.
so, about that bug…as i was debugging MOVE, i found a bug in SHOW (which i was using to verify whether MOVE was working correctly). The bug was that SHOW would not allow a hex character ‘F’ in its address argument. however, the actual error traced back to the function that converts command line arguments into hex values, meaning that, in reality, NO function would allow an ‘F’ in their arguments.
the cause of the bug is that, in trying to determine if the character i had pulled from the input buffer was a legal hex character, i was doing a CMP #0x0F; BPL err. however, zero is considered to be positive, so an ‘F’ would cause a branch to an error condition. but because i was never setting an error code, no error message was generated – it just returned to the input prompt.
the fix was simple – change the CMP #0x0F to CMP 0x10, so that an ‘F’ now goes to -1 and the BPL branches properly.