 day eight Let's see if we can make this thing actually run programs. So last time I was complaining that the CCP was currently being loaded at the bottom of memory. So if I fire this up and do free We can see that our memory is extending from 2100 to 7000 on this unexpanded BBC Micro We want to be able to load programs at the bottom of memory and as the CCP is going to be loading them We need to put it somewhere else. So we want to put it right at the top Apart from anything else this will make it easy for the program if it doesn't actually need very much memory to preserve the CCP at the top of memory so that it can just return to the CCP rather than having to warm start CPM and load it off disk again Now we can't just load it at the top of memory minus file size firstly because we don't know how to fetch the file size yet, but mostly because The pro all programs will use more memory than their file size because of the BSS If I find our linker script so the program is made up of Five segments. We've got zero page which is not stored in the file that just defines what zero page values we're using code RO data and data which are defined in the file Which are the things that get loaded off disk and initialized and then BSS which is uninitialized memory and our CCP is Using BSS for you know Buffers and so on, you know variables working storage so what I have done is I've changed the binary format Let me load that up in a hex editor CCP.SIS so at the top of Here we go. Here's the definition At the at the beginning of the file we have two bytes and a word first byte 07 is the number of zero page memory locations that this program is using the second one is the amount of actual memory it's using and The word is the offset in the file to the relocation table, which is 04b4 for the CCP 04b4 What I've done is I've changed the definition of this the second byte To be the number of pages of memory required to load the program Which includes relocation? The relocation table of course get discarded after loading so these will overlap with the BSS so we compute the maximum of the amount of BSS and the size of the relocation table and Set this accordingly that means that if we have six pages of memory free We know that we have enough memory to both load and relocate the binary and Also to run the binary, which is a different thing and to help this I've also changed the format of the relocation tables So rather than being a sequence of bytes It's now a sequence of nibbles as most of the bytes were below 16 This is way more compact by about, you know, 50% and So this makes sure that the relocation table is likely to be smaller than the BSS as The only program we're going to be loading high is the CCP and CCP is quite small Then this was probably overkill, but It's done so it should all now work safely. So in order to actually make this work We are going to have to look at the header of the CCP to figure out TPA usage Allocate that many pages from top of memory to get the base address and load it there So let's have a look. This is what happens in the PDOS So warm start code. This is the stuff that's actually like Loading everything Previously we were opening it Loading it at the base of memory in a simple loop Patching and relocating it and running it. So we need to Read the first sector. We need somewhere to put it We could put it at the top of memory, but the maths would be work So let's just put it in the directory buffer, which is currently not being used So that would be we have to set user DMA To One load One record Computes the load address The load address is going to be We need to get the top of TPA page number in X We then want to subtract The number of pages needed This will then give us the base address which we are going to store and the The low byte is of course going to be zero because everything is page aligned we now want to copy the first record out of the directory buffer and into our Interram so that will be Okay, we want to do this But user DMA is not in zero page therefore. It is not a Pointer so we can't Until negative so could we I mean is it worth putting user DMA into zero page that adds one two eight to it Set the DMA address So here's another place where we're using it as a pointer Putting it into Zero page would mean we could just use it directly here Actually You know what this code is doing this is copying 128 bytes out of the directory buffer Into user DMA We can reuse that Okay, so if we go back to our entry code which is here We are actually just going to call copy directory buffer to DMA We then want to advance the DMA pointer to the next record as we know we started at Actually a one and actually a zero Because this is the high byte who calculated and then the low byte is zero We know that the low bytes started at zero therefore. It must be eight zero now Okay, we now get rid of this code so We read the rest of the binary Copy directory buffer to DMA has used temp zero let's actually You want to keep Actually, I don't think we can use temp because I bet this is going to use it Okay, so Instead we're just going to push this and Apparently I thought of that idea Because this is pulling it back off again. Okay, so this will get the base address of the program So we then poke the entry vector Then we want to relocate it our relocation code is going to need to be changed Currently what it does is Actually, I think this will work like once The way it currently works is you give it a pointer well The way the code currently works is you give it a pointer and it fetches some numbers out of nowhere and Uses those to do the relocation and we're going to have to be a little bit more focused So We are going to Set DMA So what we're going to do is change it so that it starts relocating at the DMA address which is why we've just called set DMA and It then takes as parameters the Thinking thinking no, we don't need to do that We do need to give it some parameters So a is going instead of surpassing the address in an x a a is going to be the page number of the start address and X is going to be the Zero page start because we're also going to want to put those high We haven't done anything about that yet so compute the load address Get ZP top of zero page is in X Plus ZP usage and We don't need that yet at all So we just push it in fact We're going to want to put that here so that We consume the load address before the zero page address and Why am I pushing the low bite of the start address because we know it's zero So temp zero one contains the start address We want to So that's the top of zero no that is the start address of zero page that we computed So just stick that in X Then we call relocate then we Calculate the entry point and of course this code here is This is all a waste because we know it's we know the start address is 256 bytes aligned therefore the Low byte is going to be zero. So we don't need to do any of this addition we just need to do store temp plus zero and Go okay It does not assemble Biles get ZP. I haven't put in Get TPA Get ZP Okay, that won't run because we haven't done the bios side of things relocate or rather entry relocate relocate an image high byte of Memory address is a Zero page address is in X. So This is setting up the load pointer So high byte goes into pointer plus one Low byte becomes zero store memory page PHA Actually Store zero page Start restart Okay, so then we compute the start of the relocation table and that doesn't change Here we need to set X to the that goes away To the the value we want to add on currently it's doing ZP base i.e. the base of zero page So we're actually going to just change that to PA LA T a X But as this code does not use X Okay So then we go to the second pass where we relocate the the RAM addresses so we want to pop the memory start Reset the start to reset the pointer, which is going to work through the code relocating things We also want that to go in X. So We are going to do this one first so that we can just do this So that should actually be smaller code so Is it gonna work? I'm gonna guess no I was right and that will be because I forgot to update this stuff this is Where we load the BDOS which happens from the BIOS, so we're actually going to mood the BDOS Here and it still doesn't work. So this Should have done the right thing. Okay, let's just step through it rate 100 go boot So we print the banner figure out Mem start and end these two calls to Osword Load the image Do the relocation for to a So a becomes one nine. That's the address where one nine. Oh is where we loaded the BDOS X becomes two. Okay. Those are both correct. That is the start of Where the BDOS zero page is going? We then go to the relocation code we store a the memory start and update the pointer in zero one so 0019 that is the address one nine. Oh, oh We add the relocation offset so Yes, that's actually mean stored at rello pointer, which is Self-modifying code because that makes always makes things better So two zero five five that believe is the right address. We now start relocating. I Think this this looks Fine. Okay. Well, anyway, let's just skip over that so This is now partially relocated These addresses look wrong that looks like it has that looks like it's got zero page and No, it doesn't oh six. Oh, I haven't done the rest of the relocation That's why those are wrong. Let's just break it one nine oh four. Have we reached the BDOS? We have reached the BDOS and now These addresses look plausible So this is going to load the CCP So this is the bit that's clearly going wrong Okay, let's skip through store BIOS pointer clear some variables update the memory regions So that we don't overwrite the BDOS stuff call get tba I called what my house now we call get tba Yeah, one nine seven C You know this stuff works We're now here reset the stack New line reset Open the CCP file. Yes We clear the C we clear the the FCB here. We now open it It's open Read the first sector Into user DMA Really? Directory buffers a pointer, isn't it? Yes, it is So this actually wants to become this Good and I will just add one thing to the CCP in Here to just print the CCP address Because this is a useful thing to want to know because this tells you how big programs can be Without wiping the CCP and we know our start address Which is an entry so we adjust going to high byte of entry print x number print zero new line and exit CCP has loaded at seven six zero zero excellent and Everything still seems to work which is nice I also fixed a few things with user so that now it Understands decimal numbers and prints them correctly and if you try to pick an invalid number, it doesn't work Previously it would just start printing gibberish due to bad code okay So we can start Loading transient programs So we want entry transient now Command we're looking for No, I'm not sorry that it's already done Okay We have successfully yet inside transient We already have the command FCB parsed and ready to go so Let's just try opening the file. We've got code to do that somewhere Passed it. We want to open it and we're not using command FCB We're using so I'm not using user SP using command FCB so Cannot open is going to be a slightly different Message Traditionally in CPM if you try to run a program that doesn't work you just get the file name and a question mark so That's actually going to be branch if Harry sets to cannot open because we're using our local one rather than the global one So we are going to load Command FCB plus X FCB name one by Y Compare it with is it a space? Yes otherwise increment Y Compare Y with Eight this means that we have run out of Characters keep going until Y is eight or we're looking at a space So replace that byte with a zero load X a with the String pointer print it print a question mark New line and exit. Okay. Let's see what this does So if we type a command that doesn't exist Good that has worked if we type a command that does exist That still doesn't work because we need to put the extension on so if the first Extension character is a space then there is no extension so We want to write com to Those three bytes That's going to be to Store that at command FCB T1 like this until negative so not No output that means it is correctly opened CCP there is no such file CCP comm CCP sys No output it has successfully opened the file. So if you type the if you supply an extension Then you can run any binary. Is that a good idea? I can't actually remember what the original CCP did Let's have a look if I can find the command table Directory arrays type save rename user. Here we go user funk So this is something that we have to be aware of if the user can just type something like a colon to Change disk and that will actually show up here So we have to test for that too so this will happen if the first file name byte is a space and The drive byte is not zero If it is if this is the case then we want to Save it. We didn't specify whether this is zero based or one space Get drive returns zero based number so This is actually going to save the drive Save the zero base drive. We now want to select that drive. So it's still in a So that would be B-dars select disk and then we're done and we should never get here the only time when we should see a space in the file name is If The file name is empty This means no command line. So we shouldn't get here at all or there is just a extension Which case we want to fall through into the normal code? Okay, so we've opened the file We now want to go through the load process. So this is the same code as in the B-dars But different So we mute the start address This is going to be loaded low. So this is actually fairly straightforward to do So all we're going to do is call Get tpa therefore x Put that in x which is the high byte Load zero into the low byte Set the dma address We now want to read everything Not just the first sector because we don't need to look at the header for loading low So our load loop is Here so one actually there is one thing we should be doing Which is making sure that we have enough space to load the program But I'm not going to at this point We also want to advance the dma pointer each time Do we have a pointer? We've got a pointer 10 that will do fine That needs to be a B-dars Here we want to actually want to load 10 to 0 Here we want to advance the pointer That's the same code Okay, we should have read the file So we haven't done B-dars read sequential and we haven't done B-dars get tpa because it's actually BIOS gets tpa Well, that was wrong hmm, you see We're loading the ccp just below video memory so if We end up loading too much data It will overwrite the video memory. Yes, it is. This is a bitmap font so you can see data there So this is telling us that we need seven pages of Ram Which would seem to be correct So why? Well, most of that stuff we saw was empty zeros Why that would be our BSS and we are using Two lots of 37 for the XFCBs plus 128 Yes 37 so that has actually this is more than a page of No, it's not knows it's under a page of data So this is loading the ccp at 7600 We want seven pages So yes, that is using one extra page More than we expected It should be loading at 7500 Are we doing our maths? incorrectly So seven C Minus seven is 75 So that should have done the calculation here get tpa is exclusive The the top the top values here exclusives. They are I think it's technically a closed comma open interval So the bottom bound is inclusive the top is exclusive so seven C is actually the start address of the video memory Okay, so Break out one nine seven F Right get tpa has given us two one seven C Our directory buffer tpa usage byte Is that's a pointer I'm actually amazed it did anything useful, right? That's better That's not right Because I forgot to load this There we go seven five oh oh That's much more sensible alright, so if we run not It loads it and It will have loaded it to one oh oh so we break Dump to one oh oh, I haven't figured out a keyboard shortcut for bring to front yet This is however garbage that does not look like a Program This looks at relocation data a program should have a jump instruction here This is a pointer. That's correct. Ah in fact Is it safe to use Directory buffer here. Yes We can't write to directory buffer from directory buffer Because it may be that we need to advance to the next extent which will mean loading a new directory entry Which will cause write it into the directory buffer, but we can Read into it Because it may read a directory buffer It'll then update the user fcb. You haven't done this bit yet And then we read the sector from the user fcb and overwrite what was in the directory buffer Because it's no longer in use at that point Now I say that this looks like relocation data, but it doesn't end in an F So that's weird So this is what we expect to see this is our not program So has that loaded at the wrong place? Let's try this again the what we're looking at is the The remainder of the BDoS's own relocation data the BDoS extends from from 1 9 0 0 up to Here That's the last byte. I believe so we get relocation data from then on But the BDoS isn't using anything from then on Well, is it the BDoS's own BSS starts here from the BDoS has a reasonable chunk of BSS Actually, that's not a great deal. It doesn't have any buffers buffers a hold elsewhere So if the buffers all fit Here then yes, they won't extend above 2 1 0 So this suggests that we have in fact failed to read anything into 2 1 0 So we should be storing the actual address in the temp pointer Because set DMA because read sequential. I Think this is failing immediately for some reason. It's not reading a sector Okay 77cd load our address 2 1 0. That's what we expect Call set DMA Here we are in set DMA just stores value and returns Okay, read sequential. Here's the stuff before Read Carrie is not set but it has not Actually done anything. Yes. So then we go around again Go around several times. I have not set the the FCB address so That's probably left 10 0 1 in the FCB Yeah, that was just reading garbage from wherever on the disc. That was never gonna work If you pass in an invalid FCB all kinds of weird things can happen Okay, so let's just continue Read one sector Let's read the second sector and the file. So we break Look at 2 1 0. Oh, hey, that looks like a program So here we've got our header No zero page one page of TPA relocation data at Oh Oc which is here Here is our jump instruction needs to be patched and here is our program good Okay, we now want to relocate the file So this will be straightforward like so We want to Put that in the high byte set the low byte to zero and call You don't set DMA and then we can call BIOS relocate so This is setting the point into the loaded image and at the same time Saving the page number so you can put it into the address there We are going to need a BIOS function. So not these Drop back to the prompt dump at 2100 and we see LDA 0 jump to 2 1 0 4 2 1 0 4 is our BDOS instruction. This is now being fixed up good We now want to patch the jump instruction for which we again need the address, but this time in a pointer so Okay, and we can get rid of this because we actually have this in temp pointer The BDOS Instruction Jump instruction so this is Com header we're going to copy it out of our own header BDOS plus zero and then execute Now this is pointing at the last byte of the BDOS So in fact what we want to do is I'm head to entry. That's the entry point Store it in 10 plus zero and go Okay, so not Brilliant that should have just restarted the ccp Not will have Jumped To the warm start routine in the BDOS which will have reloaded the ccp So I think that might will be working Can we do anything more interesting? Well, just remember where I put again. He's under apps I put not ccp I Should put these in a library to be honest Excellent it works so we can read files We can run programs That basically everything from all the you know writing to disk stuff so Let's let's write a small Program No, we can't write a small program yet because we haven't actually done any of the stuff to do with parameter passing So there's no way to get a parameter from the command line into the program. So let's do that bit Now I thought of various ways to do this. I was originally wanting to put all this stuff in the bottom of the stack page but We need 37 bytes for an FCB plus 128 bytes maximum for a Command a command line and that's uses up more of the stack page than I'm really comfortable with So we're not going to do that instead. I Am going to Allocate a chunk of memory in the application workspace This is going to come just before the bss which Is going to be used for parameter passing I'm going to call that P block and This is going to contain a pre parsed FCB Well an X FCB really Which will contain the first parameter and possibly the second parameter plus a 128 byte buffer Which is Into which we are going to write to the command line tail We have not defined Okay, missing memory aero sign with a segment P block. This means it's not in our Linker script, so we're going to have to put that in So that's going to go just before the bss P block and due to the unique way in which we are doing our linking Where we assemble each program three times slightly different addresses and then compare the result Why I have to repeat that three times Okay, oh Segment P block does not exist Okay, the BDOS won't have one The CCP won't have one But not will have one and We're seeing this message twice, but not three times so these warnings are coming from the BDOS and the CCP I think I can work around this By simply making sure the segments there But empty in the bear header Okay, so this should have made no difference to our not program which as you can see It is now way bigger than it was at 30 About 45 ish bytes so Here we actually want to Copy the command line Into the user buffer Into the Programs P block so the the The reason why I put it above the bss is that it's going to be the first thing in memory after the program It's going to overlap the beginning of the relocation data So we in fact it is at the same address as the relocation data so Calculate address of the programs P block so we know that temp Contains all the entry point of the program so And we're actually going to do this up here We're going to want to modify temp So we can either do it above this at which point we have to Reconstruct it further down or we can do it further down at which point we have to put it back the way it was after modifying it here and Honestly, it's going to be easier to put it back the way it was So now we are going to want to add on rel offset here, so But of course we're trying to add it to the pointer. We're reading from so So xa is now the offset To P block we want to add this to temp so The reason why we did this going with a decrement and starting with the high byte is so that we end up with Things in xa in the right order because the low byte is now in a so with this is just going to be add 10 plus 0 store 10 zero transfer x to a Add 10 plus one store 10 plus one okay Want to copy the command line and that's going to start at the size of one X fcb and we don't have access to X fcb in this Include file Okay, they're just gonna have to fake it So This is going to be Going to work. I'll have to look up how to get the size of the structure. Give me a sec size of Obviously, and we want to skip the size bite Okay, so X is going to want to be the command offset But first we are going to Skip any white space. So we now we want to copy bytes so that's going to be Z repeat LDA command line by X Store it in temp by Y So this will iterate copying bytes from the command line buffer into the P block Until we reach a zero. So now we want to Set the size bite So the number of calculate the number of bytes that we have copied We are just going to Basically just calculate the number of bytes we've copied and store It program is getting too big Well our not our program our loader Wait a minute. We don't need to do that We can put the whole block of code here in an if statement if carry set then Go do the cannot open stuff This is why I wrote these macros. Okay Now, let's go over here to not In fact first let's go back to the CCP Because we are going to steal this And this also wants con out. So we're going to steal to and what we're going to do is is Is we're going to write a little loop that's just going to dump the command line in Hex So load a byte from the command line Command line plus one because it's the size bite Actually Yes, it's dumped the whole thing for now And then we print this as a hex number And then you print a space Then we are going to increment index Load it compare it with 128 Until Equal and then we just stop we haven't done space Okay, so now if I type not then it hangs naturally As it actually hung here or In the CCP Well, I would like to trace trace through the CCP code. So Let's put Okay, so 7 8 3 4 5 6 7 Okay, so We are stored temp to Beginning of program Oh, yeah, this is now because the CCP is loaded high. We've also loaded all the zero page high. So, yeah, eight C so that means that our Address Yeah, our address is at 2100 so we are Calculating the P block address So X a is the offset to the P block Which is 01? Oh one I don't think that's right It's not right but That is what is in the header the thing is the relocation data looks like if it's there Which is not oh 101 was it oh 101 in the File Yeah, it was okay, and yeah clearly the file is not 256 bytes long So that means my Program is doing the wrong thing. So this is the tool I made to produce my binaries and All it does is it? Assum it links the program three times using the different link script and then by comparing The binaries with each other It can tell where all the relocation bytes are so link Z Links it with zero page one hire Link M links it with the memory address One page higher. This is a very old trick that dates back to CPM itself so a Minute I know what's wrong It's got nothing to do with that it's because This piece of code this piece of code sorry is Trying to get the offset to the relocation block, but I've just bumped it up by a five bytes so in fact This wants to be FCB Because the BSS comes after the P block. What do you mean it's undefined? It's right over here, of course and we don't get a P block if Unless it's a com file Okay, that's better Do that I wanted to do I didn't want to do that either. I want to do that Okay, so 45C 5c here is the relocation data excellent Now I wanted to do that so we run not and It hangs Because we still have our infinite loop three four five six seven we're there so load the Start address of the P block into XA which is 215 C Which is the location of the relocation stuff? We now add it on to temp we copy the command line Into the program's P block so the command line buffer in the CCP is at 7 a af and Consists of just a knob now command offset, which is in X is three zero one two three Also be command line plus one it doesn't include the size bite so this with this This is going to copy one stray bite that shouldn't So we store it increment increment Compare with zero to reset the flags Go around again get a bite, which is a zero Store increment compare with zero branch We're here didn't I write some code here to Set the length. I thought I did I must have accidentally removed it Okay, so that wants to be Dx a sec sbc command offset ldy Let's just try running nop and see what happens of course it hangs I Did take that thing out didn't I I did okay? Well our program loads at 2 1 0 0 therefore The entry point should be at 2 1 0 4 So let's just see if not actually runs it Okay, this jump here is going haywire for some reason so if I Where do we go? seven seven eight three four we want to break out be here Let's get white space copy the command line Yep, that was nothing so compute the length of the of how many bytes we've copied That should be zero one Well, we did copy a zero So yeah, that actually wants to be one less than it was You can do this That is one byte cheaper We lose three bytes because the end loop is now a jump This is to a two byte thing However previously we were spending two bytes for the compare two bytes for the branch and One byte for the decks. In fact, it all comes out the same So I'm going to go back with the old code because it's clearer. No, it's not the new code is clearer. Yeah Okay, by the new way we are storing the wrong value into the the buffer which is at That doesn't look like a pointer. Have we overwritten The pointer somehow did this do garbage? Okay, so HC our pointer is that HC to one oh oh add on the Calculate the offset into X a X a's got X a has been fixed up. That's why Right, we don't need this So the the offset field has been fixed up by the code above the relocation procedure so that it's now a absolute Address Yep Good that actually simplifies things so run not Ha still garbage still garbage, but we're getting there 7834 want to break at Okay, we are here Yeah, we never actually wrote it into the Into the pointer, that's why I didn't work whoo We have 250 128 bytes From the command line buffer, so we start with a zero because we didn't supply any command line tail So if you now do not hello world world then It hasn't worked because We never Actually did the offset properly Should just be able to know we have Okay, so size byte zero Terminator byte zero and left over garbage H e L L o space W o R L d zero Looking good to me All right, and there is one other thing we want to do So we are going to pause the command line and Stash the fcb in the p-block so the program doesn't have to pass it itself in traditional CPM the fcb parsing code Only exists in the CCP Which makes no real sense because there's lots of other things that go what gonna want to pass fcbs And as you can see from all that code that we wrote further down the other day It's pretty nasty so our Pars fcb stuff that actually passes an x fcb We pass in the address at xa so temp is pointing at p-block So we just want to do 10 plus zero 10 plus one Pars fcb now it's Not actually that as easy as that Because we pass to Commander to fcbs and these go into the first 16 and the second 16 bytes of the fcb If you if the program wants to use both It then needs to copy the second one somewhere else because as soon as you open the first one it'll overwrite the stuff so We want to load 10 plus zeros you'll see ADC 16 LDX 10 plus one If carries set increment Fcb Okay, and we're gonna change not so that Instead of starting a new line. It's going to start at fcb not So here is our fcb. It's empty. No drive. No file name Here's the second one also empty. No drive. No file name not Fnord passed Not passed and The command line tail should be down here somewhere. So foo bar nothing Okay, what has gone wrong here We've done this carefully in this order So that we are copying the command line without advancing command offset so that we can then Pause the fcbs here. Why is that not shown up at all? but something if there is a natural regression so so let's just put a breakpoint in and and Debug through it. It's the quickest way to figure out what's going on. I've said it before and I'll say it again I can't imagine doing this stuff on a real computer with no debugger break See seven eight four five six seven here Just happened anyway, we've got the address now, so let's do that again Okay, we're here at skip white space next Right, we got a which is a 46. Okay. We're writing it to the command line 8c is 215 e which is what we expect Okay relocation data Wait a minute 215 e That's not right. No, that's correct. We've writing it here. There's the f that we just put in so this is going to Iterate round until we reach seven eight five seven Okay 215 e Right, so our X fcb goes here Here rather and then we have the command line parameter there. Okay. That's fine so We calculate the length of the command line is five Bites and we store it. There we go. There's the five We then pass the command line. I think pass fcb is overwriting the command line I think this is wrong somehow anyway, too Yeah, there's our fcb at 215e command line still there Set the user number field, which is user number zeros that will have put it there The only difference between an X fcb and an fcb is the user number field on the end So we wipe it Okay We now wipe four bytes of metadata. In fact, we're right. We're wiping Everything oh Oh It's the second fcb So this is wiping all the allocation data. That's the So we've got the file name in the first 16 bytes and then the allocation data in the second 16 bytes And then the current record the random access number and the user field in the third 16 bytes And this is wiping it all. That's why it's behaving the way it is so actually We are going to have to Pause things in the other order That's annoying so Okay, so pause the command line into an fcb. So we want to Command line position white space load Command offset Pause the command line into an fcb. Okay Copy the command line into the programs P block PLA Get saved command line position DAX so we need it again here Let's just put it back into command offset Okay, let's try that that's better, right foo Puzzle into an fcb bar parts into an fcb and then seven bytes f o o space B a r Okay Command line parsing works. So let's go over here to not We are going to Try and write a dump command because I want one so This one we want to include this. So what we're gonna do is if the The file name parameter starts with a space Then we got no No file is supplied so this is just going to terminate So that's just going to be the EQ Syntax error and because this is returning with RTS. It's important. We don't push anything on the stack Which is this is probably going to come back to bite me later, but so try and open the file We've done this already. This is the same stuff. We did in the ccp Go carry set to not open That's also wants to do a Here we're gonna do cannot Open which is the same code cannot open file so We are going to have We've got our We're going to have a word There so We're going to reset the address Read a line Stop if carry is set We need somewhere to put it Well, we could define our own 120 128 byte buffer, but it would actually be easier We already have the command line buffer that we don't need and in fact in CPM 80 Both of these things the stuff that we've called the P block exist in the 8080 zero page and on Entry to a program the DMA address is automatically set up to point at the command line buffer Set DMA, okay, so this will read one sector one record from the file. We now want to dump it so for every one Each 128 byte record is going to turn into We're going to use eight characters per lines. That will be 16 lines and once we've done 16 lines, we have to go around again to fetch the next sector So that's going to be dump one line and advance address if it's zero then this means that We have reached the end of a sector and we need to do it again And then we're done and notice the fact that we don't even have to close the file Because there is no state other than in the FCB structure. So we just discard it when the program exits Which is nice So dump line first thing we want to do is To print the address now. I actually got this wrong our address is going to be Three bytes long Yeah That'll allow us files up to 16 megabytes so yeah, so so spacer is going to be a thing to spare separate the three fields of our dump So we want to figure out the address into the record Which is simple because we just take the low byte of the address Mask it and we're done. So we're going to we need to want to put that Somewhere we don't need to put it somewhere. Okay, so we are going to load the byte print it print a space and I got that wrong then it needs to be no leading space there Instead we're going to put a space there. There's reasons for this It'll become apparent in a moment. We are then are going to increase the address Have we reached the end of a line? If so stop then we print another spacer Then we're going to do this again, but instead of printing hex We're going to print sanitized ASCII in the traditional format Okay, so we actually want to Do lots of 602 comparisons be right back once I've looked up my favorite table. I Couldn't find it, but here's a different one Okay, we want to be check for less than 32 so compare with 32 Branch if carry clear to Actually, we can if carry is clear then it's not printable therefore replace it with a dot compare with Got greater than or equals to one two seven If carry set then likewise Replace it with a dot and of course it's safe to fall through from here to here because dot is printable So here we just do BDOS con out No space, but everything else proceeds as normal in fact We do wish to rewind a Little so that here we rewind back to the beginning of the eight character String so we have Incremented address by Mmm-hmm, this thing is this may have This may have at the end of a line this will have wrapped round and Incremented the next bit along so I think actually we want to Decrement it Is there a better way to do this? Yes, there is Just put another we're just going to use a Counter we know that address is aligned So we can just or in the index so this will then allow us to just increment index load it compare it with Eight and then stop So then we can get rid of this stuff Index Index so now you see we haven't changed index at all We're gonna do that now so add eight if It is zero Then it's wrapped around therefore. We want to increment address plus one If that Set to the zero flag Then we want to increment address plus two. We're done. So just print a new line That's a end proc xfcb read sequential is undefined really Don't remember to link these things with the I Did link it with the xfcb library is there a There is a read sequential Xfcb reads. Oh, I have to import it, of course I would like to put all these imports in the include but That then causes problems because the import conflict with actual definitions further down so Beed off set DMA we can steal that from the ccp What's this doing? We want to move our two error routines up here, okay So run it If we then try to dump readme.txt what happens, of course it hangs What were you expecting and we're somewhere in the OS? Break a 2104 Okay That wasn't where I was expecting to go is this Because the program is now bigger So if I look at it in this text editor, it's now Just under 256 just under Just over Three records long. Oh, here's the transient loader debug 87 67 we're here Setting the length of the command line so 8c is 2253 this is where Where not P block is and you can see that it's got two FCBs in it I didn't give it a command line and The command line has got it is empty I Was too clever here. I forgot that The P block is not in the root page. So we're actually going to have to call I ask get tpa put that into 10 plus one Okay, that should work and I need to take out that jump. That's good. That's good It doesn't fit We are three bite the three characters short. I'm gonna have to make these spaces smaller and we're still going to be I Know we can make it work can make it work so in fact the spacer just goes away completely and Is replaced with a simple space and we also I want to Set the top bite of the address So not read me the text. Why are you getting two new lines? and This is garbage So after Dumplin, yeah, I think this is now. Oh, yeah, there's an extra space here. This is an exactly 40 characters across so It's wrapping around to the next line. So we want to get rid of that space. Yes. It's already added one here Right and the other issue is That yeah, I've done this in the wrong order. We order in the index, but after we put the Value into X that's better so I could also not not com and There is the program and Control C should work. Yep control C works and I didn't actually check to see whether the Yep, this counter is working the biggest thing we've got is ccp.sys Good good good So there's only one more thing to do which is to Rename it to dump dot s So that goes away You to edit the make file to change this to Dump, okay and I actually set this up So this is creating a disk image with our files on it Then I can load it up in mess Which emulates a proper disk and? I don't know if the Sound effects are showing up in the recording But they are well, but there are sound effects, but this gives you the timing of the real thing So now if I do shift f12 to boot it Okay, all right We do that again That's interesting. That looks like it's restarted the ccp After doing the dir it's not supposed to do that So you may be able to hear the disk moving to dump in tax error Dump dump com and you can see the pause as it reads another sector. Let's try the ccp It's taking just enough time to print each Pair of sectors that the motor is stopping spinning so it has to spin it up again next time But this gives you an idea of the general performance of the real thing Which honestly is completely acceptable compared to 80 80 cpm, but let's try putting this into Three oh, yeah 331 that will set the Level put the video in this is a BBC master being emulated the video memory is now in a External memory bank, so do free You see that we now have quite a lot more space available So let's try dumping ccp.sis again. Yes, the pauses are now more pronounced in the the BBC micro's bitmap screen modes are a Masterpiece of fast programming it uses hardware scrolling, which is why it's can shuffle 16k of memory that quickly Internally it's the BBC micro uses 256 bytes sectors and if there's a disk buffer So you can see a brief hesitation Halfway through each page As it fetches the other half of the buffer from the operating system And then a much longer delay at the end of each page as it fetches the new The new sector from the actual disk if I put it in mode zero, which is 80 by 32 That should be more obvious So we're not quite getting a page on the screen at once. I thought I calculated 16 lines This isn't right no, no, that is right never mind My terrible methodic skills are acting up again It'd be nice to be able to do a 16 column dump But as all the systems I'm targeting a primarily 40 columns apart from, you know, the BBC micro running in 80 column mode I Think I'll leave it as is the time being but That's some nice progress. That's basically the user land done There's nothing we're not going to change anything else. I mean we've done nearly everything There's a few tweaks. We should probably do like After running a program, we should probably check to see whether the program Try to change the current User and disk But that's trivial We need to do the ask get drive SDA drive That's it actually that that will allow Do we have a call temp? I'm not sure we do We do not. Okay, let's put one in The user is not stored Locally, it's just the drive that stored locally so that we can fiddle with the Drive Without if we can fiddle with the BDOS's idea of the drive, you know We are gonna have to do that for the user as well but later So, yeah, okay, that's great Added a dump.com User land is done. So next time we will actually start Writing files, which is going to be terribly exciting. Okay until then