 day 14 What we're gonna do is try and finish this thing off. We have one more system call to do Which is That's my system call table. Here we go Which is this one? Right random filled this is exactly the same as right random except if a new block is Is allocated it gets cleared to zeros rather than Just being left with whatever garbage was on the disk previously so Now there's several ways you can do this as I said last time Which is we can either duplicate this code and put in some logic to Wipe the block or We can Put in some kind of flag so that when seek to block and create is called it will clear the block Just thinking how to do it. So The bit that we actually care about is here this code path We call get fcp block that returns this block value If it was zero, there's no block has been allocated. We do some stuff. So we could put some code in here or We can call get fcp block Prior to calling seek to block and create and you know do extra stuff there. I'm just gonna put a flag in Okay, so And for reasons of efficiency. We are going to reset that to zero Okay so the way so we create a one of these and Then we call We need to make sure that the block that last flag gets reset on entry and in fact the cleanest place to put this is here in the Entry code So this means that whenever we call whenever the application calls the bidos the flag will get reset so Actually, I think that's probably it We need to also act, you know actually do the Implementation Yeah Okay, so If it is not zero Then we need to Wipe it to do that. We need to get the sequent the sector number Probably shouldn't call it twice, but it won't do any harm so we can Yes, we want a record of zeros So we are going to use the directory buffer for this because it's a handy 128 byte thing that we are not currently using so now we are going to want to Write this We actually have something for setting the directory buffer Somewhere in the directory code or not Okay, so we Seek how this works compute the sector number of the block in x a Okay, we should have got that block from somewhere and Yes, then we update current sector and Write sector here We'll actually set that so So we're actually going to put this here because set FCB block should Return yeah preserves x a so this will seek to our new block we then Enter the wipe code So we clear the directory buffer to all zeros. We set up the DMA So now here all we need to do is to call write sector we then want to Increment the Sector count if roll over if roll over like so We now want to find out whether we've actually reached the end Which we are going to do by ending the current sector with block mask block mask is the the mask needed to turn Sectors into records. So this when this gets become zero We know that we're on the next block Okay, so we fall out the bottom of this But we need We need to get our actual block number in x a which we can do by just doing get FCB block Because we now know that the block has been allocated and then we'll call get sequential sector number again To return the sector pointer to where it was before None of this is quick, but we're touching the disk so It's it's not the most efficient code in the world, but it doesn't matter Okay, and it builds So we now need a test program so We're gonna put this back a to I CPM FCB to zero Okay, we open the file If a if the open Failed we Create it that make file make file Okay CPM FCB R XR So this will seek to a particular location We then write garbage to file with CPM write random filled CPM FCB and close the file and this needs to be a Just a okay, so let's give this a try and see what happens so Created that file and we are going to write to record 100 We have a file Which is the right size? So let's take a look at our file system And we see this is test sub 15000 oh, yeah 15400 is the start of the next block 1k blocks, which are 400 So we have empty space Then our record worth of data Then empty space down to the end of the block. I think that worked And so we are going to also write to record 101 Still one block that should have been the same block What we've done here. There we go We now have two 128 blocks worth of gibberish So writing to a subsequent block has not Overwritten the first block Let's get rid of that and this time we're going to write to record one and we should be able to dump that and Okay, that has set the record count To two indicating there are two records in the block. It's written zeros to the first one Yes, this is what we would expect if we had done this without using the filled option We would have got this data in the second record, but the first record would be full of junk Although because this is all backed up by a moss file When you extend the moss file it will write You know won't allocate data But the virtual disk we're using is full of zeros So that's not actually going to Help Anyway, I believe this is working good That was surprisingly straightforward Okay so There's one and big thing that I haven't done yet, which is I want I Want some better error handling so we're going to allocate a byte For to record the stack when we start a system call so transfer x to s store in Entry stack Because these then allows us to Actually while I'm at it I'm just going to put this in This will just fail with safely with an error and not do anything. Okay, we're going to put in a error handler Brings the message in x a don't need to do that So on a hard error We print a message we wait for keyboard input use has to press a key and then we do a warm boot according to the docs So we don't actually need to exit the system call we're killing the program so This is actually gotten easier We're going to put this up here so we can fall through Do we have a print routine? I don't think we do oh we've got We've got right string here Yes, we can call right string because this will then Reset the stack Reload the ccp etc etc, so we want an example of doing this. Well, there's the right only disk stuff Sorry, I'm yawning on but the other thing we want to do is to check for Overwriting a read only File so that will be well our So right sequential is going to need to do this So what this will do is? it will check the The read only flag in the FCB which has been opened from the Directory okay for random access again. We do that there Ran right random field gets that by default so So we want to also put checks in in Rename but this time because the file hasn't been opened. We haven't read the FCB We want to check the rightful flag in the deer end and the same thing happens for delete file and And now it would be tempting to put the same thing here and set file address But that would make it impossible to actually, you know unset the The read only flag So is that all we need so you can get out of system calls Open and close on unrelated Because in CPM we don't tell the system whether we want to write to the file until we actually Try to write it. We've done delete Creates unaffected because create doesn't work if the file already exists. Yeah, okay. I think that's it. So This would be under Directory management, which is around here somewhere. So Okay So over here We can see that the read-only bit is in the top bit of t1 so So the FCB t1 LDA param y that will set the The top bit so in fact all we do here is branch to Not writeable error Otherwise return and Over here and check deer and writable. It's exactly the same thing except we use current deer ant So so what we do here is we load our Message into x a and we jump to Hard error, so let's run it so Uh Here is our dat file. Let's make this read only Like so and then try and delete it and the file is Hmm, why did that happen has that actually updated the Thing on disk. Yes, it has it set the top. Ah, I know what's going on here Yes This happened when we set the file to be read only It has actually set the top bit there so if we do that Read write It goes away. That's actually a bug in our ccp Because our directory Lister Here needs to be masking off the top bit So you go like so and we do the same thing here and Now this is just too big After to do that four one five is too big, right? This is trying to jump up to the beginning of the loop. This is common code and we can also Put the space in Okay, so that now fits so we set Test.com to be read only I can't you remember what test.com does now? It's one of the test programs and now we see that it is set correctly we should We should not be allowed to rename it Correct, we should not be allowed to erase it correct, but we can set it to be Read write and then we can delete it Good Okay, and that is also shrunk our ccp a little which is nice However big it's got to Okay, so back to the BDOS I Think the last major thing we need to implement is the disk writable Stuff and this is straightforward This just checks to see whether the current disk is in the read only vector Now we've got some code for doing this So we just copy this code So this is the This is the Right protect a vector Why is the active drive we shift so the flag is at the bottom of 10 plus 0? so we then Take the flag into the carry If the carry is set that means the flag is right protected like so So we load the value And jump to hard error Okay Do we have a way to actually test this? I think stat will do it so Stat val will give us here we go stat val gives us the help so we should be able to do a becomes Read only Like so Stat should now say that a is read only So a should still work, but if we try and delete something then that shouldn't have worked Because we do need to We do need Mmm close file. I think we need that there as well Just to make sure that Changes don't get flushed to a file once the file has been made read only Yeah, we're gonna have to put checks in all our system calls so Everything beginning with right. That's not right string Check that the FCB is writable check that the disc is writable Check the FCB is writable check out the disc. I don't need anything there Rename file Check that the disc is writable Arrays file Check that the disc is writable. Okay, so let's make a read only and Let's try and delete stat.com disc is read only and Let me drop back to the CCP Good, okay And how big is our B-DOS these days? Ha It is just under Three and a half K that makes it almost exactly the same size as the 8080 Version of the B-DOS There is probably some cleanup and shrinkage I can do here for example here, so This is where the entry point is for a system call is called We then we push all the registers so we can do some stuff here But we don't need to push X Because we're not doing anything with X here. We do need to push Y. So that's not a lot of code There's little bits that can be shared that will probably do horrible things to maintain ability on The whole I think this works. It's simple enough that there shouldn't be any really subtle bugs It'll either work or not work The biggest issue is running into Edge cases to do with the the dear end and module and extent stuff But I am going to call this done So what we have is we've got a port Here for the BBC micro We've got the the BIOS here, which is a tiny file Under half K. We've got the B-DOS We've got the CPM file system, which should be bigger than that. It will grow as you write files We have no CPM software. So there's nothing you can actually do with it yet I have found that Microsoft have open-sourced their Famous 6502 basic. So that should be a really easy port Be nice to have a Microsoft basic will run on this thing I Love doing that. There's the hole where that file was And it will always allocate the first block. So It will start filling stuff up there. That will lead to terrible fragmentation, but honestly, we don't really care and Also, there's another thing to show you which is this. This is a Commodore 64 emulator because I Have done a Commodore 64 BIOS so here Here is our disk with very similar files We can load the CPM program and we run this. It's a tiny basic loader This is all machine code and Where did that garbage come from? interesting and Well, this works when I tested it earlier Yeah, I know what's going on here, and it's really stupid Now how we made hard error fall through to exit. Well, guess what was already falling through. So, yeah I don't want to Okay, so now that's fixed this should work So we're just going to attach the disk image Like so and auto-start it just types in the relevant things and here we have a working prompt And there are all our files and everything works or nearly everything works That's Not quite right. There are a few wrinkles that need looking at That's very not right actually That's not pausing the Interesting well a lot of this stuff works Ha ha ha This used to work honestly uh, I Spend a little time doing this before today's session and everything was fine then so it's something I've done with the recent code Well, that should be a jump for a start, but I won't make a difference Why does that think the file had changed? Okay, I'm a bit suspicious there Don't I think does the BBC version still work the BBC version is just fine the Commodore 64 version is Using the same BDoS binary the same CCP binary in fact the entire cpms file image is The same one that the BBC is using it's actually rather bigger than will fit on a disk But it will get expanded as needed. So If that should be fine Why is that not working? Okay, fcb pausing looks like it should be all right on The Commodore 64 this version is using the kernel The c64 kernel to access the disk The entire cpmfs image is inside a big rail file, which is excruciatingly slow what you're looking at here is Actually a virtual disk The emulator here has patched all the entry points of the kernel If I turn this Put that on And then do a reset. I don't know if you can hear that noise. It's supposed to be There we go, this is this is the correct speed And it takes a while it's currently loading the BDoS right now it's loading the CCP It's I can tell from here and there we are That's very interesting. Why is that not working? well We know that our program will load at one nine oh oh So if you fire up the debugger In fact dump.com just ran so we should be populated here so this is The Debugger showing the contents of memory. Oh, that's very interesting Where is our P block? That has not populated The P block that's why dump can't tell what It's supposed to be doing Okay, so let's put a breakpoint at 1907 go Com Okay, he so here we are at the beginning of dump here So the first thing we're gonna do is look at the The FCB in the P block and we can see that The P block should be at one a oh see No, actually at one a well this is going to be the first byte of the file name and So it should be at 1a47 This is the relocation information for dump. So why has that not loaded? it's the CCP's job to load the P block so this should be parsing the The CC the FCB on the command line into the P block But it doesn't even seem to be trying to copy The command line That should be happening here So how can it not be doing that? Let's figure this out to reattach auto-start Go Let's turn This back on Try and speed things up a bit Hey, I don't know if you can hear that but we get disc noises now We weren't doing that before I Think I want to turn this off Yep, there we go. Okay, so now if we run our dump it will hang Break into the debugger Why are you a CB 5e? Because we're inside the CCP that's where we are Okay, break CB 5e Go reboot Dump dump calm. Okay, we break at the debugger. I forgot to save my file so it didn't reason it didn't Recompile it dump dump calm Okay, that's better. So we are getting the The FCB Let's just actually skim forwards. Here's we open the command. We know that works Get the start address So there's the call to select disk there's the DUI here Here we are trying to load the File that's a right string Be does come out new line Get tpa should be here Expect that to work because it is running the command Just read sequential It's not that set DMA. That's read sequential so Here CB DB is where we actually do the relocation Okay, we're here and we see that the tpa is from 1 9 to D 0 Which is what we expect so We call set DMA We call get ZP which which From 1 9 to 9 0 that number doesn't look right This is the C64 BIOS Get ZP See, I wondered whether we were returning the wrong values here. I'm Bicious, I don't think that should be 1 9 Because the BIOS here isn't using anything like that much It's using four bytes of Zero page, but then there's the be DOS Which is using a chunk to? So the C64 we start at two because there are a couple of hardware registers mapped at zero, so that's two four six eight a C e One zero one two one six One seven actually that does seem reasonable But it is suspicious Okay, so We are relocating It's starting at zero page one nine and Memory page one nine, so we've done the relocation So if we go look at one nine oh, oh here we can see the relocated version of dump so our T block should be around here somewhere P block rather so here is where we actually go and Calculate it so We are now patching the jump instruction Calculate the address of the programs P block so we're here and we see that The value in X a which is the address of the P block is complete garbage Why is that complete garbage? Actually, I do think I know why Let me go look something up So I thought that what was wrong was that I was using the wrong part of zero page Because the common or 640 page is less well organized than on the BBC micro and The kernel users bits and pieces all over the place but the first I Don't think that's the case what I have observed is That the codes that gets loaded here Doesn't match what I see in the emulator. So This is here so this is here see 806 is where we put the BDOS instruction of the CCP so then we store through temp We then load a zero Into a and that's not a zero. That's a 13 So what that's going to do is it'll try to push the pull the address of the P block out of the program header but get the wrong one and 13 is kind of what you get from the disk system when you do things like run out of sectors or You seek to put a particular record in a rel file and then you read the appropriate number of bytes And if you read too many you'll start getting 13s And I notice that the address of this ends in F e our records are 128 bytes long they match sectors but the Commodore DOS sectors are actually 254 bytes long due to the unique way in which the Commodore file system alleged file system operates And I gather that there are bugs So I am wondering whether I've hit one of these bugs here and It's Not correctly loading data off disk So in our C64 BIOS Here is our read code We seek to the appropriate place We seek twice because that is apparently how you work around one of the bugs and Then we just read 128 bytes and write it into memory So yes, I am going to investigate this offline and come back I was hoping this would be a nice triumphant end thing, but we'll see Well, that was a complete disaster It is now actually the next day because I have found out what the bug is and Done a ton of work to work around it and I can basically sum it up by saying that The Commodore 64's rel file support is fundamentally broken and does not work So what was happening here? The reason that this bug was occurring is because this zero parameter to the LDA was the last byte of A CPM sector that's 128 bytes sector This was being stored in the rel file as a 128 byte record Which meant that the zero was the last byte of the record and According to this document, which I found all about rel file bugs if a Record ends with zeros Then DOS on this Commodore 64 will not return them. I Am not entirely sure this is accurate This says that it'll actually jump to the first byte of the next record I skip trailing zeros in a record, but I was observing that I was getting ODs 13s instead So I am not entirely sure, but this also describes another couple of horrifying bugs Which would cause random data corruption? With random access patterns in a rel file so that combined with whatever was going wrong here I have just given up and I'm not using rel files anymore. So what I did was I Am now using the wrong file raw sector access and this Works fine. So let me fire up the emulator. I Just want to make a couple of changes here for reasons which will become apparent So this off So we then Attach the disk image Which is this so you can see that we now have a BDOS file Actually, no, we had a BDOS file before We I have gone through an iteration off-camera where we didn't have a BDOS file. So you also start and It boots and everything is fine So we can run the stats command and there you go. It has stattered the disk We can see that we now have two hidden system files I Can even run the test submit script and it did it I've actually observed a bug there that needs fixing, but I won't do that now That's interesting that did actually work Interesting in the if I try to Do a dir of another user from inside a submit script it would actually return The directory for user zero I Bet this is because reading the record from the submit script is Interfering with the current record the current user, but that's a minor thing as I've been saying Constantly CPM's user support is kind of broken but everything works I can Dump things. Yeah, which is nice And that the way this works is these users horrifying techniques to Make a disk image that is both a valid Commodore 64 disk image with the The byte allocation map and a Commodore CPM DOS directory and is also a Valid CPM file system at the same time so I can do 64 D64 So there is the contents of the CPM side of things and If I can remember how to do this D64 LS Yeah, I am trying to remember what command it is there we go and there is the directory of the same disk image in common or 64 mode the way this works is The reserved sectors for both file systems are in different places, so I can make a I Can make a file in the CPM file system? That's the CBM DOS CIS that covers the Parts of this of the Commodore File system that are in use and vice versa Here it's clearing out the block allocation map to ensure that CBM DOS doesn't overwrite the Sectors used by the CPM file system Given that one of them is called CBM and the other is called CPM. That's not confusing at all Anyway, there is a problem of course, so let me go over here and turn. Yeah, that's gone back on Which is if I attach the disk image and boot it and the problem as you can see is That it's very very slow part of this is the fact that This the Commodore 64 has a 1MHz processor, which it isn't very quick but mostly it's due to the fact that the Commodore 64 disk system is Possibly the slowest disk in the world. It transfers data at 300 bytes per second Which means that a single 256k Commodore sector takes almost a second to transfer So everything takes an age. I will point out that 300 bytes per second is Twice as fast that is only twice as fast as the BBC Micro's cassette system Which is not so brilliant But it does all work very very slowly So because the Commodore Sectors are 256 bytes. I'm actually storing two CPM records in each one You can see the pause as it loads the next sector off disk There are a few wrinkles. I think Yeah, the control key doesn't work properly so I can't control C out of this, but that's a minor front-end issue So yes, that's nice But it does work There are ways around this the simplest thing to do is to implement a proper fast loader In this the way this works is that instead of just using the default very slow Serial protocol to transfer data From the disk system to the Commodore 64 You instead use a custom much faster protocol and you can get up to like 10k per second or But which is fine, but That's really annoying to do and frankly I am fed up of this by now So I'm gonna leave this for the time being it does work In the C64 BIOS We actually have a 256 byte disk buffer Which is used to store the sector last loaded so that Reading a single 256 byte sector can then be used for both 128 byte record reads and Changes that's made a lazily flushed back to disk and so on. It's actually pretty standard CPM has Standard algorithms for doing this But anyway, that's all works. Oh Yes, and the Let me fire up the BBC master version Just to demonstrate that this still works like so This isn't particularly quick either The BBC micro has this amazingly fast disk system that's capable of transferring a complete track of data in one revolution Which we are not using Because CPM reads a single sector at a time so it wastes all that which is why it's just kind of slow like running stat takes several seconds to run Which is better than on the C64 where it will take about 45 seconds to run Which is kind of embarrassing So What is next with this well, I'm gonna stop doing videos. So this is the last one thankfully The biggest problems are really to make this useful is that there is no software So what this really needs is an assembler and a simple editor like, you know, Edlin type thing There are some around the Traditional CPM 80 editor was I think it was written in PLM and there are no modern PLM compilers So that might be interesting to take a look at And then I can compile the original PLM source code to 6502 machine code using LLVM, which would be cool But there's also other things like the fact that You can't really do anything with the screen Both the C64 and the BBC micro use different escape sequences to talk to the outside world and It would be nice to fix this One option is to imponence the thing like a vt-52 terminal emulator in the BIOS but honestly, I Think what I'd rather do is exploit a feature of the way we've done things with relocatable binaries and Implement a system a simple device driver model so that you can load at runtime a device driver for doing things like providing screen support This would then use a couple of pages of memory So you see this system has 65 hex pages of RAM left if I fire up x64 so If you wanted to do screen stuff It would be perfectly feasible to load the driver to give you a Simple cursors like interface which so you get library calls for doing things like moving around on the screen and drawing text Rather than having to use escape sequences There we go and on the C64 which has a lot more memory. We now have B5 hex pages free Which is nice By the way, if I were to implement a fast loader, we wouldn't need the kernel anymore so we'd be able to Reuse everything above D. Oh to the end of the address space as RAM So we'd have huge amounts of memory free which would be very nice particularly for running stuff like compilers So yeah device drivers that would also allow Loading things like serial drivers if you need them that could make As my entry point That would make things like orcs input and output work again Because these are not implemented in the core BDOS in this model So that you only have to load them if you need them stuff like that As I said earlier, there's Microsoft's basic that would be very easy to port to this system And it's worth re-emphasizing The same BDOS binary is being used on both the more BBC micro and C64 and will work on all other 6502 systems the same CCP binary likewise All those applications likewise It's a genuinely portable system All you have to do is write the BIOS and then you will have a basic system That will Run on on anything it will adapt to however much memory you have available It'll still load the same programs on any system. So that's actually better than Real CPM 80 which relied on having the same memory layout on all systems So this does actually form the core of a proper self-hosting Portable operating system that will run on all 6502 machines Which is pretty cool Anyway, I am now done. I'm declaring this finished or at least code complete I'll fiddle with it and fix some of the bugs there are some and Write it up and Document it and so on all the source is Available on github Look in the description for links. I would love to make this work on other platforms like the Oric has 48 to 64 K of RAM and a fast disk system. This would run beautifully on one of those The Apple 2 likewise The Apple 2's disk system is weird. So It might be tricky to adapt on the Apple 2 The floppy disk system is mostly handled in software So you'd have to keep all that software around to read and write disk sectors. So that's irritating There's a whole bunch of 6502 systems. This could be ported to anyway That's all finally over 14 videos more or less two weeks. It's not it's a little bit more than two weeks of real time If you are still watching I hope you enjoyed watching this and I will see you in the next project