 So, this bit's being added in post. During editing I discovered that there was a small problem with my microphone and all my audio sounded like this. And that is just not right. I did manage to fix it to a certain extent using audio processing, but it's still not great. Apologies for the poor audio quality. And now, back to our main feature. So last time I got to the point of getting the calibration tool running, but then ran into lots of issues with crashes and weird things with the multiplication and so on. I think I've managed to sort those out. I've switched to a different libc, this is libc mini. This produces TOS binaries which are much smaller than the ones at the min-tool chain was producing by like a factor of 10. These do seem to run. I've also discovered an issue where emuTOS gets confused if there are long file name files on the SD card, so removing those has made things better. I think what I was seeing last time was it was failing to load binaries properly due to this issue, which was why I was getting the crashes. And with that done, I managed to fix another bunch of things, one of which was the fact that the actual calibration coefficients are long, 32-bit wide. The coordinates we get from the touchscreen are 16 bits, so this multiplication was overflowing and causing bad things to happen. The solution there is that since the values we get out of the touchscreen are 12 bits, but we're actually reading 16, we just drop the bottom 4 bits and that keeps everything in range. So now I can actually show you if I do ctrl-z and run the calibration routine. This is now dumping, it's full of debug information, but these are the calibration coefficients. This is what the kernel is seeing, this is what the calibration tool is seeing, so the data is actually making it through intact. So exit back to gem and you should be able to see the last pointer moving with stylus. Unfortunately, if we move towards the edge of the screen, you may notice that it drifts away from the stylus somewhat and starts moving rather glitchily. Notice it's going diagonally even though I'm moving the pen up and down, and likewise if you go over to the left, that movement is not right. If you look over at the console here, x and y are the raw values from the touchscreen shifted right by 4 bits. So you can see that these correspond directly to the voltage, so you can see it increases going left, x, up to about 1200, and then it starts dropping back again. And that is just not right. So the screen appears to be somewhat non-linear. In the middle of the screen it's more or less alright, like I can open menus and things, so that's nice with a certain amount of difficulty, and I'm trying to get down to the bottom screen is hard, so I can't actually click on the cancel button, it just doesn't go down, go that far down, so I have to press return a bit. However, anything on the left is not working, and anything on the right is not working, and you may notice that if I touch repeatedly, the pointer is jittering around quite a lot. So if you remember looking at the decompiled touchscreen code from the Palmer Throm, there was a lot of logic in there, and I think that was all due to smoothing the touchscreen in order to get reasonably accurate touches. I could certainly take multiple samples and average them together, that's not very difficult. But I'm more worried about the left and right side. You see, if I throw the switch in the keyboard to reset, this then boots up Palmer's. So then we go to the Palmer's calibration, which is the same as my calibration, it uses different targets. So here, here, here. Oh look, it's asking for the top left target again, and again. So it does eventually accept it. So I've actually been trying for several minutes to make that work, and not. So it's not behaving at the moment. This used to work. I think I have managed to somehow damage the touchscreen, probably by disassembling things. I have taken it apart and put it back together again, trying to reseat everything. And it's still not behaving. I did manage to make it skip the calibration at one point, but I can't remember how you had to reset it at the right moment. So that's a bit of a problem. Yeah, okay, it has now skipped the calibration, but of course the pen won't work, because it doesn't know how to map the coordinate. So this touchscreen code here, I mean there's some stuff I could do with it. I could take like three samples and average them that might help. But it's not going to do anything with the non-linearity of the screen. So I am not really sure what else I can do with this. Let's actually just do that. So let's take eight samples and this is going to simply be like so. And let's give that a try and see what it does. If the middle of the screen works reasonably well, then it should at least be usable. So let's go back again. The switch, just see there, controls the line that tells the 6-8, 3-2-8, whether to boot into debug mode or to do a normal palm off boot. That's not working. There we go. Just a small loose wire problem. But let's see if this works and then I'm going to call the touchscreen driver done. It needs work. I'm not sure what work remains to be done there. I would like somebody with a different device to test it, to see whether it's actually a problem with my screen or not. It gives a shame. I hope that I haven't damaged the screen by miscontrolling it. That shouldn't be possible, really. I mean, if I have to configure the touchscreen controller for things like voltages and I'm using the wrong ones, it's possible, but very unlikely. But anyway, let's see how this goes. And then there is only one remaining piece of work needing to be done. And I can call the first alpha of this finished. So let's get on to that and I'm just trying to fill time while the download happens. OK, I've turned the backlight on because it makes things easier to see. So what was I called it? Dana-Cal. So now how does the mouse button really use the answer? It's smoother. It's not really smoother. That's glitching about horribly all over the place. It occurs to me that if the pen has come up while taking out eight samples, the values we're getting back are going to be garbage and are so going to pull off the position. So I think we want to do. So if... Let's try that. So what we're doing is we're sampling the pen down line before and after taking our coordinates. And only if the pen is down both times do we trust the sample we're getting. So that if the pen comes up while doing a sample, we ignore it. Yeah, let's try that. I have in fact done a bit of refactoring so that the sampling code is now in raw read, which should help a bit. Let's see what this does. OK, well, judging by the way that the printer icon bottom right has been selected, I don't think that smoothies working well as I would hope it won't. It's pretty jerky, but the main reason for that is all the tracing that's being produced. That's interesting. That has in fact just read fire. So these values are actually the addresses of the state variables, not the result. So let's just put this here instead. So the x equals y equals is actually coming from here. x and y are the values that we've fetched. And sx and sy are the screen coordinates. I mean, these are clearly wrong. That means that the pen is up. So OK, if the pen comes up before any of the samples, then it will return 0 and 0, and the state will be not pressed. So let us do this. So we check to see if the pointer is down. If it is, we try to do a sample. This will then return whether the pointer is whether the pen is still down afterwards. So that will tell us whether the sample we've just read is valid. OK, so let's try calibrating. Why is the raw read not now returning then down? It is possible that this is actually connected to the touchscreen chip's interrupt line. So this may not be returning whether the pen is down. It may be returning whether the data is pending. It's not the same thing. It's not that one. This one, this is the datasheet. So the output pin is pen IRQ, pen interrupt. Pen output is connected to the Wiper input. When the panel is touched, the pen IRQ output goes low. During the measurement cycles, the output diagram will be internally connected to the ground, and the Wiper is disconnected. And that's power download. So as far as I can tell, pen IRQ will be low while sampling or when the panel is touched and high otherwise. So that should work as far as I'm aware. So have I got something wrong here? State here is the U word. This would want to be this. Actually, we'll put this here. We know what the problem was. I think I know what the problem was. No, that was not the problem. So what I was thinking of is that bools and words are not necessarily equivalent, depending on the version of C. If it's a real bool, then values will be coerced to 0 or 1 when they're assigned. If it's just an int, and of course state here is now a pointer to a word, then it won't be. So this will return either. This thing will return either 0 or 2. Not will then produce either 1 or 0. Yeah, yeah, I'm getting this confused with complement, which is wrong. So yeah, that should work. Interesting. So let's run that again, shall we? State is always 0 being returned from the, well, then we get the 0. I do not think that this line is behaving the way I think it's behaving. Given that we seem to be getting, we're not getting 0s from as samples, if depends up. We're getting 0s because this condition is failing, and it's not actually reading anything. And so the initial values here are applied. I'm just going to deploy this. But I think what's happening is that we're reading the, is that doing the read is somehow causing state to go high. So it thinks it's not pressed. OK, so we run it, shall we? That's as far as the next one. OK, that does seem to be behaving now. And we're not getting, it's still a bit jittery. It's still quite a lot jittery, actually. But it's not highlighting the printer icon anymore. Yes, we still have terrible results that are right inside the screen. Moderately bad results in the center. And the left-hand side of the screen is garbage. Fabulous. Well, I think that's better, so let's just stick with it. I'll just turn off the, so not that tracing. I do want to turn off this, and I'll try that again without tracing at all just to make sure it behaves the same way. OK, I'm hoping the mouse point will be a bit smoother now. Calibrates, yes, it's smoother. Not very well calibrated, I have to say. This is probably due to a glitchy press when I actually did the calibration. The calibration tool could probably be enhanced to be more outputty. Try that, yeah, that's better. You see, this bit is all right. Going up and down, it goes sideways hugely. Anyway, I think that's fine. It's not very happy, but there we go. All right, well, this takes us on to the other part, and I shall just tie this thing back up in Parmos. The other thing, the last thing remaining to do is I don't really want people to have to rig up a development board like this and solder stuff onto their device in order to run Emutos. So I'm going to try and do some Parmos programming and try and write a very simple tool that will load the Emutos kernel from the SD card and run it. So believe that resetting here will take it to the... We'll skip the calibration. No, that takes us straight into the calibration. Hey, it calibrated. That's a stroke of luck. Good, well. So I have here Hello World in Parmos code. No, that's Hello World in VDI code. Here is Hello World in Parmos code. So we run it with or we compile it with Parmos GCC minus C minus OS minus G Hello dot C. That does the actual compilation. We then need to turn the object file into a resource file. Let me go and look this up, shall I? OK, here it is. I did actually have the right command line. This wants minus O Hello. This wants, ah, I need an executable. So no minus C. Right. That then turns this executable into a resource file. I'm not quite sure what a resource file looks like. Hello dot Gruck. But then we run build.prook here that reads that in. Right, these are the resource files created for which there is a number. Build.prook then reads these, and it turns it into a Parmos Prook file, which is one of these. And we then put this onto the SD card in the right place. So mount the SD card. We then need to create the directory structure needed that's annoying. And then we copy the Prook file into the launcher directory. Then we unmount it. And then when we insert the card into the machine, it should use an error message. So it's supposed to understand DOS file systems on SD cards. But this has been configured for the emu DOS. It's possible that Parmos doesn't like it. But Parmos does appear a little bit flaky. Let's try the other card slot. Fantastic. Well that's a lot of help. I'm going to have to figure out how to produce a card that both emu DOS and Parmos understand. That's going to be annoying. I'll be back in a moment. Okay it took a while, but here we go. There is our hello application. So if I run it, there it says hello world, which is good. So what I eventually did is the emu DOS card is in the right hand slot. And the emu DOS card, and that's the Parmos card. Because of course this machine has two card slots. That's nice. So we are now running code in Parmos. So let's try and turn this into something a bit more useful. So let's copy this into tool. We can call this data run.c. This is going to be really, really hacky so that we don't actually need a event loop or anything. What we're going to do is attempt to open a file on the SD card, which will contain our ROM image, load it into memory, and then run it. So I am going to go and have to have a look at the Parmos. Actually I've got the Parm SDK here. So version of Parmos we have probably four. So in here there should be some file handling code. This looks plausible. Probably don't want Parm compatibility.h. Yeah, I'm actually going to have to go and look this up I think. Interesting that these do not actually need to be defined anywhere. Possibly the years. I wonder if there's stuff missing. Okay, I am going to go and look this up. So I found this PDF of the Parmos 5 API, and there's a nice set of BFS functions. So I think the one we want is the FS file opened. This takes the volume reference number. So we're going to have to call this to enumerate the volumes. We're going to try and look for our kernel every volume, because that means that we can put it on in either card slot. So the FS volume enumerates a pointer to the reference number, pointer to the variable that holds the index. Ah, example code, perfect. U, okay. Let's use the Parmos types I suppose. U in 16, volume reference number. U in 32, volume iterator starts at the FS iterator starts. And then we have a loop. While VI is not equal to the FS iterator. Dot equals pfs volume enumerate volume reference, volume iterator is not equal to none. Okay, so if we get here, then we have a valid volume. So let's go to the FS file open, taking the volume reference path, the open mode is one of these, and we want the FS mode read, and that's all we need. Back to here, and we want a return file ref. So if E, hang on, we have enumerate gone, this is the wrong sense here. So if E equals power none, as it's found a kernel.image file, then we are going to try to load it. So first we want to figure out how big the file is. So E equals bfs file size, file reference followed by 52, followed by the size. If E is none, that succeeds. Actually if it doesn't succeed, we're going to fail. When we fail, we are going to close our file, and the only reason that this would fail is if the file was unreadable, at which point we want to loop round, or to close the temporary file reference, loop round to the next volume. So we should here now have a correct size. So we want, I looked this up earlier, mem pointer new. Right, I was going to say that we would use this to allocate the size of our size block of memory, but it looks like we can't, because there's a limit of 64k for a block. Um, is there a way to get more memory than this? I don't think this is it, that's not relevant. Okay, this is a little bit of a problem, because if we can't allocate enough memory for, to put our kernel in, then we can't load it. It's got to be contiguous memory block. I wonder if we're allowed to use large chunks of bss, so could we just do, computing uint8 is correct. So 256k, so we can compile this with dena run to dena run, integer overflow. That makes me think that these are using 16 bit ints, which is, yeah, it looks like our bss can only be 64k. That's a bit annoying. Okay, more research is required, I think. Okay, so this is actually a little bit more interesting than I was really expecting. There seems to be no way to allocate large chunks of memory. So I'm going to have to work with, uh, it says maximum size of a memory allocation is a bit under 64k, so let's work with 32k. So block size is 32.24. Now we are going to need to allocate enough blocks to load our file, and then read the file into these blocks one at a time. Then, once we've finished with parmos, return interrupts of, so that now none of the parmos code is executing, and then we have to reassemble this all of our blocks into one contiguous chunk and jump to the beginning of it. Luckily, I've already done the work so that the ROMs are position independent code. So what we're going to do is, uh, so for, so let's say the biggest kernel can be 300k, say. So that will be, we will need this many blocks. So we now know how big the file is. So let's calculate the number of blocks, that can be a unit 16. We are now going to allocate them. Okay, there's this compile, that's the unit 8, par 0, unit 4 block, unit 16 is a time. Wait, this compiler doesn't know C99? Yikes, okay, that was a shock. It's not going to be unfailable either. Okay, um, inflicted 2, actually blocks 5. Okay, so the crypt value is neither a rain or pointer. Uh, okay. Now, we are actually going to sort our list of blocks by pointer so that they are in ascending order. This will make it easier to reassemble them again later. So the size of, I don't know, the number of members is lock count. The size of each member is that, and the comparator is comparator cb. Now I'm not sure yet where the parmos has a, has a Q sort, but 1, 0.2. The first element is less than 10 minus 1. It's greater than return 1, otherwise return 0. Okay, no Q sort. Do we have a sort? I also found this, which is palmloader.c. It's the UC Linux loader for starting UC Linux on palm devices. However, this works rather differently than my code. So cursors, I cannot see a sort in the index anywhere. Ah, sysq sort. Excellent. Let's go here. Starting arg for from, that's the comparator. This was to be in 16. You are not const. Excellent. Okay, well that will sort the blocks into ascending order. We can now go through and actually read the data, vss file read, number of notes to read, point of destination, block, okay. So this would be vss file read, parreference, block size, blocks, pi, and we don't care about the number of bytes read, that still builds. Right, so at this point we now have our entire file in memory. We want to reassemble it into a continuous block. Now it is possible that our code here that we're running from is actually between two of the blocks. So we can't just overwrite random stuff because we might overwrite the code we're actually running. So what's a good way to do this? We could have a piece of position independent code do the work, which we copy into one of our allocated blocks, the allocated extra block. That way we know that we won't be overwriting it. Because of course, our blocks are now in numerical order so that we know that between blocks zero and blocks however big our kernel is, there will be at least that much memory. So how do we do this? I think we're going to need some position independent code. We're also going to, however this code will need access to our array of blocks which again might be between the two of these. So yeah, we are going to need to do that. Can I file this? No we can't. So we're going to have to write machine code manually. Okay, so if we do that, that means that we'll allocate one extra block. This is going to contain our actual boot code. I wonder if I want to try and load the boot code from a file as well through use of development. Actually trying to write it in here would be tricky. There is inline assembly. Yeah, let's do that. So close the current file, load the new file. At this point we can't really clean up. I mean we could free all blocks but we're pretty much into it's going to work or the system will crash territory. So let's just read in our boot block into the last block. We want to make sure that is included in the sort list. We are going to define a function pointer which is going to be our boot code. Let's assign it to the last block and execute that compile. Oh yeah. And it's taking as parameters the pointer to the list of blocks and the number of blocks. Excellent. So does this what does it look like if we just assemble it? I did do my minus g. So here's our main function which is this, which is quite small. These are where we're calling power mile system calls. Each one is a track followed by the number of the system call you want to call. We are here calling Q sort which I think is this. We are now calling, it's probably the loop here. This is a multiplication by four. These three calls here will be file closed, file open, file read. So after file read we, so this is this file read because it's then got the bottom of the loop in it. So file closed, file open, file read. Oh yeah. And this is our code here. Here is the indirect jump being done. Okay. So I think that code is possibly right. There's nothing resembling tracings. So we won't know if anything goes wrong. And we also need to write this. Let's do a little bit of rearrangement. This needs to be a directory. So move to your run.c run tools on loop.s8 equals to this blocks or equals number of blocks to be for zero equals turn address. So first thing we need to do is turn interrupts off. So that's going to be, we're going to steal the code from here. More wx2700 into sr. Okay, almost now it doesn't get the chance to do anything. We are then going to load what that should be a 32 bit value that pointers to 32 bit value. So denort is the number of blocks. Okay. So we're actually going to want to loop until denort until we've run out of blocks. We're actually going to start at block one. Of course blocks, okay. Let me just back up a little what we're going to do. So we have blocks like zero, one, two, and three with gaps in between. What we're going to do is we're going to copy block one. This would immediately follows block zero. Block two. So it immediately follows the new location of block one, etc. So that way we will end up with all the blocks concatenated together and we know that there will be enough memory for them. And they were not overwriting anything we care about. They're only overwriting like Palmer stuff. So this is going to be the current block we're working with. No, we don't need to do it like that. First, we need to find our destination address. So this is going to be the thing pointed at a zero. So what we are going to do is we need to figure out whether we run out of blocks. So we're going to compare our block number with one. If it is equal to one, then we are finished. We could put this comparison at the end, but if we did that, then we wouldn't be able to support ROMs that were in a single block. We'd always end up copying at least one block. So we now, this plus will cause a zero to be incremented by four as we read it. So it is now pointing. So it is now pointing at the pointer, pointing at the current block. Oh, blast. This isn't going to work because this block address array is going to be in unsafe memory. We could be overwriting it. We're going to have to copy that into safe memory. We know we've got 32k in this in the block that this code lives in. So we can just put it there. That's lots. Okay, so the first thing we need to do is to copy the entire block array. So that will be, let me do that. So DBF is a little bit odd as you need to subtract one from the loop counter. So let's just do it the old fashioned way. So that will be subq1 from d1, grand shift not zero to one. Okay, so this then picks up the address of the first block. Okay, so we should be ready. We now want to compare the entire main loop. We want to pick up the address of the block that we're going to copy. Actually, there's an easy way to do this. So this will write a zero after the list of block pointers. We now no longer need the number of blocks. So all we're going to do is to pick up the new source block if it's zero we've finished. Then we are going to actually do the copying by copy from the source to the destination DBF11 back. Right, that's copied. It has advanced our destination address. So here all we need to do is jump back to the loop. Right, once we have finished we just need to get the first address which is going to be that's the address of the first block. So this is the address of the first array slot which contains the address of the first block. This gets the address of the first block. This jumps to it. So this should be our reassembly code. I think I'm actually going to use the Atari assembler for this as it's rather newer. It doesn't matter. It's going to be creating a simple image like this. 46, jump to 0. Okay, so now let's just assemble it to see whether it looks sensible. I did put that in the wrong directory. Okay, so now let's assemble it. So interrupts off. I'm checking for any absolute address references because of course they will work. Number blocks, block address array, this is program counter relative so that's fine. Okay, it does occur to me that we could turn this into data that then gets embedded into our program here. That would probably be better than having a separate file to be honest. So we are going to want to make danerun.prook which will depend on obj danerun and our command line for community experience. This is the application ID which is a four character identifier. I'm keen on that in this. One of the problems of make, one of the many, many problems of make is trying to deal with rules that generate multiple files. And this is one of them. So let's actually do it like this. Let's take a parmas of res. Doesn't have a destination directory. Okay, cd obj and so this will then spit out all the .grc files which this will then consume. So now all we need to do is do this. Okay, it has compiled the xe but this hasn't found it. Okay, so that has now created a executable which is danerun.prook I believe. All right, so that compiled the program. We now need to compile the loader which we're going to be using this Atari Mint As. All right and we do also need an image and we create this with, I'm actually doing this in the daner code. We're doing it here. So this will be m2k, Atari Mint, LB, obj, pmp, position independent. So I don't think, I do need to tell it that I wanted to generate an output file. So I think I want this. Let's just stick this in to make sure it gets built. Let's see what happens. Okay, we have, what is it? 989 is big. Right, that is in fact generated in Mint Executable which is not what we want. Yeah, this specifies the format of input files. If you want to specify the format output files, there may not be an option for this. Here we go, oformat. Oformat binary. Let's see what that does. That's better. That looks about the right size. It shouldn't matter what the start address is because it's position independent. So we've got 46fc, 46fc, yep. At the bottom we've got 40d0 and then 0 is good. Right, we have built our bootloader. We now want to get it into our Dana Run program. The simplest way of doing that is to use xxd. xsd does a hex dump. We've got an option that will emit a cnt file. So we can do that or we can do that. h depends on palm boot dot image. Okay, so this depends on palm boot dot h. Okay, all right. So here we have our bootloader. This means instead of this code, all we need to do is to copy. Destination is blocks, block count. Source is bootloader. The length is size of bootloader. We can get rid of this. What doesn't it like about that? Maybe, all right, implicit declaration of built-in function, men copy. Let's take a look to see whether there's a system functions. It doesn't look like there's a men copy. So probably we just want to include string.h. And there we go. Okay, so here is our proop file. So to make sure we have a working Dana. Okay, the card is in the slot. So if we copy the Dana run dot proop into mount palm launcher and enu task dot image into mount kernel image. So that will copy. So we insert the card, wait for it to be mounted. You'd have noticed it does not actually appear to be showing the application. Did I remember to change everything? It should be called Dana run. So let's just see whether it actually made it onto the card. No, it didn't, peculiar. So we insert the card, we wait for it to mount. And there is our Dana run program. So we run it and it doesn't work, which honestly I was expecting. Okay, so the next job is to figure out why it doesn't work. And to do that, I'm probably going to need to get out a Plamos emulator debugger attached to it. So I am going to leave that till next time. So I hope you enjoyed this video and please let me know what you think in the comments.