 day six I think So we have console IO we have our ccp running Let's actually make the ccp do something useful the Ccp is actually a pretty simple program. This is the original CPM 80 version I'll find the startup code It reads a either a command from the console or a Command from a script that's currently being run it then parses it and It runs the command there may be some built-in commands there Dear arrays type save rename user and Everything else and for everything else the ccp will load the program into memory and run it the program may then return to the ccp's loop or It may restart cpm The user doesn't see any difference at all other than a delay of about a second as cpm restarts It doesn't like clear the screen of show banner or anything That way the program gets to reuse the RAM that the ccp is using and on cpm 80 that is Two kilobytes if I remember correctly who knows what it will be for l1 so We have a command loop We have some initialization code cpm 80 it actually gets sent information by the BDOS Sorry by the BIOS Because in cpm 80 the BIOS load the BDOS and the ccp and then it's jumps to the ccp to here or Here usually here and then it's the job of the ccp to reset and initialize the BDOS But the way we're doing it due to the differ differing requirements of the 6 of 0 to and you know We have to relocate stuff and so on The ccp is loaded and executed by the BDOS. So the BDOS has already initialized itself So in fact, there's nothing we really need to do here We do need to handle submit files. These are scripts, but I Am not going to do that just yet also Cpm 80 has the ability You can load the ccp then write stuff to the keyboard buffer jump here So ccp start and it will execute the command in the buffer. I have no idea what this is for Actually, I do know what it's for. It's to allow programs to run other programs easily Yeah, now I think of it, but of course you can only run one Let's just not worry about this for now. This is going to be pretty simple so we want to Print a prompt and read a command so printing the prompt we are In cpm 80 the ccp keeps track of the current disk But there is a BDOS function for that Get disk reset Get set user code user codes are another thing I'm going to ignore for now user code here we go return current disk Entry 25 We have to implement that User codes are an idea that really didn't work If they allow you to Subdivide a disk directory into 16 user areas They are a kind of Directory the problem is the BDOS doesn't really understand them very well so the user code doesn't get baked into the FCB so every time you do something that touches an FCB you have to remember to set the user code by calling this function and Heaven help you if you get it wrong because really bad things will happen as It will look for and write Directory entries with the wrong user code and it will mangle your disk Okay, get drive. We have a section for drive management somewhere. This one will do entry Get drive That is pretty straightforward. You simply get drive Store it to par and plus zero and return Okay Back into our CCP Get the current drive Which is a value a zero based value? add on a to turn into a drive letter Print it print the prompt and Now we want to set up for reading a String this means we need a buffer to put the string in I'm just thinking so Now the easiest thing is to just create a 128 byte buffer down here But That we've got one, but I'm not going to do that We have to pass information from the CCP to the program that's being executed that information consists of the command line and the FCB's of the more or less the first two parameters and To just make this simple I am going to put these At the beginning of the stack page So let me just fire up the emulator again and run it. Okay, so it's been executed reasonably so if we dump stack page This is 256 bytes of RAM That the stack the CPU stack users it starts at the top and works down the ticks over to doesn't really use a lot of stack unless you're doing really dumb stuff and So I am going to steal a bit of the top of this the We want Enough space for one FCB, which is 32 bytes Plus the maximum possible command line, which is 128 bytes. So that is a zero Worth of bytes so that will use up in the worst case this much of it hmm Now I look at it that's quite a lot but the alternative is to Well, the alternative is that we need to allocate some memory somewhere to pass to the program being executed which is quite hard Because we are expecting Well, we have to allow the CCP to be overwritten by the program if the program wants to use the memory So just pointing the program at something in the CCP buffer isn't so hot because that will force the That will force the program to copy everything out of that We could we could require the program to Create some buffers at a known location and as part of the relocation process We poke stuff into those buffers That might be Preferable Of course, it'll only use this much memory if you're using the entire 128 bytes of command line, which is very unusual, but you can see That Moss itself it's used stack up to about here So I didn't realize it was quite so much This leaves like interrupt handlers and so on So that only gives us a safety margin of a factor of two Which I don't think is enough Okay, let's see if we can pass the buffer in as into the program By writing it into its memory in the meantime We are actually going to have to create a buffer here so We create a buffer of 128 bytes here Okay, and we read a line I did not Run my build thing. Okay. So now if you run that This is now actually looking like a That's not working It's beginning to look a bit more like CPM Okay, oh Yes, I also fixed control you last time I said it did a new line and printed a backslash I went and looked up what it actually did and that was complete nonsense It prints a hash sign at the end to indicate that that line has been cancelled and then moves on to the next line the reason for the stuff like retype this one and and The way control you is handled is because CPM was originally intended to be operated on a teletype and on a teletype if I type a If I make a mistake and I press the backspace key It does indeed backspace and it deletes the character from the buffer, but it's not like it can take it off the paper So the next character you type over types what was already there and you get a mess For occasional mistakes, that's fine, but imagine Trying to retype a entire command line So over the top of the existing one so control you just goes on to a new line and a blank bit of paper so We have a command line and it's not really a lot of code So we are actually going to We go read command line how does this work? This is all submit file stuff. These are those are script We'll get on to that later. I Use a code management that we're just ignoring as the The code that actually does the work that should be about here somewhere. Yeah. Oh, yeah, here it is Arbuff read buffer. Okay So what it does is it then? Make sure it's zero terminated So we read the length of the command line Like so Let me think we have the length and some text The length is a in that case is four So zero one two three four we do indeed want to add one to The command line length You then convert it to uppercase and yes This does mean that you can't pass lowercase text into a CPM program on the command line Just deal with it So Here we have our length the Byte and there is in fact a utility here for Translating to uppercase now The 8080 and the Zed 80 is Secret superpower are these one byte conditional return and call instructions So they allow you to do this kind of logic really densely what this does you You load a character into a and call here and it says is it less than small a Then immediately return is it greater than small Zed If so immediately return otherwise Translated to uppercase and return and that is nine bytes The 65502 secret superpower is indexing where you get basically for free a Eight to sixteen bit addition in a lot of instructions not all of them, but a lot of them So we are going to have to do this the hard way So is this greater than a Capital a and We want greater than we have to do greater than or equal to so Actually we want it is it greater than or equal to a capital a yes Is it less than? small a Is it less than carry clear a? a The character after a Z if so then we know this is a lowercase letter therefore And it so we have One two three four five six seven eight nine ten bytes Of course, it's not a subroutine So we then want to write it back increment and go round again In fact, I was going to test for zero and Exit, but it's cheaper and easier to simply compare with the length and do it like that, okay, we have Hopefully We should have finished reading the command line. So I am going to test that by Doing this and where is the caps lock key this one? So it hangs We break we see that our buffer is at two zero seven one Is it be at the end? There is indeed a be at the end and this is all translated to uppercase Good, right next stage is we want to parse it Now this is with a bit where it gets really interesting because The way it parses the command is it Assumes that the it starts with a file name and Of course in CPM as soon as you do anything with a file name you need to turn it into an FCB So there is a huge chunk of code Which actually Starts here and goes on and On this is really dense code to here. So that's nice So we are going to make ourselves zero point a zero page variable, which is I was going to say a pointer, but this is for though, too. We don't want to use pointers We want to use indexing and so we need to we're going to need to parse several FCB. So this is going to be a helper Routine and if this one we do need a pointer and down here we want to create ourselves a FCB and an FCB is 33 bytes long. We're not using random access. Okay, so parses and we want to we need to set up our index pointer and Pars the FCB and The first thing we're going to do is to store to set up our pointer Like so Okay, and in fact this is going to be a zero Because it's going to be relative to the first character of the command line So this is The code that actually does the FCB stuff So D blank skips white space straightforward if it's a space if it's a space a tab This is only doing a space. Okay. Well, that's easy enough. So break if it's not a space otherwise Otherwise loop. So our pointer is now looking at the beginning of the command line So the first thing we want to do is To check to see if There is a drive letter. Do we need to worry about? End of string here. I think we do need to check the first bite so if the first bite is zero without we've Appended zero to the end of the command line here if the first bite of the command line is a zero then obviously there can be no drive letter But also there can be no command Therefore no FCB So this is an error We now know that the first letter is not a Zero which means the second letter is going to be either a colon Meaning that there is a in fact a drive letter here a Zero meaning there is a one letter File name or anything else so We do want to Load it. Can we do slightly better? There is a I can load Y indexed with X and I can load X indexed with Y So let's actually put that that's the drive letter because then that will allow us to No, let's do this the other way around So we now load So is this a colon? Yes Therefore a Must contain a drive letter. So Let's turn that into a To the one based drive numbering scheme and Am I actually going to get a chance to use the bit instruction? No Okay, we want what we want to do is test to see whether it's got the Top four bits set actually we can just do a comparison We want to do a less than comparison. Is it less than 16? Actually, no, is it greater than or equal to 16 carry set? That means the drive letter is out of range Otherwise you do want to do Otherwise we store it at and the beginning of the FCB and skip over the drive Did I do? Said break if any I did not So we should have successfully parsed the drive letter And it also occurs to me that we probably want to Wipe the FCB so the So drive needs to get reset to zero Then we want to keep writing Spaces Because the file name is padded with spaces so Dr for drive so we have a loop where the increment y Let's actually load a space store it until we reach the last extension Byte Which is e3 and then it'll stop. Okay But then we want to store zeros over the remaining four bites of the FCB That's ex s1 s2 and RC until we reach until we've done RC E3 Now there are parents apparently called t3. I got these names from the digital research documentation t3 Okay, so this should get us to the point where we have the drive set We now want to start reading file name characters, okay, this is Irritating because we have to index two things at once We are Iterating over the command line whose index is in Y We also need to iterate over the FCB itself The FCB is a pointer in direction So that's index register must be in Y However, the command line indirect the command line indexing is That's from here the command line indexing is in Is using is using an absolute address? So that can use X. So let's put the offset Into X that means this Has to be in Y so The file name starts at f1 so we are going to Keep reading characters Test to see if it's a valid file name character. We are going to Stop if it's the if you read the end of the string Test to see if it's a valid file name character at which point We write it to the FCB increment Both Here we want to test Is it a dot if so it Stop parsing here. Is it a space? If so stop parsing here in fact if it's a space Then we know that There can be no more characters So let's just stop However, if it's a dot we have to deal with the extension have we reached the first extension byte Stop if we have Okay, and in fact if it's the end of the line then This is also a case where we have to exit Didn't it like so now we want to skip valid characters until We reach a dot this was going to truncate the the file name part of the file name to eight characters So is it? Is it a dot? Oh? Yeah, and we also if it's not a valid file name character Then we want to Fail so if it's a dot then We know that it can't be a Space or a zero so we know we are not at the end of the line Invalid characters are still not allowed at this point, but we do now need to No, it's not a dot and it's not a space. It's not a zero Okay, read the next character if it's a Zero Then we've reached the end of the line and there is nothing more to do if it's not a valid file name character This is not a valid file name. So Error out and we want to increment x here because this is all pre decremented Probably and so is it a Is it a space? if so exit and I think That does us so If it's a letter Well, if the last thing read the thing that stopped us reading the file name is a letter Then it will be consumed and discarded We read the next thing and it will keep going around until we see a dot or end of line or a space Okay, so this is going to be the same code here Just slightly different So we get a character Do the test to see if it's the end of the line do the test to see if it's valid if it's a inval Yeah, we stick it in the FCB which the Y is already set to the right place But this time we are giving up when we reach t3 be nice to Be able to factor this out, but given that it's full of branches out of this code. I think that would be hard Okay Now we want to discard any more File name characters. So that's going to be this code again and in fact This is still full of branches and things so we can't use this either, but it is in fact the same code So this should work now one of the things we want to test for is Just a drive letter Because that's a valid command it just changes the current drive so that would load a character here it's a End of line so we go down to exit Okay, that looks reasonable We do want to Write our updated Command offset so that X is now pointing to the character after the end of the FCB Oh Blast It's also the job of the FC of this parsing code to do with wildcards So if the user type uses a wildcard like star dot star then This code here needs to needs to Um Convert those to question marks So So read the file name part. Is it a star? If so Consume the character Hmm So what we need to do here is write question marks to turn To do this so we're going to want to keep writing question marks until we reach the end of the current file name segment The issue is we do actually want to Increment the the read pointer. So I think What we do is We change the character we've read into a question mark then we're going to Decrement X is that right? This is going to So we're going to keep reading stars until We Fall off the bottom here. So this will be full A the character just read is going to be a question mark Not a dot. So We want this code here To skip it Which it will question mark is a valid file name character. So the next time round will get a dot and Then we go on to the next bit. Yes, this should work just fine Maybe and the same thing happens Here to be missing Said until any is valid file name character not to find That is true So the valid file name characters anything which is Anything which is graphic is invalid So we can say we want a We want a less than if it is strictly less than 32 Die This is just this I'm just reusing this piece of code that Sets carry and returns because we're using the carry flag up here to indicate validity And we we can't just return because we know the carry is clear. We have to set it Anything that is bigger or equal to 127 is invalid These characters are all invalid. So a question mark space We should not be seeing any spaces in this code. So it doesn't matter what we do there Space equals is invalid dot is invalid colon Semicolon Left angle bracket Right angle bracket Otherwise, we're good range error. Well, that's annoying Can I break this up at all? I'm not sure it's these branches We can Do that? Okay, let's have a So this is two bytes 3 4 5 That's five bytes and by putting it into a subroutine call. We get it down to three. That's not going to help these breaks are Yeah, break if is cheap a simple Z break is expensive these loops are Expensive because each end loop is three bytes for a jump back to the beginning What I'd like to do is To common out these this code because this chunk of code in the same chunk above are identical it's just We have a branch there a branch there a branch there and a branch there all of which can exit the loop So I don't think we can okay in FAC that's two bytes two bytes three four Inlining that it will do just fine Okay Let's run this and see what happens So we type in a command line If I can remember where the various keys are in this thing semicolon colon see colon A B's star dot x Right it hangs in that jump loop. We're here 2049 I Can change registers can't I our set? Okay, so our set PC to for C So we should be at this JSR step once two oh five seven. We are here AX is our FCB and is going into two one DD Which is all zeros? So let's get white space So we load the command offset 215 E is our buffer Which is Correctly zero terminated. There is no white space so Load the bite and pair it with a space. It's not a space Break if it's not which is not and return wipe the FCB This is the offset of the drive bite Store it. It's already a zero so that won't do anything and Now we are writing eight Spaces, I am sure that This is the last one Just miscounted Wait, oh we're writing 11. Okay There we go and We're down here. We're erasing four bytes of metadata right, so now if we dump our FCB we see a zero followed by four eight Eleven spaces and four zeros. Okay. Check for drive letter. There is a drive letter We're going around that loop again right now. We finished So load the command offset into X which is zero Read in the drive letter 43 that's an a Was it a zero to reach the end of the line? No Read the actual drive letter read the colon into Y 3a that's a colon was it a colon? Yes so convert the drive letter into a zero base number and And Throw a one base number so a is one b is two c is three Correct is it in range Yes Store it in the FCB Reload command line offset into X in fact it never went out of X So we can get rid of that line increment increment Okay So There's our three Read the file name. So we set up our right pointer. We get a character. It's a Whatever that is it's an a it's not a zero It's not a space It's not a dot it's not a star Check to see if it's a valid file name character. So is it less than a space? No Is it greater than 127? No, is it that's wrong? Okay Good to know and this will in fact fail So set carry return Where were we we were at two one? This is now going to bail out se see And now we're right up at the top here going there on the loop again, okay so write that back reboot asset to actually Now I've changed the code to none of the offsets work to a four To four nine a BC Okay We're now up here. Let's just disassemble this that RTS means we are Here looking for LDY one there Okay, so Break at two nine to continue We're here So read the character. It's still an a No, no, no, no check to see if it's a valid character Carry is clear. That's fine. So store it in the FCB Move on to the next Did we see end? No until Equal PC 204 V To new that seems wrong. I Did have a break point in there, didn't I? Actually, it's just do That's it PC to be that does not seem like it's working, right? Okay. Let's just Get rid of that because you now know that's at 2049 but a break point at 2049 continue a Star dot B Okay, this is our FCB code Let's assemble that This 209 for is where we want to go Continue For we are at here. So write it increment Have you reached the end? No, we haven't so we go around again This time oh Yeah, I will who wants Here is our FCB. So we see we have written as an a in so this time is going to be a star So it's Not a space It's not a dot It is a star So turn it into a question mark Back up one it's still valid because question marks are valid. We write it we increment We go around again there is our question mark so Same thing happened Right, so let's break at two zero B naught one two So we now finished that bit so let's Dump our FCB and Oh, yeah Go go go There we go dump to one DC and There is our file name which consists of a three question marks four question marks That's eight in total. So skip non-file name characters. Well, our is 3f, which is a question mark So we tested to see if it's a dot It's not a dot Skip it pick up the new character Was it a zero? No space. No. Is it valid? Yes, no To e to each dot that should not be there. Okay. We want to go to two zero C three Two zero C three. Did I get that word dressed right to one D seven? That looks very wrong That does indeed look extremely wrong Dump to one D seven, okay all zeros. I only changed one line of code Has the FCB moved Somehow oh the FCB moved because I deleted the J. S. R. Okay, so zero one two three You and E3. There's our correct FCB, right? So Where are we? Two zero nine four. We should be filling it with question marks Be two C three so now Right there is our correct Stuff we now know that R is still set Why is R set? to ease a dot that's a Valid character has BM not picked up the Wonder if it didn't pick up the new file sometimes happens Right carry is set it has in fact that's fine. It's just Didn't work So to e Is a dot it should have fallen out the bottom here Okay, so this is where the J. S. R. Should be so let's just try that again to zero zero Dot is it a space Is it too big? Is it any one of these things? This is why we test okay? Let's try that one again. Shall we start up B to a C zero to a C three carry is still Clear they're still set didn't pick up the file Because I didn't run I didn't have my script running. Okay, who zero C three go may start up B Carry is clear So now we get the next character But we've rather we've read the new character here So we go around again you see that it is a dot so we end exit the loop with this one So we're here. We're reading the extension. This should all be the same code This time. We're not reaching the end. So we read a Character which is our B. It's valid You write it increment Y Increment X have you reached the end go back to the beginning write a new character is We haven't done that yet wait that That fell off the bottom of the loop So it thinks Y is too big So we haven't written these last two Characters Yet so it shouldn't have fallen out the bottom of the loop What is Y set to Y is a so zero one two nine a T three This is B C D E F until equals That worked Doesn't look like it did but It actually correctly passed it went around the end It Should have hit that breakpoint actually Yeah, something moved Two zero E six. Oh, I put the breakpoint in the wrong place. That's why a dot B here is our here is our FCB and The B is in the wrong place the B is in the wrong place because if this If reading the final name doesn't increment it sufficiently then it won't be set so Ignore that now we just want to set it correctly to T1 Okay, I think that's working To be honest Let me just find So here's the top of the FCB. So here's our code here. No, it's not I Don't know where the FC where the ccp loaded none of this looks Very familiar to be honest Okay. Yeah, here we are So if I find I'm looking for this call. So we have a here we go Load a x with command FCB parse FCB So if you break out to a 4c So we can type something in and then we can dump the FCB dump the FCB So we see there is indeed the FCB more wanted continue a dot B Dumped to 1d3 that's still not right read the file name Read the extension Why is it put this in the wrong place? Did I reboot it? I don't think I did a dot B. Okay Yeah, it's a work night. So I have just spent a day at work which is Why these there's this one in the last one are slightly more muddled than these usually are okay to 1d3 That's better Okay, we have the file name part and the extension and in fact that's a bit misaligned 25 that's better. Okay a star dot BCD a star has turned into question marks BCD has not been read a dot BCD five has put BC but not D Because And right this is a systematic errors Why the FCB check for the drive right? this is one after the The last character of the file name. So in fact, let's change this to f8 plus one to make it clearer what it's doing So Read the extension. This should be t3 plus one So a dot BCD D5 Yeah, we go a dot BCD a Star dot BCD Still not working So this is coming in with a question mark. So it should be advancing Picking up the next character, which is a dot and then breaking Then we skip the dot and go on to the actual first character Great. So where is that code? This is This is our validity checker. So let's go from here This doesn't actually all seem to be particularly big code to be honest I think it's all fitting within 256 bytes Okay, LDY one is here So we actually want to go to the next Paris and Against 2e here to a 9A. So a star dot BCD that's That's an a we're in the wrong place, but I Think we are here So let's go from here anyway Yeah, compare for against the star. It's not a star. Is it valid? Yeah Compare branch Break out to OB to our all our question marks to OB to write 3f is a question mark. So Was it a dot? No, it wasn't Increment so we're looking at the dot load it to e that's a dot was it space? No Was it valid? Yes, was it a dot? Yes We're here skip the dot Set Y to 9 to indicate we're starting on the extension Load the byte That's a B So There's our FCB so far. We have not written the extension. So where is this bite going? Because we have forgotten to put the comparison in here for a wild card. That's why so it's always It's not a dot therefore. It's always branching To here should be fine. That's valid Yeah, so I was nine. So this should be writing it to the FCB 25 Yes, it did put it there Okay, so we go around again pick up our character It's a C. It should be valid It's written We go around again This is our D. It's valid We write it We reach the end we stop so We have not read the next character Okay, this is also Yeah, if we break if we break up here a contains the character just being read If we break here and fall off the end of the loop a contains the previous character read But why has not been incremented? So I think that all of these need to be While Loops with the branch at the top which I with the exit at the top so We get the character Test it for zero then we see if we've reached the end if so we break and then we go around that's annoying because it's It's another three bytes of code for this jump But this means that this should work. So this now instead of being a repeat until becomes a Z loop end loop end loop so Here when we break we should have Yeah, he that's just pushed us over the limit for the invalid FCB stuff Well, we know that Carrie is set. So actually this becomes a very simple if Carrie is set Return three bytes. So it's only one byte more than previously Okay a.btd We are at all the codes been rearranged. So I'm not quite sure where we're at. We are probably We are here So you can see how far this is the FCB's moved on to on the Okay, so Of course all my break points are now in the wrong place 204 C A dot BCD and We are there we go a dot BCD Right a star dot BCD. Oh good that finally worked Star a dot BCD right So that I believe is correct. It has print it has inserted eight question marks and discarded the a star dot star 11 question marks AFL is the first three characters of the extension This is the first eight characters of the file name Okay, I believe that to be working She'll just do one more. So it's set the drive letter and Has set the extension good. Okay. Let's get rid of that. So our CCP is a mere 400 bytes Okay the next thing to do is to try and Check to see if it's any of the commands we know about. Oh there was actually Something I forgot to do 204 C it was a colon right Carrie is clear which is which is good because we think this is a Valid FCB for certain purposes And it's got a drive letter and an empty file name Good good and one more time. I Just pressed return there. Carrie is set meaning It's thought that was invalid and the FCB is unchanged We do actually kind of want to test for Empty lines, so we are going To So it's if we've skipped the white space then command line plus Well, that's Bought this whole thing. Okay. Let's just put this way Just fix all that shall we That wasn't actually advancing anything so this So this will update Rocky Leaves the updated command offset in X so we can just do this if The command line is Empty then We want to print a new line and Go again Yes, we did actually forget to part to test parsing spaces. So new line good Spaces return good spaces Command Right. Yeah, notice how When I type in a valid command the cursor just goes to here that's actually correct in CPM the cursor is a print say carried return after the command and Then it's the command's responsibility To move the cursor down before doing anything and no, I do not know why it does that Okay, so we've passed the command and now we want to decode it Intrinsic So this is actually pretty straightforward Our four commands if I find The relevant bit here. There we go They all max out at four characters So we can actually just test for the four characters And as following space in the FCB we don't have to deal with parsing the actual command line This is why we wanted to pass it to an FCB before doing anything else so our command table is dir arrays type save Rename and user so Command FCB is absolute. So we actually have one and actually let's put a Zero at the end of that So we want to index through the FCB and also through the command table So that's actually relatively straightforward as we have to index registers So for each command We want to Load a byte from the command table Compare it with a byte from the FCB FCB If they are not equal Then it obviously cannot be this command otherwise loop until We reach an FCB offset of Four and then stop. I mean I want to check the last character To make sure it's a space If it is a space It's a match if it's not a match then We want to advance to the next command Thing is if we reach here Then X has been moved up. So it's pointing at the next byte So for the next command, we actually want to round X up so TXA and with not three 4 x LD a Table x and keep going Until that is zero and if it's zero then We have reached the end So at this point we know X is pointing at one of the commands or The last thing so in order to get a command index. We just have to Return so this should give me the command index in a on exit. Oh LSR a all right so There's 204 C wasn't it we as we moved all this this is the test for the empty command line So here is where we pass it This 206 5 is decode So break out and continue right We are going to try for a type command So command table index FCB index Load a byte from the command table that will be a D Compare it with let's just Here's the command table. Here's the file name So it's not a D So we break to here X to a Round down add for put it back in X. So X is now for pointing at the next command load the byte and Check to make sure that we haven't hit the end of the table So back to the beginning This is era which is also incorrect Reset the FCB index load compare doesn't match Go around again Right this one should match load compare Okay, looks good. Have you to the end of the command? No go for the next one Okay, go around again and the last time Y is now for We didn't take the branch. We're now here. So decrement X to keep it in range No, no, that's not going to work Because If the first character was invalid we decrement X and it puts it in range of the previous command but anyway this Hang on if the first character is No, we can only get here if Y is four. So this is fine. We have X must be X must be pointing at the beginning of the next command Yeah, that's fine So decrement X Check to make sure that the next FCB byte is the space and in fact we know Well, the Y must be four therefore this is always going to be character five That's makes no difference to the size, but so is it a space? Yes, it's a space we are here TX X is OB so put that into a and shift shift shift shift leaving a as zero Because I don't want to shift it by four. I want to divide it by four which is a shift of two Okay, so tight We're here. So let's just skip over the decode and a is two Zero one two Good Let's try user skip over the decode A is five zero one two three four five good Right, let's try something difficult decode A is six excellent. That's exactly what we wanted and Just in case let's do a dir a zero right That wasn't so bad so that has Decoded the command and We now know what the user actually wanted to do so now we want to execute it and we are going to start with type Because this is the simplest But first we actually want a dispatch table so Type Save user Transient okay, so let's just put all of these in here are type save Ren user Transient okay, so this is actually the same It's actually the same execution code that we are using in Like everywhere else So where did I put it here? That's Truly That's using a pointer to do it Actually, we can improve this code But I'm not gonna do that now. I'm going to go with the simple code so the way you'd improve it is by Pushing the values on to the stack and then Doing a RTS However, this requires all these addresses to be one different so Yeah, so we're gonna create a general purpose pointer and We are going to create a called temp Which we can JSR in order to get a call to a subroutine so now we should have a You can now execute commands All the commands I'm still not convinced about that carriage return, but anyway, we are at We're going to implement type now for type to work We need to pass a file name and luckily we already have a You already have a routine for doing that And we're gonna have to have us. I mean we can read we're not touching the command FCB again in this piece of code But we actually Need more than one FCB anyway so we are going to pass the parameter into user FCB if it is invalid We are going to print a message. So this is this is BIOS and BDoS right string and we now want to So at this point, we really want to do a long jump back to the top and reset the stack but It's actually easier to just do to do it like this And yes, we could do a BCS to here But not once we put all the rest of the code in so anyway, we now have an FCB We want to make sure it's not empty Actually, I don't think it matters Okay, so we now want to open it and we know how to open FCB's Which is like this So we open the FCB that seems to work fine If there's an error we print a message In fact, we also going to put a new line in here like so We are now going to I'm going to reuse the command line buffer for this because we now no longer need it so So this is a loop where we set the TPA The DMA rather you haven't done that one yet we read a Sector and then we are going to print it. Oh, yes, and this needs to Break if carry set so Read a byte read a byte into a increment our counter Until zero At which point we reach the end of the sector and we keep going until we reach the end of the file In fact, there's one more thing we want to do Which is to check for a end of file character because CPM doesn't track the lengths of files in bytes there's a convention that File text files end in a control Z character and if you use DOS this will seem familiar and if it was then we want to dot By printing a trailing new line so now That assembles but it's not going to work because there's one thing we haven't done yet Over here in the B DOS Do you remember back when we did the things like open file and We processed FCB's by converting them by taking the drive byte and turning it into a Zero base drive byte with the user number in etc. Etc. We stashed it in old FCB drive Well, we're gonna have to undo that on exit So where's our here we go So the first thing is to Is to reset that so now we can do old FCB drive If it's positive that is something has written to it then this means that old FCB drive contains the drive of The drive that used to be in the FCB That only ever happens when param is an FCB so we can do so that should work and Turns out that we do have a read me dot text file which I actually now recall Doesn't have a control Z terminator on it, but never mind. It'll just print some zeros So we run it and we do type Read me dot text and we press return and Nothing happens. Of course, it doesn't work. What was I expecting? Did it even print a new line? That's not doing anything Is it so it obviously isn't calling entry type? Let's use the right register Okay, I won't edit that bit out So press return Interesting, but it did something. I saw a C exclamation mark appearing So we set the DMA. We try to do a read. I don't think there's anything particularly controversial about that code. Oh, well Looks like we're just going to have to debug our way through it again This is the CCP Nice to have some symbol information and so on but of course no debugger will know where the programs have been loaded This looks like out of spatch code 2081 Okay Type read me text. Yep We are now in type so next Printed a new line Pass the FCB FCB looked fine and that went in to Two three oh one. I know what's happening What's happening is I am forgetting to consume white spaces before calling pass FCB However, I am curious to know why Okay, and there is actually something else we need to do in the dispatch code in the BDOS Here we go We need yeah, we need to preserve the carry so we are going to push the processor status word and We're going to push the processor status word and pop the processor status word So let's just give that a try and see if it's still No, this still doesn't work Okay, I'm going to have to check to make sure the errors are propagated correctly because I bet they're not but over here in the CCP under Pass FCB. We do actually want to put back that call to skip white space We have called it here To check for an empty command line, but it'll do no harm to do it again Okay Does it work? Oh? Oh That's better but that's because We haven't put in set DMA address like so and That where does that live probably under disk system? This is trivial user DMA 0 Okay, and Yeah, let's try that run well Yeah, that was a thing so This text is Actually, what's in the file? So let's just put our control Z in here The problem is it didn't print the first four characters of it Why and we do expect garbage at the end Because no, we don't expect garbage because it should have read a complete sector. I know why it's because a sector is 128 bytes and What I was doing here was expecting rollover to set it to zero so so nasty hack whoa Okay, maybe that nasty hack was not quite working So what's supposed to happen is that we start at 128 and work up and by We compensate for that by subtracting 128. This means that when rollover happens at the end of the sector You need rollover from 255 to 256, which is zero Rather than from 127 to 128, which is not zero. So why did that produce garbage? Okay Luckily that remained in the same place We Pause the FCB we open the file. Well, let's take a look at the FCB which is in 2304 Yep There is our FCB file name and we can tell by the way that these values are populated we have the Current the record count is one indicating there is one record We have We have right This is the The high byte of the extent it set the top by this top bit to indicate that this file has not been changed and therefore We can just discard it on close and we see a allocation bitmap with block for being hours All right, and I did not mean to type an end there Let's try that one again. So We're calling set DMA where we're going to be writing to 2263 which as you can see is the command line buffer and In fact, this is overwritten the previous Sector So it has read the sector correctly. We can tell that So anyway, we want to Set the DMA we now do a sequential read, okay 2263 here is the sector we read now. It's not carry set that failed Why did that fail? Carrie is set. So we actually Break to here to the end of the file we Print a new line and we go back to the top of the program So let's try type it file. It doesn't exist. Well, that ain't right So clearly the second time round it is not reading the file properly Is it not resetting the FCB? I mean here is the FCB we had that looks fine that should have worked Okay, it's easy set the There's the FCB. We are Going to read it step that takes us into the BIOS. Oh, that's interesting That drive should be a one Don't think this is working does LDA set the Sets the negative flag. Yeah, that should have worked Read sequential returns an error code So it's overwritten param Okay, let's add another Let's add a word variable So param is going to be the input parameter our param is going to be the return parameter Or I could Just push it onto the stack so push a Tx a push x t y a push y This is very traditional. This is this is the how you push your through your three registers and status bite on to the stack on the 6402 the 65 co2 Has push x and push y instructions. So we now want to do this in the opposite order. So pull a ta y Pull a ta x pull a plp return So now in we on to go and find all the places where we transfer x to a A a with 3 1 x is there's still a copy in in a for con out and Return Con out doesn't touch the return value Right string doesn't touch the return value Read line doesn't touch the return value new line reset Honestly reset might open file Returns the carry flag Read sequential Does return error value Login drive don't know about Okay Let's try that again Hmm, so it's only the first time that that happens. Oh Only the first time after a hard reset Very interesting So where was our breakpoint two oh eight one? Okay? New line pause FCB open the file That has correctly opened the file, but it hasn't put the drive back the way it was So something in the BDOS is still wrong. So convert user FCB loads the drive byte copies it to old FCB drive Okay, open file Calls new user FCB So that will fall through here. So is param Not pointing at the right thing Okay So we our BIOS is loaded at 1 9 0 0 So I'm looking for CCP. Oh No, that's put at the end there Couldn't open CCP is here Bios entry point unimplemented is immediately after That looks like our push pop. So let's go from 1 9 C 0 Yeah, there we are So let's put a break point at 1 9 C a continue so Okay, so this is exiting from Read string Oh, yeah, and Those are our registers So load the FCB drive value Which should be ff it is That is negative So we skip on So let's put a break point 3 4 5 Get rid of the one at 1 9 C a and continue clear Okay Wait, yeah, okay Uh So this is probably open a is zero that's the old drive that's wrong So param is 2304 So if you look at that we see it's our readme.txt Oh, oh, oh, this is actually correct This is correct. Um Because we didn't specify a drive letter in the command in the path there Therefore it's going to be using the default drive But anyway, it does actually seem to have opened the Yeah, that's that's all perfectly normal But at least we seem to be in roughly the right place to debug the read sequential We're just coming out of this now We are here. So set dma Read sequential by our bidos entry point So we store our parameter in 4 5 We make sure that old FCB drive is ff We compute the jump table stuff and jump Right, we are now in Read sequential we convert the user FCB So this is actually the first time we've used the default drive thing So load the old drive which is zero and stash it extract the drive if it is negative Which it is replace it with the current drive number Which is zero That's correct. I'll put that in a Save it as the active drive or in the current user And update the FCB That's all zeros. So that's fine. And then we go to select active drive Which is here now We call the bios to make this disk active Did it work? Yes We copy the dph Yeah, I'm going to assume that this works so Because we know this must be at least a little bit reliable or Uh, we wouldn't be loading the ccp So break 1 edf Continue, okay So this takes us back to read sequential Which is here okay load the Current record Which is zero and the first record load the record count Sorry compare with the record account record count, which is one. We're not at the end of the file Well the end of the extent rather so that skips ahead to here get the f the disk block value in xa Again, we can assume that works and it has it indeed Our xa is showing block four Store it in current sector Just check whether there's a block allocated which there is so we go here current Yeah, so we load the block shift, which for us is three And we start converting the Block number to a sector number Add on the current record number Which is zero Well add add on the the sector offset in the block, which is zero so Move the fcb on to the next record from last time Y is indeed still pointing at the current record So if we look at two three oh four, we can see the current record is now a one Set the user dma by calling the BIOS 1c6 def 1c7 zero done Read the sector, okay And exit put the fcb back the way it was although nothing has changed so Whatever we're now back in the ccp We've just done a read sequential why with 128 Store that in temp Okay, it has correctly read A sector worth of stuff and that looks like the right sector too, which is you know nice So load a byte A is e8 Why is zero type read me dot text There you go Does it work again? No But at least it worked the first time Well, that's irritating But at least we have successfully executed our first internal cpm command So it's now quarter past 11 and I want to finish so I am going to finish here Let's go and figure out what's going on there next time shall we See you then then