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.
i’ve been on vacation and busy with some other things, so i didn’t have a chance to work on the 8-bit project for a couple of weeks. but the last two weekends i’ve been working on the XMIT command, and finally finished it up. as usual, there were a couple of stupid little bugs in the assembler that i didn’t catch. one was properly loading up the X register with the number of full pages to transmit, and the other was not having copied the data location information into the alternate location for use as a counter.
coding XMIT actually wasn’t all that difficult – i just started by copying the code for RECV and switching the sense from receiving to transmitting. i also needed to update my PC-side utility to allow it to receive as well as transmit data.
the form of the command is
where ‘a$’ and ‘l$’ represent the starting address and length of the data to be sent, respectively. both arguments are required, and the order must be as shown – because i’m taking a shortcut and ignoring the letters and assuming that the arguments are in this order. i also skip the $s and assume that the value is in hex – decimal simply isn’t allowed. i justify this because a) it works and b) i do intend, if i ever manage to complete this project and get it running on my own hardware design, to at that point go back and flesh out the firmware to provide a better user experience.
the format of the transmitted data is identical to that for the RECV command.
with XMIT and RECV now both working, i think the next step is to go back to the hardware side and integrate the serial hardware onto my breadboard, bypassing the SSC.