 Okay, time to do some more work on this. So the story so far is that I am porting emutos, a re-implementation of Atari TOS, to my AlphaSmart, a laptop thing. And last time I got it up to the point where it was starting up and making a certain amount of progress before stopping because I couldn't interact with it. And if you've seen the last video, you'll notice that this screen here is not what it was showing last time. I don't know why it's doing that now. I suspect that I had inconsistent builds due to changing some of the various options and not doing a make clean in between. Have I mentioned how much I dislike make? But this is nice because it's actually showing the proper emutos splash screen. So what we're going to do today is to try and make it so that we can interact with this because currently it's not listening to the serial port. The serial port in emutos is interrupt driven so we will have to get interrupt set up and we'll also have to get timers working, which is lots of interrupt handling code. And we're going to do this with the help of the datasheet for the CPU. And it turns out that this CPU has a rather odd interrupt system. I don't know yet whether that will help or not. And over here, if I can find it, here, here is the NSA's amazing reverse engineering tool, Gidra, looking at the Parmos ROM for this machine so that I can go look here and figure out what actual running code for this hardware does, which is very nice. So let's get started then. So the way the interrupt system on a 68000 normally works is each hardware device attached to the system is told which interrupt vector it's supposed to assert when it wants to raise an interrupt. And then there's an enormous table of vectors, which you can see here, which contains the actual address of the interrupt service routine that's called when the interrupt happens. However, this machine doesn't do that. Instead, there are only seven different vectors here which you can map to a particular number of user interrupts here. And all the hardware devices will generate one of these. So a lot of the devices will share interrupts. The first thing we need to do before we can actually do anything that raises an interrupt is initialize the interrupt system. And one of the things we have to do is to tell it which vectors to use. Now here in Gidra, I've already done a bunch of work on this. Luckily, the people who made the ROM didn't remove any of the symbol information. So it's littered with strings that tell me what all the various routines do, which is rather useful. But I can find the IVR register at address F300. You can see there. And then Gidra has collated a cross-reference table so I can go here to the routine called hardware pre-RAM in it. And I can see that it is being touched twice. And Gidra is trying to pop up fragments of the code. So you can see that's initializing it to hex 40. What's this one doing? Update. Yeah, the pop-ups aren't very reliable. But I can double-click and go there. 1-8, that's interesting. Because the datasheet here says that the minimum value is 0x40. So Vector 1-8, where's that vector table is? Here. So they're reusing the auto-vector table for the interrupt vectors. This machine does not use the auto-vector interrupts. These are a, on the original 68000, these were a thing to allow old-fashioned devices which didn't know about user interrupt vectors to raise interrupts. So reusing those actually makes a ton of sense. So we're going to do that. So let's find our hardware-specific routine. Hardware-specific init routine, which is here. And let's initialize some of this stuff. And we need to define values for the registers. That is a word, isn't it? Nope, it's a byte. Let's just poke at this code a bit. So further up, it's set up the RAM. We've done that in our Dana init script. And at this point, it's looking at a bit on one of the input lines, testing it. And I think I can tell it to not do the... There's a bug there. You have to click that twice to turn it off. Interesting. So I don't know what's attached to this device here on port D because if that bit is set, then it skips all of this initialization code. But if it's not set, then IVR is never updated and it retains the original value which was set near the top of this routine here, which is hex 40. So I'm not sure what that's doing. Anyway, let's try 180 and see what happens. So this should set the... So that does actually build. We also want to set the control register. Yeah, this is all hardware specific and I don't know what hardware is attached. So we're just going to come up here. Here is the control register. Here is where it's being initialized to setting the top bit, which is pole 1. So I've no idea what's attached to that at this point, but we're going to have to copy it. That was 302. Yeah, and 304 is IMR. So let me reset the board and I can use my tool to print the value of these registers like this. Okay, that's just printed all registers beginning with an I. We can see that ICR is zero by default. IMR is a 32-bit register. This allows you to enable or disable individual interrupt sources. And we are going to FFF7, but it's treating this as a word register. So that means it's touching the high half. So F7 means it is resetting MIRQ1 which is something. But by default, everything is off, so we're going to leave everything off. So that will be A. Is there anything else we need here? The status register tells us which interrupts are fired since the last time we looked. And we want this to be cleared. IPR is a pending register. If an interrupt source is masked, that is disabled, the hardware will still generate an interrupt. It just won't interrupt the CPU. And instead, it will set one of these bits. I think this is not useful for us for the time being. This one allows you to configure certain pieces of hardware, but not all of them, to which interrupt you want to generate. Which again, we're going to ignore for the time being. So that should be us setting up the interrupt system. And that's all there is. Now the fun starts here where we need to initialize the system timer because we're going to have to configure one of our timers and have it generate interrupts and make it do things. So, yes, fun. Does that actually build? No, it does not. It's not a D word. It's a long, isn't it? Here is the code that is generating, which is just what you would expect it to be. Okay, right. The timer is the datasheet. There are two timers that are very nearly but not quite identical. We want to set them running in restart mode so that they count up until a particular value, which point the interrupt is generated and the timer resets. We want the timer to run at 200 hertz or every 5 milliseconds. Now we can... Actually, the first thing I will do is I will find the timer register here in the ROM, TCTL1. This is being referred to from a routine called Hardware Timer in it, so that's probably a good place to start. Here you can see it configuring it. Actually, I should be able to... view the compiler and Ghidra has attempted to turn the machine code into C. Here you can see what it's actually doing. So, we are doing some calculations to presumably figure out the timer parameters. Setting it up, we then enable the timer by setting the timer control register and we unmask it to enable it as an interrupt source. Our code is going to be almost identical except for this stuff up here which I don't know what it does. So, what is this doing? Well, actually, I'm slightly jumping the gun then. So, there are two timers, Timer 1, Timer 2. They are nearly identical but Timer 2 is more configurable than Timer 1. I believe from looking at the source that Timer 1 is intended for use as a system timer and Timer 2 for something else. So, I am going to use Timer 1 for this. It should be easy enough to change if it's not. So, we need to put some register values in. TCTL1 is a word at 600. Parmos here is setting it to 0x33 which is 1, 1, 0, 1 hang on. Is that doing a byte or a setting a word? So, all zeroes 1, 1, 0 1 hang on 1, 1 Gah! I am failing to turn 3 into binary properly. 0, 0, 1, 1 0, 0, 1, 1 So, that is a setting this bit to enable it. Setting clock source to 1, setting IRQ enabled on say you wanted to generate an interrupt and OM here is becoming 1. So, this bit needs to be reset because you want restart mode. Capture is remaining 0 because we are not using the capture functionality. The capture functionality will copy the time of value into another register when an event happens which is useful for stopwatches and the time and that kind of thing. Output mode I think this is when you configure the timer to generate an external pulse to some external device. They seem to want it to toggle. This one is on clock source was set to 1 so that is going to be system clock 0, 0, 1 and timer enabled. So, we will just use the same things. Okay. So, this is also setting well it unmasking the appropriate interrupt. D here is 1, 1, 0, 1 so it is un-setting one specific bit. So, we go up to the interrupt mask 1, 1, 0, 1 is timer 1. Okay. Copy the same code. This... So, these two values are setting the speed of the timer and the value that will be used to compare the timer again. So, this is the roll over value. So, it will count at the rate determined by this until the timer reaches this and then the timer will trigger. Unfortunately, I have no idea what these parameters here are up to. I will just do quickly oops there we go. I will just remove a lot of the... It has tried to guess what the parameters are and has mostly got them wrong. So, that will improve things a little. So, this is this is repeatedly dividing the parameter wait a minute. This is the division routine. Where does it get its parameters from? I think probably D0 and D1 by the looks of it. So, let me just add these custom storage. Do not like this user interface. It's very cumbersome and slow. D0 D... Why is that turning into... I need to set so much stuff in so many places. So, add storage register in D1 and it figured the rest out. So, now you can see two parameters coming in and we don't care about that end of the division routine. We care about this end of the division routine. I said this end because now you can see it's figured out the parameters. So, this looks like it's dividing something by 100. Is it getting... Doesn't appear to be getting a remainder. So, I don't know what it's doing. This will be part of the calculation for the time of parameters. Anyway, let's go look up TPRER General Purpose Timers Programming Module Prescaler Well, it's a 16 bit value. The clock source is divided by the value contained in this register. So, I think the parameter that it's being given but it's not doing anything with Param 1 Well, we can figure out where it's cold cold from, which is inside the startup code. No, we can't because what this is actually doing is setting up a set of vectors in memory. So, I would need to define a structure based on this address and see who tries to access it. So, here's the structure. Can I go go to 1, 2, 2 plus 0x918 So, I think this is the place but it looks like there are no cross references here it looks like Gidra hasn't seen any code that actually calls that yet. So, this is a pointer so, no, we can't see who's calling this. Yeah, this is clearly setting up a jump table that will be used by other bits of Paramos. Anyway this is no doubt the clock frequency of some description. It's a word I can tell it's a word that will improve things but I believe that we're actually going to have to calculate this ourselves. We want a 100 hertz timer so it's 5 milliseconds. Now, there should be an example here there isn't. Fantastic. I believe that the clock of this thing by default runs at 16 megahertz. So, see. So, 16 megahertz is about that many ticks. If we so each tick is this long in seconds. Each of our desired clock ticks is 5 milliseconds that's 5e-3 so, we want to generate and interrupt every this many clock ticks which is too big for the 16-bit comparison register. So, we are going to divide that by 16 that will now fit. So, where's the pre-scaler definition? So, to divide by 16 we have to set the pre-scaler value to 15 and then when this many ticks happen we want the interrupt to happen and the interrupt is so, t-comp 1 is the comparison value which is 16 bits 604 604. The system clock on this thing can actually go to 33 megahertz but I believe it runs at 16 out of the box and in order to change it we have to fiddle about with the PLLs and I don't want to go there yet or even at all. So, this will now generate a interrupt but we haven't actually put an interrupt handler in and we don't know what vector it is using. Up here one of these registers in the interrupt level register allows you to configure which vector is being used for SPI 1, UART 2, PWM 2 or Timer 2. However, the Timer 1 vector is hardwired to um... I think it's level 6 where is it set to? This is how you can configure where the clocks come from for the timers. Apparently there is a system clock divided by 16 but it may not be coming from the CPU clock, it may be coming from this much slower crystal oscillator. If so then we'll need different maths. What is 32 in decimal 50? Right, they're adding 50 here to round the division. Yeah, I am not sure what's going on there or rather I know what it's doing, I'm not sure what the various units mean. Okay. So, we are going to need a interrupt handler and that is going to need to be in machine code. I can go and find the coldfire version of this, which is in coldfire2.s and if we scroll down here for example is the handler for the coldfires timer and what this does, this is registered on Vector61, hence the name of the routine and what it does is it if it returns from the interrupt back into normal code, but as it does so, it fakes a subroutine call to the emutosa's timer handler, which is Vector5MS. So, in fact, we are going to want to copy the same code. I haven't looked for Vector VMS yet. That's in, where is that? So, I would expect something would be assigning a value ah, here that's not it. Ah, yeah. Right. This is the routine that actually does the work. So, let's go find that. Right. Which is another machine code routine in Vectors.C which is this and this is why yeah. This is why we have to go through all these hoops because it's not a normal C routine. That's an exciting piece of code, to be honest. Okay, we're going to need a routine. So we're just going to copy what the coldfire does and call it that. We could put some of our start up code in here, like if the coldfire is done, but honestly, I'd prefer to use C. But we are going to have to actually set up that Vector. So, I still haven't figured out which level it is being which vector it's actually wired to. 32-bit timers, compare and capture, interrupt, enable, timer, status. That's all there is. Sysclock, Sox16, clock 32, or an external clock pin. And we are setting it to clock source 01, system clock. Whatever system clock happens to be. So, I think it is... I think it's interrupt 6. Timer X interrupts issue to the interrupt controller if the IEQ enable bit is set. Right, so I don't think I think this isn't documented here. I think this is documented in here. Oh, here we go. Timer unit 1 level 6. So, that is going to be, yeah, auto vector interrupt number 6. Which is at well, it is Vector1E. So, we do need to set this here was the cold fire setting the vectors. Here we go. Interrupt Vector 1E was 6. Yeah. 30 equals Dana int 6. And in fact this could be used by other things, so we are going to put it there. Interrupt Vector is standard. Okay. Exception Vector is not also a cold fire thing. Yes, it is. Okay, we are going to steal this because it's useful. Or are we? Let's go see how the Amiga does it. So, I know that the int VBL is an interrupt handler. So, this is the vertical blanking interrupt Amiga VBL. So, where is this getting set up? Here we go. VEC level 3. This sounds more like our thing to be honest. And I bet that's defined in Amiga.h. No, it's defined in Bios vectors.h. Okay. Hex address 64 level 1 auto vector. Right, these are already pointing at the auto vector vectors. What we need to do is to say VEC level 6. Right. And this will fail because this is not initialized anywhere because we need to declare that Dana int 6. And then this will now fail to link because we haven't put it in our make file. So, all we need to do is put this in and build. Right. And it links. So, if you run this it should actually set up a timer and then when the timer fires it will call this routine which will just drop out into the middle of nowhere and everything will crash and burn horribly. So, what we're going to do now is to copy the cold fire code. I need to check that number because the cold fire is not very thousand like in some ways and the exception vectors might be different. So, we push SR into the stack. That will show up as a parameter. I have no idea what this is but we push that onto the stack too. We load the 5 millisecond vector value from memory into a register and we call it. Now, there's a cold fire int 61ac routine here. Oh, I've missed a bit. I have missed a bit. Right. Before we do this right, we need to push the what we're doing here is we're discarding the old exception stack frame and now we are setting up a new stack frame. So, that has the return address which is going to be this routine here. We then push SR and we push the, yeah, that pushes SR and then we push this magical number here and then we jump to the handler and on exit we put things back approximately the way they were and I've also missed a bit which is to save registers that we're using onto the stack. I am I'm trying to translate the machine code dialect right now what this is doing in the acknowledge code I'm going to have to go look up what RTE does. I know it's returned from exception but I need to know the details. Okay, RTE pops it pops R and PC so we are pushing PC and SR but then we're pushing this other magic thing so this comment is wrong what this is doing is it's subtracting 16 from the stack pointer it's making space for four words the four words here leaving the old exception stack frame exactly as was we're simply creating a new stack frame on top of it which when retracted over when our vector handler calls RTE will in fact jump to here so what we're going to do here is we are going to restore the registers that we saved we are then going to discard the stack frame where we put those registers and return from the interrupt proper now the cold file here is acknowledging the interrupt this is a thing some architectures need to do where you tell the hardware that the interrupt has now been serviced and it stops asserting it I do not know whether this platform requires it I looking at this I think we do when programmed as edge triggered interrupts external I know here we are all interrupts from internal peripheral devices are level triggered interrupts the interrupt handler they are cleared at the requesting sources so either we need to tell the timer that we have cleared it or we don't do anything I am just looking to see if I can find something talking about clearing the status register did mention it but I am not sure that was relevant control bit well over here in the parmos code we should be able to find our vector at level 6 our vector is at address 7 8 if we go all the way to the top of memory and find address 7 8 which is here which is vector IRQ 6V and this is being referred to from here so these two addresses close to each other look promising this is not a function I have looked at yet so what is it called hardware sleep unfortunately gidra is a little bit confused because the parmos code is riddled with these which are system calls and it uses the the next two bytes of some kind of payload and gidra just gets thoroughly confused by them so here it is this might be waking up from sleep and reprogramming everything you can see it is setting IRQ setting the vector to something source code disassembly there so what is it setting it to here well this routine which if we disassemble it is very small and it is a function and it is called prove wake up int handler this is part of the code that wakes up after it goes to sleep so that is not what we care about it is setting all the interrupt vectors to this routine so that presumably when the system wakes up from an interrupt it calls this thing and it then proceeds and wakes up back into full state as part of the power management for the platform but it is not what we wanted but so if you go back to the vector itself there is another couple of uses where it is written to here this is the same piece of code because gidra hasn't figured out that this is all the same function so it has unfortunately let me down here I was hoping that I would be able to see where this was originally being initialised and then go and find the handler itself well here we have the actual start up code which is all a bit weird eventually we end up hmm we have two mains to find so something here is probably going to initialise all the vectors this piece of code is clearing all the workspace at this address which is higher than we wanted this is setting up a vector of some description what's at address b c it's the right, that is the trap 15 instruction so I actually want to name that hang on, it's already got a name trap dispatcher this I don't know what it does it's a private subroutine which doesn't have a name but I don't think it's useful for me yeah I was hoping to find something in here that sets up the vectors but I don't think I have that is a shame well one thing we could do is if the interrupt is cleared by resetting the status bit which actually seems likely we can simply go to 60a the wrong place 60a is the right address, that's here tstat1 which does not appear to be used by anywhere, by anything so this is a word but there are no cross references so Gidra has not yet seen any code that refers to that address which is interesting if this sentence here worries me to be cleared these bits must first be examined and the bit must have a value of 1 now I don't know whether the hardware actually enforces that is it saying that I have to read it and look at the value before I can clear it or can I just write 0 to tstat1 and clear it I think I'm just going to assume that I can do that so tstat1 and tstat1 is at ff f60a so we have an image with hopefully all the timer stuff in it although I'm still curious about this mysterious thing so that was in vectors.s int timerC is here isn't this seems complicated yeah this does not seem right to me so this routine must end with an RTE so we have to go through at least some of this junk in order to set that up this this status word is only valid on the coldfire so we can get rid of that so we have a simple stack frame from this we do need to save d0 and a0 because we have to use d0 to push the status register and we have to use a0 in order to do this in direct jump we don't need to save d1 and a1 so why are we so this will yeah I think that is now more or less right so let's write it and see what happens so it's downloaded and let's run it and it instantly crashes ok well I cannot honestly say I'm surprised so the first thing we're going to do is just try removing this because this should now turn this into a very simple interrupt handler which pushes some stuff but then doesn't pop it off again and take a look at that coldfire code yeah of course this gets these two words are supposed to be popped off by the RTE so let's do that and then we just have to oops didn't want to run it again and now we just have to write it and run it and see if that makes a difference so does this still crash yes it does ok that's fine so let's just replace this with something even simpler acknowledge timer interrupt and return and we write and we execute it and we see what happens alright execute and it crashes ok next step we're going to turn this off we're no longer setting up the timer if this still crashes something's wrong with our configuration here ok that proceeds but it didn't produce the splash screen interesting well so this suggests that there is something not right with my timer configuration let's do everything except enable the timer enable the interrupt I am expecting this to work to be honest I think that this is not hooked up to the quote system clock I think it's hooked up to something else it said sysclock timer control bit here we go so let's go look for sysclock in the documentation what is sysclock clock generation ok sysclock here is fast the CPU clock and here is the 32kHz crystal clock from elsewhere so that does suggest that sysclock is indeed the CPU clock which is running at 16MHz ok well run interest let's do it that way around shall we yeah each time I have to reset it do a write and pause the video it uses up 30 to 45 seconds at this end and about the same amount of time again when I have to edit all this back together the coding videos are actually really easy to edit ok as there is just all the segments concatenated together the workbench videos are much trickier because I have to like make a little decision right what I suspect is happening is that we are not acknowledging the interrupt so the first timer interrupt comes along and generates an interrupt and then the interrupt handler here is serviced but the interrupt is still asserted when this thing exits so it goes right back into the interrupt handler again and in fact I can test this by doing I want it to be one on success so if that hypothesis is correct then we should get a stream of that signed as the interrupt handler keeps getting re-invoked and I forgot to re-emable the timer I wonder also if I don't want to change these numbers so 16 MHz approximately I'll divide that by 256 multiply that by 256 gives this many tips I want to try and set the timer to produce approximately one pulse a second because that will make debugging easier so at 16 MHz this is going to need to be clearly 16 E6 ticks per interval however that is way too big for a 16 bit comparison so if we divide that by the maximum value which is 256 we get this which will just fit so divide by 256 comparison is 6250 and let's give this a try I'm interested to see whether we get slow at signs or whole stream of at signs or no at signs in the hang alright, what's this going to do as my execute nothing at all interesting so what is it doing maybe that we can't actually set IVR to 0x18 and that's just not allowed we did see the palmos code doing it it's being written to in this code 2 which is setting it to 1 8 and initialising some things now we haven't received any signs at all which does seem peculiar so maybe it hasn't hit this code at all maybe I am not configuring things correctly here let's just take a look at this dener in it okay VEC level 6 is writing a value to 0x7 8 check this yep, 7 8 is indeed level 6 interrupt auto-vector we configure IVR, ACR, IMR IVR ACR is a word IMR is at 304 and has a long setting it to all F what I was doing there was unmasking these which could be doing anything these actually look like external interrupts these are IRQ 1 and 1-5 SPI RTI real time clock emulator interrupt so if these happen to be a certain interrupt then that would cause very bad things to happen like it's spinning furiously waiting for an interrupt to be cleared so let's try that alright and we execute and fantastic well it's no longer hanging but we're also not getting any out signs I wonder if we waited long enough would one show up what is actually left in this code we are clearing the timer and writing an out sign and that's it so let's put this back and see if anything shows up yes having an actual debugger would make a big difference as well as an in-circuit emulator in-circuit emulators are fantastically expensive and no I don't have one alright and now let's try it no out signs also interesting one thing I could have that would help is an emulator but I haven't been able to find a dragon ball vz emulator I mean we're not using any palm specific hardware here it's all built in functionality so this code should just drop in and work once you configure where all the RAM is that's interesting we do not have the prompt appearing to say you know welcome to emucon so is it hung it could very well have it might have hung on this do I did I get that right UTX one is at 906 yes it is the data is at 907 let me try this alright so execute and still nothing okay I wasn't really expecting that to do anything so has this actually called the interrupt I think it must have otherwise it wouldn't have hung because if we don't unmask the interrupt everything is fine so of the interrupt control registers this one is not relevant for us the mask register excuse me yawning is the one where we actually unmask our interrupt we know that is working the service register yeah we looked at that before interrupts from internal devices are cleared at the requesting sources so we can write to these but it won't help is it timer one this bit indicates that the timer one has occurred this is a level six interrupt they are read write this documentation is a little confusing so for like IRQ six here which is not the same as a level six well as the timer interrupt it says the interrupt must be cleared by writing a one to this bit but five up here says source interrupt must first be cleared but doesn't say how so the level six interrupt is actually shared by several different peripherals we know the timer one is one we know IRQ six is another but we haven't unmasked IRQ six in the IMR so that we should never get one of those but if the interrupt vector was being called I would expect to see an at sign so I think that is not being called one thing it is worth doing is just we manually call the ISR there we should see one at sign appear that will just let us verify that the routine itself works so why is this hanging how can we find out well I think the first useful thing to do is to go into bios dot see and enable the copious tracing oh yeah this is tracing is appears in screen dot see and we can actually turn that off so let's see what comes out now okay execute so we've just called in its system timer which is our code which is here we've seen the first at sign and then it all halts so this indicates that everything is going bad immediately after I unmask the interrupt let's take another look at the parmos code T control 1 timer in it this doesn't look sophisticated where is clock frequency being set a pre debug in it private calculate the pulse width value that looks like that 3.6 million now there was that divide by 100 in the clock code so I wonder is that a fixed point value 36864 there's stuff in here about the clock crystal a couple of values there's two standard values one of which was 3 32.768 kilohertz but there was another value 38.4 kilohertz that actually looks suspicious if that is 38.4 in 16 bit fixed point but the timer code here is dividing things in decimal we have 100 here 50 here let's see where else this is set from here here right it's this is calculating something based on the PLL configuration so let's go and look at the PLL configuration the PLL is the oscillator that drives all the clocks in the system so PLLFSR PLL frequency select register so uvr2 is the value shift by 8 that's QC so we should be able to rename that this PLLFSR this is the bottom byte that is PC so that's multiplying PC by 14 yeah this actually requires knowledge of how PLLs work which I don't have but this is PCX14 so this is the value that we actually get out of the calculation and why is this code writing the value to clock frequency twice that's odd let us reset the board and use the tool to figure out what PLLFSR is set to 8347 so QC is 3 PC is 47 let's do this way around PC is QX47 PCX14 is therefore PC times 14 which is 994 so this is some kind of odd code to be honest so we add 15 then we shift left by 15 and then we shift right by 1 so this is the multiplication alright this ADEL is an extra left shift so that's shifting by that's doubling it which is a shift of 1 so that's actually shifting left by 16 why doesn't use a swap instruction and then we shift right by 1 ok so what is PCX14 plus QC and why is QC being added with 3 oh yeah it's extracting these 6 bits including these 2 bits that aren't used for anything so plus OX15 is 108 so what is 108 times that right this is the value that clock frequency is being set to honestly that looks very much like 33MHz I was pretty sure the system was running 16MHz from the trouble I was having with the board rate when I was trying to get the debug tool working ok so where is our timer init routine let's just copy this the user interface is quite old and it doesn't support all the right clipboard stuff so so you are for i this is divided clock is clock frequency plus 50 divided by 100 prescale value is initialized to DC while the prescale value is too big keep doubling the divisor value hang on I'm going to do this more cleverly ok so uv4 is the comparison value that's no it's not uv4 is the prescale value uv1 is our divided clock uv2 is our comparison value some types and this is comparison value so I think this 100 here is our clock frequency that is this will generate a 100 ticks per second somehow so that's not going to work it's not dword it's u long so let's see if this actually does anything up and let's try this alright so now when we execute it then nothing happens see I was kind of expecting this line of printing to produce something because this hasn't actually done anything particularly exotic I wonder is it it can't have hit division by zero error and say right we can't call dener in 6 there because that's an interrupt handler and it terminates with RTE RTS is for subroutines RTE is for interrupt handlers so that was always going to crash there that was just not helpful so let's try this again now I've gone through this code I actually understand what it's doing which is this is the desired clock and it keeps halving that keeping track of how often it has to adjust the scale value until what's left is a comparison value that will fit so this is calculating the appropriate timer for the calculated clock frequency and the interval we want the interrupts to happen so it's actually good that I figured that code out and I think I will actually also go and copy the PLL code wherever that got to it was here I will copy that in because then that will pull the clock frequency from the PLL configuration okay execute it got further alright now here we go here is the prescale and CV values we got so what I think has happened here is that it's set everything up it's working the timer is running it goes on and does some more work then a timer interrupt happens and it crashes but we haven't received an outside so the question is have I correctly set the IVR is this a legal value I mean we've seen it in the code here let me just copy that for reference has it hung somewhere here I can't imagine it could are these interrupts different from normal 68000 interrupts well I don't think so but it occurs to me that I can very easily figure that out so an RTE is a 4E73 so go to the top of the code search memory for 4E73 so this then finds all the RTEs in the ROM and there aren't very many here's one this is the trap dispatcher so this is what gets executed when a trap instruction is called is there a search next, repeat search so here is some code this looks like the fatal exception handler I can tell by the big words fatal exception here here's some more code trap dispatcher again well this is a function called trap dispatcher that's interesting there's a debugger in this ROM might be useful to find so dd22 is where that RTE was so that was here so I'm going to guess that this does actually look like an exception handler so you push something onto the stack we do some work and eventually up two things off the stack we only pushed one on a 4 anyway I don't think that's what I'm looking for some more this also does not look like a simple piece of code these routines don't seem to have strings in them so I can't tell easily what they do without having to you know read the code and figure it out hardware low battery handler well that does sound like an interrupt and it's an ordinary function which starts here so hardware low battery handler so what is this doing we push some stuff onto the stack we do some stuff possibly call a routine and we exit can I see where this is actually set from it's read from here it's referred to there oh no I'm looking at the parameters not the actual routine itself the cross references for the routine here would appear there underneath it and they're not it hasn't seen any cross references for that yet hardware doc status what about this one looking for strings don't see any this looks like jump table stuff yep here is the jump table this looks like an exceedingly minimal uh what is this is popping lots of registers off the stack and then doing an RTE right I think we've just found a context switcher so I think that this is called to restore the state of the CPU for based on a structure in memory so I think that if we eventually find a string at the bottom of this this will be some kind of microkernel threading context switcher stuff target CFG routines I have no idea what that does okay well several of these what's this one 3 1e that's in the middle of this string so it's spurious and then we have this RTE in the middle of nowhere doesn't look like code no this does not look like code you see the disassembler keeps failing because it's found stuff that doesn't exist so I think this is spurious yeah there's some zeros here immediately above the RTE so let's tell it that's not code right well we found lots of RTEs none of which seem to help so that was good there's some stuff here so this appears to be looking at opcodes this could be some kind of debugger core and there aren't any strings well I think that's actually taken as precisely nowhere never mind however some cross references here look like they've shown up what was the what was our level 6 address 7 8 oh we've already seen these yeah it's the this code that we don't know what it does alright well okay so we seem to be getting an interrupt and then immediately stopping the options are that we've actually set this for the wrong interrupt now we set the base vector to 1 8 which is here so level 0 which is spurious interrupt that's not part of this table is 1 8 then we have 1 9, 1 A, 1 B, 1 C 1 D, 1 E is our level 6 interrupt auto vector which is where we've put the which we set up here so if that's not allowed and 1 8 doesn't do what I think it does then we have to set this to like 4 0 4 0 maps the interrupt to user vectors 40 up so that will appear at 100 up then we have to change this actually before I do that let's just do this first let's set all of those to Dana in 6 so now if any interrupt shows up it will be routed to the correct interrupt handler as opposed to just crashing the board so let's see if this makes any difference alright execute and it hangs well I think I am going to try setting it to the 100 addresses and just see if that makes a difference I don't think it will L value required as left side of operand and let's try this okay so execute ooh that's different yes that's because I was stupid yeah it's getting late and I'm getting a bit tired but that was an unaligned access error and that by the way is you want exceptions on unaligned accesses so that when you make stupid mistakes like this you know about them rather than just filling the vector table with garbage and run it hmm interesting done what I thought it had did I remember to make it apparently I didn't okay ooh interesting right what did that do panics at that address and then it immediately starts spewing incredibly corrupt looking tracing okay but at least we have an address now so that's more than we had 64 64 64 B con out one right that's not produced anything particularly useful it's just died inside the routine that's supposed to trace that's right to the console so something is horribly corrupt I don't think that has really helped unfortunately 640 I get the right address 6B5 4 yeah right 6400 is inside panic it's panicked in panic and then it just repeats hmm this does seem to be different than before I also see no at signs yeah I'm just double checking the addresses again oh yes and I did notice but then forgot about that all those addresses were wrong there was one too many F it looks like the compiler has truncated them down to 32 bits so that seems to have worked but it's still not brilliant so why has it panicked inside con out well you see here it got halfway through printing something so panicking inside con out suggests that something was corrupted inside con out itself so if it tried to call this and something went wrong and you ended up with corrupt registers that would cause bad things to happen but I very carefully didn't use any registers in any of this code just check the disassembly to make sure it actually does what else have we just put an RTE in ok so this then does exactly the same it is at least consistent so I think it's not reaching this interrupt handler somehow so this suggests that it's not calling the right thing well if IVR is set to 0x40 then it should be being mapped to vectors I think 6, 4 and up the vectors at of course vectors 40 and up at this address there's a reference here that if the IVR is not set then interrupt number 0xOF is returned as an uninitialized interrupt with vector 3C so 0xOF vector 3C is not here so how do I do this vector 0 is at 0 so this should just be item 4 so vector user becomes vector IVR plus I so vector 0xF is uninitialized interrupt vector 15 is this the one that's being called let's try that ok so now we execute and it crashes in precisely the same way so that hasn't helped but this address is different now hmmm that is one of the classic mistakes with macros because I put IVR plus I in here that was textually substituted in here so that it ended up being IVR plus I times 4 so it read IVR and it added I times 4 to it rather than reading IVR and adding I then multiplying the result by 4 I should know better than that ok let's try this what happens it crashes and our addresses are right back the way they were ok so that's done nothing so as soon as we turn the we enable the interrupt we see D is 1101 that should be enabling the correct interrupt it then immediately dies well not anymore it dies a little bit later oh yeah in another stunning example of incompetence the problems I was having the other day with unaligned accessors because the screen was 540 pixels wide which is not an even number of 8 pixel wide characters well actually I had misremembered the width of the screen so it's supposed to be that and that's fine 560 is 70 characters wide so yay so I'm wondering if there's something funny about the Dragon Ball exceptions so I'll just go and scout the documentation I'll do that offline I think so it turns out that the interrupts are indeed perfectly normal and you exit them with an RTE however I discovered one incredibly stupid mistake let me just undo this I was doing IVR plus I here reading the base vector out of the IVR register which only gets set up after I initialize all the vectors so IVR is set to some garbage and it was just writing random stuff so I have hopefully corrected that so let's see what happens oh yes he's been panicking that didn't actually make any difference fabulous well I can verify the code it does actually seem to have done the right things you can see here that VecUser6 has written the address to ignore these names here it's the name in the relocation it's important has in fact written to address 018 which has 118 which is in the right range I also went and looked up the Linux source code rather the UC Linux source code and it's doing everything in a perfectly normal fashion so here you can see it setting up all the vectors here you can see it initialize the IVR to 64 turn off all interrupt initialize the default handlers where it's handle level IRQ there's more in here so well here is a standard interrupt handler which does very much the same kind of thing I was doing it saves the vector number onto the stack does the thing pop it all off the return returning is more complicated in the nooks because it's doing task switching so there's this huge chunk of code that tries to context switch which I'm not doing so I'm still somewhat puzzled I have to say so I think what I'm going to do is basically fiddling with this isn't achieving anything I am certain there's an emulator somewhere that may be able to help if I can actually see what it's doing then this should be straightforward so I was kind of hoping to get more done today but part of the reasoning behind doing this old on video is to document how painful some of it is so yeah it's painful so I'm going to come back next time with hopefully some better news and hopefully we'll make some progress for a change I hope you enjoyed this video I'm back I found a thing look at this tracing BIOS in it Dana in it where we set up our vectors blah blah blah vex in it vex in it is in BIOS BIOS dot c what does this do it initializes all the vectors in it what does in it user vex do it's not in that file for a start it's in vectors dot s in it user vex it erases all the user vectors so what's happening is that no matter what vectors we set here they are being stomped on a bit later down the initialization process okay let's take that out of there put this in here cause this happens much further down the line in it system timer down here and let's try this and while that writes I'm just going to take a quick look at this yes we still have the at sign stuff okay and now let's run it and see what happens and we get at signs it was as simple as that fantastic so yeah that was a good our also wasted never mind now we know it works I wouldn't have gone through the process to figure this stuff out if I hadn't and I have more of an understanding of how this works so that's good hopefully we're receiving 100 at signs a second but I'm not going to do anything more with that now I'm going to wrap it and come back to that later now I hope you've enjoyed this video and please let me know what you think in the comments