 Good news! I have managed to make it work. I did have to fix some bugs in the SD card code and wire up a few minor things, but up here you can see I have a console, a prompt, and I can actually do stuff which is limited somewhat. I have a copy of Protext on this but if I try to run it the system crashes to no one, it's a great surprise. I should be able to read the readme. There we go. So it's reading files off the SD card. I can create directories and files. Creating a directory takes quite a while for some reason, but creating a file is pretty quick. There may be something there to look into Does this work? No it doesn't. As you can see the environment is extremely MS-DOS-like because that's what they copied in the user interface. So at least it makes a nice simple environment to experiment with. Remember I can't do dir-shw. This was something I made earlier. There you go. So now we get to work on something a little bit more interesting, which is the screen and because working on graphics code without being able to see what you're doing is kind of boring I have, I hope, hooked up a webcam to actually film the machine itself. However, I have no preview because if I try to put a preview up on the screen then the combination of the preview software displaying video and the capture software trying to capture video causes all kinds of bad things to happen. So I've done a bit of basic testing but we'll just have to hope it works and I will composite it on in editing and I'm sure that won't take a horribly long time. Anyway, let's get on to graphics. The system on a chip does have an LCD controller and I am sure this is being used for the graphics because I've looked at the code. It's pretty straightforward to set up. You just like program a bunch of registers with the size of the screen, the start of the frame buffer and some information about the LCD device itself which I don't have. So over here we've got Ghidra. I have fed it the LCD register names. So LSS-A here is where the frame buffer goes so we should be able to find places where it's written to. So for example, this undefined function. So if I scroll down to get a name, hardware display in it, that seems plausible. So let's call this hardware, display in it, turn it into a function. So what is it doing? It's looking at lots of parameters that we do not have set up correctly. I'm guessing those are all words. So let's just help this along a bit in the interest of clarity. So all these in-stack things are mis-detected parameters. 6789A, I reckon. The first parameter is a pointer. That didn't do what I wanted. Right, let's turn. Come on, go away. Let's turn that off. Now it will auto assign things. That's still not in the right place. I believe that the calling convention used by Gidra doesn't match the one that Parmos is using. So can I? No, I can't put that somewhere else. Let's bodge things slightly. Can I put that there? So I believe that Param 1, which is the long, is the address of the frame buffer, because it's sticking that in A2, and then eventually it's writing it to LSSA. No, it's copying LSSA to it. That seems peculiar. Right, it's if the old parameter is 0, then it's copying the old value. I bet that down here, here we go. Well, I'm also slightly disturbed by all of these. I do not know what this is doing. Well, it's setting various bits in port K, but I don't know what port K is actually wired up to. And why is it trying to set the bits lots of times? Anyway, I need to know what these parameters are, so I need to chase up the call stack. And so I want to try and get them set, if possible. So this is testing A2. A2 is here. It is, yes. The first parameter is indeed a voice star, but it's not at stack 4, it's at stack 6. That's better. So they should work up from there. So I think this one's a word. And of course, it's not going to auto assign them for me. I'm guessing that... See, I need this to be at address 4. It wants the first parameter to go at 4, but we want it to be at 6. But if I put a word parameter in, it tries to align it... Oh, I know what it's doing. It's pushing words as 4-byte parameters, which we don't want it to do, curses. Okay, we are going to have to set these explicitly, but I'm just going to add them first. So I think what we've got here is width, height, and frame buffer address. Was there a... I think it was just a 3, actually. So this is the address. This is width. This is height. And that's at 6A... No, there is a fourth one. 6A, C, and E. Stack, O, X, C, 2. Of course, it's size 2. Like I told it, it was a word. And E, add, stack, O, X, E, size 2. Okay, so this is the address. This is the width. This is the height. And I do not know what this is, but it's probably the depth. Yes, it is the depth. You see here, it's checking for a depth of 0 that makes no sense. It's going from 1-bit, which is monochrome. Here, it's setting up the various registers for 2-bit and 4-bit depth. So let's change that to depth. And that actually shows us what some of our values are, which is nice. And the LG PMR, if I remember correctly, is to do with gray scale. Here we go, gray palette mapping register, which we're not going to be using because we're monochrome. Okay, so where is our screen init code? We do not want to set up the frame buffer just yet because that will happen here. And in fact, Dana's screen base no longer has to be stored because we're just going to use the frame buffer register. So here, in Dana's screen init, what we actually want to do is to set up the LCD parameters. So that will be LPICF equals 8. And I'll figure out what these do in a moment. This is doing something with ports. This is on Earth. Okay, this is slightly complicated. So we start with the top bit set. And for every bit in D3, we mask off these bits so that we just get bits 4, 5, and 6 into D0. And then we do practically nothing with it. We just set all these bits in PF and PK. Weird. Okay, well, I'm just going to put these up here somewhere. Screen width times screen height divided by 8. So LPICF is this. LSSA we're going to set in a minute. LVPW, well, let's just cut and paste all of this, actually, is here. This is the number of... Oh, this is the stride, the width of one scan line in bytes. These are easy to set. And then it is calling some system traps that I actually forgot to look up. I do wonder what these port K things are. Is port K mapped to anything interesting? These look like the LCD control lines. Port K has multiplex with the ERDA SPI and LCD controller signals. So at some point, something is going to want to set the... It's because something is going to want to wire these up. What's port K? Oh, that is port K, the other one's port F. What's port F? PF cell. There we go. Okay, contrast, just contrast as far as I can tell. So, by ANDing this with BF, B is 101. So that is clearing bit 7, bit 6. I would expect it to be trying to clear the contrast bit, to be honest. And then there's this whatever it's doing with port K. This is setting bit 6. And then it's repeatedly setting UDS, what's UDS? And then it's clearing UDS. And then it's clearing LD6. That is a mystery to me. However, it's clear that port K and port F have got something to do with the screen. So let's go and look for port K select. And where is it being written to? Well, pre-ramming it is where a lot of initialization happens. FD is setting all the bits except for bit 3, which is UDS. So, which way around does port cell work? So only UDS is wired up by default. Again, that does not mean very much to me. So the only other places it's being written to are in the SPI code, which is setting this bit. So data bit 5 is being set up as a GPIO bit. So that one does not seem to be being used by the screen. None of this is making a whole lot of sense to me. Right, that bit is the power bit for the second card. So that is setting the bit to be GPIO and clearing it. But it looks like PK cell is being written to in one place, which is there in the startup code. And that's setting them nearly all to be GPIO bits. The only one that is not a GPIO bit is 1011 is data bit 2, LDS. So what is... I need to look at the LCD controller docs for this. The LCD data bus lines transfer pixel data to the LCD panel. The LCD controller is initially configured to drive single screen monochrome panels. The data bus size can be 1, 2, 4, or 8 bits by programming the LPICF register. So what is LPICF? LPOLCF, right, LPICF. And in our code, we are setting it to 8. So that's setting a bus width of 4 bits. This is actually being... It's been read from a bunch of places, but it's being written to here. And right. OK, the physical connection to the LCD is 4 bits wide. Then the bottom bits here indicate to tell it what grayscale mode to use. So we have to specify 4 bits because it's 4 bits, but we want to use black and white mode. So this should mean that we know which bus pins we need to wire up. For 4 bits, we probably want, well, LD0 to 3. So is there an LD0 somewhere? Port C, which we haven't seen before. All of these LCD, judging by the names, they probably are. So our startup script has touched all this. Here we go. Port C select is 0, so they're all being set to dedicated function pins. That seems fair. But let's just set these anyway. That's pull down enable. Some registers appear to have pull up resistors. Some of them have pull down resistors. This is a pull down resistor. Right, port. So these are the 4 bits used that we saw in that register. Here are the 4 data pins. So I think it's just those 8. But we do need to, I believe, set up the contrast line, unless we just want to disable it. Oh, this is useful. Rather wish I'd found this earlier, to be honest. So yes, port C, LCD controller. Port F, LCD contrast, port K, LCD controller. So port F select and port K. Right, this is where the other LCD lines are. LDS and UDS. We still need to figure out what they are. Did I look up LDS? This is all about RAM, nothing else. So I don't think we need more to touch that. I still don't know what the code here is doing. But I think, well, we still need to look up all these other things, LDPW, virtual page width, virtual width and pixels divided by 16 for black and white, 8 for false grayscale, and 4 for a 16 grayscale. Lxmax and Lymax indicate the size of the screen. That's clear enough. Lpolcf, polarity register. This is configured by the LCD panel, so we just use the magic number given. Lacdrc, rate control register. This is another magic number. Let's just leave it as is. Lpxcd, pixel clock divider register. Generates the pixel clock. Likewise, magic. LCKcon, the clocking control register. I think we want the top bit to be set, to be honest. Why is that putting anything in there at all, given the lower bits are not used? Let's turn that on. And in fact, we should go look to see where LCKcon is being used. PreramInit is the init code. It's used in two places in the display init. So this seems to be turning it off, because it's clearing the register. And here it's writing 2 to it. That's this bit here. So there's a function here we haven't done anything with. It's being written right. This is clearly turning off the LCD. Prove display depth. So unless this is trying to turn the screen off for some reason, then that was 8027. 8027, 80249. All the traps, it's still not decompiling. OK, I do not know why that's not working. This is proof display row bytes. That's clearing lots of memory. I think this is the, here we go. Ah, now this looks useful. Hardware display draw boot screen. Hardware display draw boot screen. OK, so this is going to, it's filling the frame buffer with the boot splash screen. It doesn't appear to be touching much in the way of registers. So what it's doing is it's drawing the Parmos logo in the middle of the screen. However, it looks like Yidra hasn't spotted anyone calling this yet. Let's have a bit further down. Prove display backlight. If we want the backlight to work, we will need to turn it on. And by the looks of it, it is wired up to port K. And this is also touching L-Pulse CF, LP-Pole, line pulse polarity. What that's doing is it's inverting the image by telling it that the data is either active high or active low. And I'm going to guess that PK here is the actual enable bit. Yeah, there's just one of them. So that is, let's help this along a little. Right, now it decompiles. And you can see there are two parameters, four and six, which of course is putting in the wrong place. Oh, and apparently that's a temporary. So that is straightforward enough. What's this one? Prove luminosity to hardware register values. There's more, lots of backlight stuff, pallets, doze, timers, OK. So I think that's worth a try. And we do need to fill in a lot of stuff. That's a byte at A21, that's a byte at A23, that's a byte A25, that's a byte at A27, that's a byte at A28, that's a byte at A2D, A33, six, and that's a word. So LSSA is AULONG at A00, keep going to the wrong virtual screen. LVPW is a byte at A05, LXMAX is a word at A08, LYMAX likewise A0A. There's also some cursor stuff that I don't think we're going to use, LPICF is a byte at A20, LRRA is a word at A28. OK, so now we need PCCELL, which is a byte at 413, PCPDEN, which is a byte at 412. Somewhere on the internet there will be a header file with all this stuff in it. And it will be of a format that I can't quite use. pfcell is a byte at 42B. OK, so this should build, reset. I can't see the flashing lights on my breakout board, but I don't think that's working. Let's just power cycle the Dana. Hope it's still on screen, now I've moved it a bit. And let's see what happens when we run it. OK, execute. Well, I don't see anything on the screen, which is a shame. So this is also being set here. Proof display base adder, which seems to be conditionally setting the display address. Oh, this one's interesting. Hardware display sleep, this is turning the backlight off, turning the LCD controller off. And this is probably a bit to say that it's done it. So we have the LCD controller on. Let's just turn the backlight on. Also, I don't think I did that right. No, no, that's correct. The fields are two bits wide, so 08 is 104 bits. GS is 00, black and white. There's the contrast control. It's possible that it's running, but the contrast is all the way down. That was an LCK con. No, that was just the enable bit. That register's only got the LCD controller in it. PWM contrast. Right, so are we setting this? We are not setting this. Right, what is this setting that to? And apparently it's not being set anywhere. Did I get the address right? A36. So it doesn't appear to be using the built-in contrast generator. So was that stuff to do with port F an external contrast generator? So let's look for screens. We still don't know where this is called from. Display depth, sleep. Oh, I didn't decompile this one. Hardware display attributes. It's got this handy string in it. Backlight control. These all call private functions to get the work done. Get depth, nothing particularly interesting there. Hardware, you'll see the base address. Gamma correction table. That just returns a pointer to a thing, probably this. Calculate nipper pulse width value. The nipper, the NIPR, is the non-integer prescaler something. It's got to do with the UART. So we're now out of, let's just decompile that. We're now no longer in the LCD code. So display in it. PF and PK. PK data is B7, which is 1101011. PF data is BF, which is 110111111. And then this is, no, sorry, that's D. This is B. So then the code here that's failing to decompile is actually repeatedly pulsing these mysterious bits and then turning it off. So this is, this is clocking something out to this bit. This is the, sorry, I keep double clicking for emphasis and it doesn't do what I wanted. So this is working through every bit in a byte. This is the byte we're clocking out starting from the top. And so if the bit is set, then we set our bit here. We then jump to here and we pulse PK. Then we go again. If it's not set, we clear the bit in PF and we pulse PK. PK, this bit in PK is a clock bit. This bit in PF is a data bit. It is clocking out a SPI style 8-bit value. And then here, in the next piece of code, we set a different bit, enable, wait, disable, and disable our data bit. Heaven knows what these are connected to. I wonder if I should at this point take the thing apart again and do some more trace following because I can find these bits in the, on the CPU. Or I could just try and copy the code and see what happens. Let's try that last, I think. I want to go look for PF cell and PK cell. Wow, that's being touched in a lot of places. Prove BB get XY. And this is the touch screen. I know it's next to the pen stuff. I haven't looked into the pen stuff much. My feeling is that there's some external SPI hardware connected up to the second SPI interface. And I think that could be the keyboard. So I wonder if this is the pen. OK, I need to make, I need to start making some more notes. So got port F and port K. So port F is, we want this bit is our data. Port K, we've got, I think this bit's the clock. And no, it's not. This one's the clock. This seems to be some kind of finish or termination. And over here in the pen code. So FD, D is 1101. So that's 1101. So that this is pen related. So I don't think we need to touch that. It's being initialized here to C5, which is 1100, 0101. So that's 1100, 0101. Port K is being initialized FD, 1111, 1101. So these values actually match what we're seeing in the usage. Wonder, this is the second SPI interface. These functions do not seem to have names, which is a shame. Yeah, this is a module containing lots of inline assembly. Let's see in here. So my thought is that I think this is pointing at the pen touchscreen being connected possibly by SPI, maybe I squared C, one of these clock and data protocols. And when the screen is being initialized, it does appear to be fiddling with one of the buses. So possibly it's sending a reset code to the touchscreen. I do recall hearing about I squared C touchscreens. But if that's the case, then we shouldn't need to actually do anything with this to make graphics work. The other thing, of course, is that the graphics are working, but nothing's appearing on the screen. So let us go into our config and turn off the serial console. And we do need to enable a few things here. PK cell and PK data. PK data is a byte at 441. And PK cell is a byte at 443. Turning that configuration off broke a few things. So let's just do a little bit of warning fix. So push serial iorek is actually defined here. OK. So now we won't see a console here anymore. It should be going to the screen. Interesting. My reset button is not working anymore. So it's wired up correctly. All right. Here, by the way, is a data sheet for a random I squared C touchscreen controller, which is controlled by four wires. And here is a command byte. And the top four bits do seem to contain the op code. However, I have just spotted the address byte stuff here. The address byte is the first byte received following the start condition from the master device. Right. This is programming the I squared CID, because you can have multiple devices on the same bus. Right. That makes sense. That means that this code here could be programming the I squared C address of the LCD controller to 0x70. And then it's using either bit banging or the second SPI controller to actually talk to the touchscreen. I also noticed there's a pile of code above that loop here, which could be sending a reset. So BF is 101.1 again. Yeah. I think I am going to have to take the lid off this thing. Find the touchscreen circuit board and look for integrated circuits. But in the meantime, let's run our thing. I don't see anything on the screen. So that has still not worked. Surprised that I haven't really seen anything that's writing a on bit. Hang on. Is that bit active high or active low? Have I just turned it off? Nope. I have to set it to a 1 to turn the LCD controller on. OK. Let's do some poking. What does this do? Hardware display sleep. So you see that is disabling the controller. That's actually, I think the same functions are in this ROM several times. So there should be a display wake, so display depth. Yikes. Let's just save quickly. Yeah, Gidra's not entirely stable. So looking at the decompilation here, we are looking to see whether the controller is enabled. If it is, turn it off. And PK data here is related. That's the top bit. Was that the backlight bit? No, it's not. This is the backlight. Also, I don't really see anything glowing. I don't think the backlight's come on. We should check to make sure that code's being run, but I think it is. LCKCON, Proof Display Depth. That must be setting the depth. Yeah, there are incoming parameters. Here, it is turning the screen on. And at the same time, it's setting this bit of PK. Is that power to the LCD module? So we want to make sure that the backlight hasn't come on because I've got this the wrong way around. We want the backlight bit wasn't port K, not port F, wasn't it? I wrote that in the wrong place. Yes, the backlight wasn't coming on because you set it to an internal function rather than to GPIO because once again, I got the sense wrong of PK cell. So yes, it was port K. So let's try this. That was interesting. You see, I ran the program. But then when I ran my script here, I didn't have to hit the reset button. That suggests that for some reason it jumped into the bootloader. Which surprises me. OK, and run. Backlight is on. I just saw it light up. Nothing seems to have shown up on the screen. So where is our... So this trap here, A249, it does seem to be making a point of doing that before or between enabling the LCD controller and setting whatever this is. Let's see if I can get some of these parameters in place. Luckily, the first one appears to be at 4 and it's a byte. How many have we got? 4, 6. The second one looks like it's a word. 4 and 6. Probably a Boolean and... I don't know what the other one would be. But this is at location 4. And this is at location 6. OK, much simplified. The second parameter actually appears to be a pointer by the looks of it. Yes, it is. And if you don't specify something it defaults to here, but we don't know what here is. So this is clearly depth. And this variable, here it's calculating the old depth. Whatever Param 2 is pointing to is a structure containing the old depth, where the old depth is stored. If the LCD is on, turn it off. Recalculate the stride. Whatever that's doing, it's wrong. So this is the same setup we had before. Configuration for monochrome, 2-bit color, 4-bit color. Re-enable the LCD. So this is clearly changing the color depth of the screen. I am actually still wondering whether anything is actually being displayed on the screen. We know this code got executed. We didn't see a call to this. Kdebug should still be going to the... Do we have Kdebug? Yeah, we do have that enabled. Kdebug should still be going to the serial console. Dana debug print. So yeah, that is writing directly to here. So I'm not sure it has actually called setfiz. But I also didn't see this message appear either. OK, I do have to hit reset now and execute. Nothing is coming out. Let's just put this back the way it was. And then put some code in here. I'm just going to put... So what that will do is it'll point the frame buffer at the bottom of memory, which contains garbage. So if the screen is being set up at all, we should see something on it. And actually I will set that to there so we don't turn the backlight on unnecessarily. OK, so execute is on the screen. But we do have a prompt. We can see that setfiz has been called. And here is the frame buffer address. While that was uploading, over here I looked up the UC Linux code for the Palm Pilot. And this is not the Dana, but this is another Palm device. And here it is initializing the LCD. So this underscore start is code, apparently. So you can see here, we turn off the LCD controller. We configure it. Then we turn it on in multiple stages here. We do quite a lot of fiddling with pf. Here, after the configuration happens, it sets things up to display the splash screen. That is what Penguin Bits is. It's a bitmap containing a Penguin. A05 is LVPW, which is the stride in probably four byte blocks. And then we have the width and the height. And that is all there is to it. Well, we can certainly copy some of this. Now, pfdata85 is, yeah, I think this layout doesn't match the Dana's. I am interested in that it is configuring a value for LCKcon and we aren't. But of course, we know that there's nothing in LCKcon apart from the top bit. I think that this was recorded from running a ROM emulation, which is why it's kind of undocumented. I don't think there's anything more useful there. What version is this? No, that's not useful. So the main thing that comes to mind to me is all that fiddling with port f. We have turned on whatever is on port k. Let's see where that is being. Oh, that's interesting. It's being touched a lot in the contrast code. Wait a minute. Wait a minute. This is the same loop we were seeing before. Here is our bit counter that starts the top bit and goes down. And if we go down here, yeah, there's the LSR that shifts it right by one. So we AND D4 with this to test a particular bit. Except now, instead of being a hard coded value, it's whatever's in D4. Here is a D5. I don't like this compiler much. Here it was in D4. This is programming a contrast generator of some description. Right. We need to copy this. I think it's working. It's just that we're not seeing anything in the screen because the contrast generator, some pulse width generator, is not configured. So what parameters do we have? You've got 6 and 8. 6 is a byte. 8 is a long. So 6 is a byte. 8 is a pointer, actually. So this is at 6. That one is at 8. Much better. This decompilation is not working because I didn't tell it the registers were volatile. Let's actually just go and do that remap, volatile. And you see this generates this terrible long-winded decompilation. However, it does contain the code that's actually here. So that looks like this is loading the byte at param2. Interesting. Well, we know this is a byte star. It may even be a char star. That hasn't helped. Right. I thought that this was a signed value. They can't tell it it's signed. So that's a byte star. So this is clocking out some kind of initialization sequence. Then we get here where we clock out the actual contrast value, which is calculated in s var. I don't know where it got this plus 7 0 from. And then there's more clockings. I think we need to clone this code. But first, let's have a quick look to see where it's called from. Only one place which is here. What we do is we push 10. We then push the byte 8. And then we push that's taking three parameters. This is using two parameters. It's also interesting to note that we know that one of the parameters is a pointer. Oh right, no, this is pushing a, it's not pushing the value 10. It's pushing a value off the stack. So this is our pointer. In fact, this is a function. The byte that's being passed in is always 0. That's param 1. So if param 1 is not 0, do this. Parameters are pushed from right to left. So in fact, there are two byte values. They are two aligned, because that's what this does. So is, yeah, there must be another byte. There must be another byte at stack 4 here. So param 1 is never touched. Param 2 is taken from the stack, taken from the caller of this. There we go, you can see these showing up. So who calls this? Well, the startup code, oh, so the asterisk means it's not being called. It's being a pointer to it's being taken. So what this is doing is it's setting up the system call table, which means that actually this will be called from a trap somewhere. And I've got no hope of finding it without more knowledge. So I don't know what values are actually passed into this. So we'll just have to try it and see. So I would guess that the parameters to this, I think that this is a simple flag to say, actually update the contrast. And this is a pointer to keep doing the wrong thing there. And this is a pointer to where the contrast is. So if the flag is set, then we update the contrast based on the value that got passed in. So this can be, it's been used in lots of places. That's just a temporary. I'm surprised to see this because I would expect this to be returning what the contrast is currently set to, but it's not. My suspicion is that by returning 0x80, it's because whoever did the port to this system didn't have a way to return what the old contrast was. Although why they don't just store it somewhere, I don't know. So I think we've got enough information to actually do this. I am a little puzzled by this value. So divided by 3 plus 0x70 minus 0x70. This is doing some sort of calculation to determine what value to send to whatever this chip is that we're talking to. The other thing I'm a bit concerned about is why it's doing this strange repeated setting of the value. It could just be a cheap and nasty way of doing a timing loop, to be honest. But there must be other better ways to do that. Well, I'm going to go and look up the 68,000 instruction set to see how long each of these things takes so that we can do our own timing. So or i with byte is 12 clock periods. But I don't know what a clock period is. It may not be the same as a clock cycle. So I think in the interest of simplicity, I am just going to copy this code. So that's going to go into our machine code section. All right, just do I need to set up a stack frame to get at the parameter? Nope, we can do this. Can we do it all in the registers that get saved by default, whatever they are? We can see that it's D0 and A0. This looks like you get D0 and D1 and A0 and A1. It's not using A0 or A1 anywhere, so maybe you don't get those. So the A0, A1. OK, so the first thing we do is send out the initialization bit. 1, 2, 3, 4, 5, 6, 7, 8. Suddenly unable to type. So let's just convert these into binary. B is 1, 0, 1, 1. This is 0, 1, 0, 0, 0, 0. So F7 is 1, 1, 1, 1. 0, 1, 1, 1. BF is 1, 0, 1, 1. 1, 1, 1, 1. Is that all PKs? Yes, it is. If this is clearing these two bits, I would kind of expect, oh right, these are the bits we're about to set in the next chunk of code. So what we are doing is we want to and these two together to test whether the bit is being set. If it is 0, we clear the bits from port F. If it is 1, we set the bit in port F. So I'm just trying to think if there's a slightly cleaner way to do it than this. And this will do. So BEQB is 0. This point, the bit is set. So we want to do 0, 1, 0, 0, 0, 0. So that takes us to here where we 1, 2, 3, 4, 5, 6, 7, 8. These are 4 zeros. These are 0, 8. At which point we, BF is 1, 0, 1, 1. 1, 1, 1, 1. OK, so LSRB1Z0. No, D1, this one. If it is not 0, we still have bits to go. So go back to the loop. Otherwise, we ping this bit again. 1, 2, 3, 4, 5, 6, 7, 8 times. We've done F7BF. Yes, that was this. And we are done. So this is setting this bit of port K. Double check that's right because it doesn't look it. 4, 0, PK data. 1, 2, 4, yes. This is PF. And this is B. B is 1, 0, 1, 1. 1, 0, 1, 1, PK data. So what this is doing is it's setting a bit that tells whatever's attached to this that coming up is a command. We wait, then we unset it. But we also unset this bit. Ah, right. This is clock. OK. So we set the command is coming up. Clock, wait. Unclock, clear the command bit. Then we set the data bit. This is a different clock bit. Possibly this is the clock bit and this is the command bit. But this does seem to be what's going on. Dana set LCD contrast. Oh, does this really not support binary? I'm sure I saw binary in one of these assembler files. Here we go. OB, that's what it is. Yeah. Percent is used to introduce the register. So change that to that. And that's not right. That was our BF. Actually, you know what? I am going to do it like this. So that should make it clear what bits are being set and what aren't. And I do need to check up on what chip we're actually talking to here and see if I can find the data sheet. Should we at some point be setting this bit in this termination clause like we're doing here? Because we set it here and we reset it here. So PK data F7 BF. Apparently we're not. OK, what does this do? Symbol loop is already defined. Can I do that? You're right. OK, that does work. What this does is it says branch to the closest one going backwards, which is here. So we could replace this as well if you wanted to. OK, so what kind of values are we getting here? Well, if this bar is 0, that becomes minus 0x80 divided by 5 minus 7 0. 0x80 divided by 5 minus 0x70 is minus 137. And likewise, if we went in the other direction, let's just pick C0 for some reason here. So C0 minus 0x80 divided by 3 minus 0x70 minus 90. So let's pick minus 100 and see what happens. Is it declaration of data set LCD contrast? OK, and we need to define this and we haven't defined PF data yet. So where did I put that data sheet? Here it is. Port F data 429. So let us execute. Oh, it crashes. At 856C, we're using D0 and D1, which is doing set LCD contrast. We are pushing 156 onto the stack. So this is pulling the value from 4. So Big Endian value starting at offset 4. So this is picking the least significant byte. Then we call the subroutine, which is this. So program counter is 856C, 856. I forgot to tell it these all needed to be byte-sized values. So it's doing a four byte load and store with hilarious consequences. Well, it's doing word size, apparently. Now it's doing byte-sized. And what happens when we run it? Woo-hoo! I don't know if you can see it, but I can see garbage on the screen. It's very faint. So I have to look almost side on to see that. But it's definitely there. We have a working screen. Right, we now just need to figure out what a sensible contrast value is. But that is easy. All we need to do is what we do is this. So that will then cycle through all the different types of contrast. And when we see something on the screen that looks roughly right, we simply hit Control C and look at the last value printed. Stuff's appearing. It does look like we want a fairly high number, but it's not as contrasty as I would have hoped. Maybe I'm just looking at things from the wrong angle. Yes, from where I'm sitting, I can barely see it. But it does look like all contrast values are, in fact, negative. So I'm a little bit surprised that this didn't seem to do anything. Possibly again, I just couldn't see it. Kind of give that a try. All right, let's see what this does. I can't honestly tell if it's changing. Yeah, I genuinely cannot tell if anything's happening. I suspect it's not. Now, we did find that initialization code that was setting 0x70. So let's just try that. And I will also remove that line. So now we should be seeing the in-utos frame buffer, whatever's going to be in it. OK, let's see what this does. I think I can see a C prompt in the top left corner. Nope, it was a smudge on the screen. OK, so let's try it without the serial console. OK, so we run it. No, I... Yes, of course, this 0x70 value is below 0x80, so it's not going to do anything. Let me just try to find that code again. It was display in it, wasn't it? Display hardware display in it? Yeah. All right, PKData, here's our code. And it is indeed writing 0x70 to the whatever it is. I wonder if 0x70 is a reset command and anything with the top bit set is a set contrast command. Let's try that. I am going to have to take the lid off this thing and find the data sheet for the chips on the LCD panel. I did notice when I was disassembling it earlier that there's actually... It's quite a big PCB in there. OK, what does this do? I see stripes. So in a desperate attempt to try and make that visible, I turned the backlight on, but I don't think it's helped much. However, you might just be able to make out... I wonder if I turn these lights off, that will help. There is some mangled text across the top of the screen. And I figured out why, which is that the fiddling I've been doing with the screen size is inconsistent. So this wants to be 560. Actually, let's put these in here. So here, we want to change this to Dana's screen width and Dana's screen height. Now, I think there's some more stuff wrong as well. So let's just have a quick look through here for more Danae things. No, that seems to be it. Because it's only actually initialising a small chunk of the screen. Is that actually better like that? I think it is just. Now, other things that could be wrong is, does this register... Does the LSSA register that bad hyperlink? Let's try this one. Nope. Here we go. Does this register have to be aligned anywhere? Now, it does say that it must be stored in a one megabyte boundary. But I think other than that, it doesn't care. I mean, it needs to be word aligned. Have I programmed any of these registers incorrectly? This, I think... Right. We have a 16 colour display, but a monochrome frame buffer. So I think this needs to be a different calculation. So instead of divided by four, I think this needs to be divided by 16. And we want to round up. Actually, let's just do it like that. So I think this might do something more appropriate. OK, so we run it, and I don't see anything. Nope, if I look more carefully, I can in fact see an emucon prompt at the top of the screen. The contrast is terrible, but it is actually working. OK. Good. We are getting somewhere. So right now, let's go into the config. And... Oh, right. We're not getting a splash screen because it's not the first boot. So it goes straight into emucon. OK. Well, what we're going to do now is to turn on the whole VDI... Sorry, we've got the VDIs to turn on the AES, the entire GUI. And let's just make sure that builds. And this is giving us a much bigger image. It's now 200K. So let's upload this. And this is going to take a while. Yeah, this download is over twice as long as the previous one. I know what I'm going to be doing next. This is actually not the slowest download I've had to work with. I once had to work on a dev board with a 300-byte per second upload speed and a 1 in 3 chance of a transfer failing after multiple megabytes. OK. And let's run it and see what happens. And there is indeed a gem desktop. I do not know how much this is coming up on the camera, but down here there's the printer icon. Up here is drive C. Here is the trash can. And there's a menu bar along the top of the screen. That is fantastic. I am very pleased that works. That could have been so much harder, I have to say. So what I am going to do now is call it for the day. I'll just do a little bit of tidying and some check-ins. I'm actually getting rather behind in my editing. Let's get rid of some of the extraneous tracing. The next thing I'm going to be doing is to introduce a compression layer in the transfer. I should be able to reduce the size of the image to transfer by about a third to half, depending, because any further development is probably going to need. Well, for the pen, I'm going to need the full image. For the keyboard, I can use the console. I also really do need to take the thing apart and check for chips. But it is now nearly all working. There's only two more things, two more major things we need to do, which is the mouse and the keyboard. The mouse, of course, is the pen-based touchscreen. The keyboard is the keyboard. I think they are both connected via SPI2, so that will be a bit exciting to investigate. And again, I need to know what chips are attached to the other end. So that's awesome. I hope you enjoyed this video. Please let me know what you think in the comments.