 Let's now deal with another huge section of instructions. All of these instructions down here. So we can pretty much do it the same way that we did the ALU instructions. So instead of immediate direct indexed and extended, we have accumulator A, accumulator B indexed and extended. So accumulator A, for example, negate, that just means take the contents of A, negate it, and put it back into A. So I think we can probably cover all of these instructions. And I do want to point out something about negate. If you look in the datasheet, you'll see that it's basically zero minus whatever you're targeting, A, B, or some memory location. And you can see over by the flags that N and Z are changed and V has note one and C has note two. Now, if we look at those notes, we can see that the overflow flag should be set if the result is hex 80 and the C flag should test whether the result is zero. Now, that's really interesting because if we go to the programming manual and we look at the instruction description for negate, we can see down here that the overflow is set if there would be two's complement overflow as a result of the implied subtraction from zero. So in other words, the ALU is going to do zero minus the accumulator and whatever the overflow flag is, is whatever it is. And it additionally says that this will occur if and only if the contents is hex 80. So zero minus hex 80 is hex 80, which is why down here they say that the formula for the V flag is just the high bit of the result, seven, multiplied by the negative of the other bits. So that's one thing. The other thing, though, is that it says that the carry would be set if there would be a borrow in the implied subtraction from zero, the C bit will be set in all cases except where the contents of the accumulator is zero. And here in the formula, you can see that we're just oring all of the result bits together. Now, that is not what the datasheet says. The datasheet says that the test is whether the result is zero and then bit is set. And what the programming manual says is, if the result is non zero, then the C bit is set. Well, it turns out that if you do subtraction from zero, then the carry bit really is set when the contents is non zero. So in other words, this datasheet is either wrong, or there may be a line through the equals that just didn't make it through the scanning process. Now, in order to actually verify which one was correct, I did, of course, some formal verification. So we can see here that my formal verification is verifying whether the overflow flag is set if the expected output is equal to x 80, and that the C flag is set if the expected output is non zero. And this passes formal verification, which means that it's the programming guide that's actually correct and not the datasheet. So that was kind of interesting. Okay, so that's great, but it's not unheard of for the documentation to be incorrect. And in this case, we've got one document saying one thing and the other document saying another thing. So what I did is I went to the 6800 simulator on visual 6502, that's right up here. And this is a really nice transistor level simulation of an actual 6800 processor. So what we could do is we could, first of all, notice that it starts with the carry being clear here, and the accumulator being zero. So let's go ahead and put negate a in here. So I'm just going to go for zero. And I'm going to fill the next byte with zero one, which is just a knob, and another zero one just for fun. Okay, so now I just have to reset the processor. Okay, so the data byte is going to be 40. And then I'll just step forward a few steps until we see what happens to the carry. So this should negate zero, which would be zero minus zero, which is zero. Okay, and clearly we can see that the carry flag did not change. Now this is in direct opposition to, so this is in direct opposition to this note two in the datasheet, because the result was zero, right? There's the accumulator right here, it's still zero. And it says, well, if the result is zero, then set the carry. Well, it clearly did not. Let's double check that by changing, let's see, let's reset the processor. And I think, can we change the accumulator to something like, I don't know, FF? No, apparently not. Okay, so let's load the accumulator. It's too bad. Okay, so let's compliment the accumulator. So that will mean we're going to end up with FF. So that's four three. So let's just go ahead and put a four three in here, and then a four zero, and then a no op, reset the processor and see what happens. Okay, so we complimented the accumulator. So we ended up with FF. And then we negated it, and we ended up with one, and we can see that the carry is actually set. So this implies that the programming manual was actually the correct thing. So we have implemented this correctly. Now, the reason that I went into the flags is that ideally, all of these instructions are going to be handled in exactly the same way, with the exact same handling of flags. So for example, if you look at the clear instruction, right, and if we look up here in the poster, where it says clear, you can see that n is set to zero, z is set to one, v is set to zero, and C is set to zero. So that's interesting. So n, of course, would be set to zero because the high bit after you clear is going to be zero. Same thing, z is going to be one, because after a clear, the contents is going to be zero. So the question is, what is the motivation behind setting v and c to zero? So if we look first at the datasheet, and if we look at clear, we can see, okay, reset, set, reset, and reset. So there's no notes or anything. And if we look at the programming manual, because if that's true, and subtracting something from itself results in these flags being exactly this way, then we can simply use the ALU to implement clear and not have to have some special path for implementing clear. So this is basically my hypothesis. So in formal verification, in order to verify clear, what I'm going to do is I'm just going to check that z is one and the n, v, and c flags are zero. And in the implementation, and in the implementation for clear, what I'm going to do is I'm simply going to subtract a from itself through the ALU. And now I'll run formal verification and see if they match up. And perhaps it comes as no surprise that, in fact, that is what happens. We can use the ALU to subtract the value from itself and the flags will come out properly. This kind of makes sense because the carry flag really is the borrow flag. And when you do something minus itself, you're not actually borrowing anything. So that makes sense. And the overflow flag is the same because that has to do with some kind of accidental sign change. And you're never going to have an accidental sign change if your answer is zero. So this is good. So negate and clear can both be implemented by the ALU. Let's take a look at the other instructions. Okay, let's take a look at complement. Now this is not a mathematical operation. This is a logical operation. So all we're doing is flipping the bits. In other words, we're XORing with FF. So if we're XORing with FF, if that's the implementation, then does that mean that the way we set the flags for exclusive or is compatible with the flags for complement? So again, if we go to the datasheet and look at complement, we can see that the N and Z flags are going to be set according to whatever the output is. The overflow flag is reset, and the carry flag is set. And if we look at exclusive or we can see, aha, the N and Z flags are changed, the V flag is reset, but the carry flag is not affected, which seems to mean that we actually cannot implement complement using the ALU to exclusive or the value with FF, because then the carry flag would be affected. Now the interesting thing is that again, if we go to the programming manual, it says that complement is implemented by subtracting the value from FF, and that the carry flag is actually set to one. So which do we believe? Well, if indeed it is FF minus the value, then the carry flag is always going to be one. So here is our implementation. We're simply subtracting, in this case, the A register from FF and performing a subtract operation in the ALU. And in formal verification, we subtract the A accumulator from FF, and we check to see that the N and Z flags are set correctly, and the V flag is zero, and the carry flag is always one. Oops, with the slight error that this should be 4-3 and not 4-0, so that we're actually verifying the correct instruction. Now in this case, our hypothesis turned out to be incorrect, because we can see that formal verification came up with a case where it is not true that the carry is set to one. So apparently what it's been doing is it did a complement of zero zero. So the answer is FF, and the flags got set to D8. So in other words, the zero flag is zero. The negative flag is one, that's all correct. The V flag is zero, that's correct, but the C flag is zero, and we wanted the C flag to be one all the time. So the datasheet says that the carry flag should be set, and the programming manual also says that the carry flag should be set. However, our implementation was using the ALU subtract function to get FF minus the value that we want, to get FF minus the operand. So it turns out that formal verification found a case where it is not true, that if you subtract zero from FF, you will get FF, but the carry flag will be zero, which means that we cannot use the ALU's subtraction in order to implement this instruction. And we also can't do FFX or the operand, because for that operation, the carry flag is not affected. So this seems to be a special operation that the ALU will have to carry out, where it explicitly sets the carry flag to one. So unfortunately, this means that we will have to add yet another function to our ALU, so that it can set the flags properly. So this is now our new implementation of complement. You can see that we're not actually setting anything on input one. And on input two, we're inputting A, and then we're going to do the complement function. So let's just formally verify that and make sure that it works. And indeed, we can see down here that formal verification did actually work. It's a little unfortunate, because now we can't use the same operation for negate, clear and complement. Now let's turn our attention to decrement and increment. Again, can we use the ALU to subtract one or add one? So let's go to the flags. So here's increment, and we can see that the N and the Z flags are set according to the output. The V flag has note five, and the C flag is unaffected. So immediately this means that we can't use addition or subtraction, because those will affect the carry flag. And if we look at note five, we can see that the test is, is the operand seven F prior to execution? In other words, is the result hex 80 after execution? Let's take a look at the programming manual. And we can see that the programming manual says that the C bit is not affected. The V bit is whether there's a two's complement overflow as a result of the operation, which presumably means adding one. And it does say that two's complement overflow will occur if and only if the operand was seven F before the operation. And it says that the C flag is not affected. And unfortunately, they seem to have made a perhaps a copy paste error, where they say that the carry flag is actually going to be set if the result is zero, and it's going to be reset otherwise, which is not correct. And in fact, if we look at decrement, we can see that the C bit is also not affected by the operation. The V bit is set if there's a two's complement overflow. And you can see that there's some other weird thing going on here. And I'm not even sure what that means. But nevertheless, it does say that the C flag is not affected. So now here we can see our implementations for negate clear complement and increment and decrement. Again, they look fairly similar in that we're just using the ALU to determine what the result should be and what the flags should be. And in fact, it's just going to be the same for all of the shift and rotate operations. In other words, we're going to implement them in the ALU, the ALU will set the flags according to what it needs to be. And here's a nice diagram about what all the rotates and shift actually look like. And we can also see that the negative and the zero flag are set according to the result with the exception of logical shift right, where the negative flag is always reset, of course, because we're putting the zero in the high bit. So that makes perfect sense. The carry flag is affected according to of course, whatever bit happens to end up in the carry flag because the carry flag is used for all these operations. And the overflow flag has a note six. Let's see what that is. So note six says that it's equal to the result of the negative flag x or the carry after shift has occurred. Okay. So here's how I've implemented the rotate and shift instructions in the ALU. You can see at the top this is sort of the pattern that I want to implement. So for example, for rotate left, I basically want to place basically nine bits with the carry on the least significant side and the input on the most significant side. And I basically just want to map that so that carry goes into output zero. The high bit of the input goes into the carry. And then the middle bits or the lower bits of input go into the higher bits of output. Now, I can use cat, but the problem with cat is that the problem that I have with with Python and digital logic is that I'm really, really used to treating the most significant bit as the left most bit and the least significant bit as the right most bit. But Python uses indexing, which means that index zero, which would be bit zero, would be on the left because it goes zero, one, two, three and so on. It's the same thing with ranges. If you do a range, it's going to be low comma high. Not only that, but when you have a range with low comma high, it doesn't actually include high. It includes only high minus one. So there's a bit of a disconnect, I feel, or maybe some confusion. So what I've done is I've created this function called lcat. And all lcat does is it takes its arguments, flips them, and passes it to cat. So now I can take the carry flag. So I want to lay out my output like this over here. So I can put the carry flag on the quote left and lcat stands for either left or logical or something. So I put the carry flag on what I consider to be the left, which is what I consider to be the most significant bit. And then the output goes next and then so on and so forth. So I think that's a really nice alternative to cat. And it just makes more sense from a digital design perspective to me, at least. Here's what cat lcat looks like. Basically, it just flips the arguments. That's all it does, right? And you can do that with Python pretty easily. You can do actually the same thing with ranges. So if we look at the formal verification code that I wrote, here's the formal verification for rotate left. So here again, I've put in my pattern. And this time, what I'm using is the down to notation that we're familiar with from the regular HDLs. So this is input from bit seven down to bit zero comma carry. So that would be the concatenation. And I want that to become the carry concatenated with the output bit seven down to zero. So what I do here is I say, okay, well, the carry, the output carry is going to be input sub seven. So I just do that. The output zero is just going to be the carry. So I do that here. The rest of it is basically the expected output. This is the output from seven down to one is equal to the input from six down to zero. So the reason that I didn't use lcat here is because again, this is formal verification, and you always want to use a different method to calculate your outputs. And this I found to be also convenient using this down to function. So what does down to look like? Well, it's basically just it's basically just this again, I reverse the arguments and I add one to the top most value so that I can use basic ranges. So again, there's nothing preventing you from building on the basic building blocks of nMyGen to suit your own need. There's a caveat, though, and that is you will probably end up basically designing your own language. And an API is basically a language. You have to know the functions and you have to know when they're called and what the arguments are. If you do that too much, you basically end up with a meta language on top of your programming language. And MyGen is basically a language on top of Python. Anyway, that's enough philosophizing. So I did the formal verification and it works just fine. So now there's also the test function TST. TST basically does the accumulator or whatever operand minus zero. So in fact, this does use subtract. And when you subtract zero from anything, the overflow flag and the carry flag are always zero. And the negative flag and the zero flag are just as they always are. So that was easily implemented just like all the other ones that operate off of subtract. So the next thing that I want to do is I want to see if there's any way that I can combine all of these instructions. Because if you look at them, like for example in the gate, you set source one to zero, source two to the accumulator. I'm only dealing with the A accumulator versions of the instruction at this point. You set the function to some function and then you store the result back into A. TST does the same thing except the operands are flipped. You still do a sub, but you don't store. Clear. The operands are both A. You do a sub and you store. Calm. The first operand doesn't matter. It may as well be zero. The second operand is A. The function you do is calm and you store the output. It's exactly the same thing for all of these functions. So just like we did with the ALU functions or the ALU instructions, it would be nice to have some sort of a common ALU instruction. I'm just going to call it ALU2. And this is what I came up with. Basically, you specify operand one and operand two. And if one of these operands is zero, it means that that source bus gets zero. Otherwise, it gets the operand you're actually going to compute on, which in this case is going to be self.a. So I would say, for example, if I wanted to do negate, so source bus one has to be zero. So I'd set operand one to zero. And source A2 has to be A. So I would just set operand two to one. So it has to be either a zero or a one. And I chose not to use a Boolean because I wanted to use a MUX in here. So in other words, it's doing this at the hardware level rather than at the Python level just for fun. You also pass it the function that you want to do and just like before, whether you want to store the result or not. And that's that. So if I go to, for example, let's say, COM, right? So source one is going to be zero. Source two is going to be A. And the function is going to be COM. And I want to store. So zero one. So this would be self. ALU2M, the ALU function, which is COM, ALU8, zero, one, and by default, you store. So now if we do formal verification, hopefully it should still work, which it does. So I'm just going to go ahead and modify all these other functions. Okay, and that didn't really take all that much time. It took about 40 seconds to do, mainly because I did six at a time. So and I was doing six formal verifications. And now we don't need any of this code. So that goes away. Okay, so the next thing that we have to deal with are the other modes. There's the B mode. And there's also the extended and the indexed mode. So we can see from the chart over here that we've got A, B indexed and extended. So again, A and B are going to be sort of bit aliases for immediate and direct. And indexed and extended are the same. So all we have to do is look at the mode and determine what the operand should be. Now it's going to be a little bit different because now we're also storing the result back, which isn't as simple as just storing it in a register. So for example, you can see that for something like rotate write, the A and B versions of the instruction take only two cycles because you're dealing with the register. But for index to take seven cycles, so you know, that's probably, you know, at least two, maybe, maybe three cycles to do the read and an equal number to do the write maybe, something like that. So let's go ahead and just implement all the other modes for these instructions. So here's what the new ALU2 function looks like with extended and indexed. So I basically stole a lot of the extended code from ALU and from store A because store A does a write and ALU extended does a read. And it is fairly straightforward. I added some comments here because I was getting very confused about what happened during which cycle. So anything combinatorial happened during that cycle and anything on phase one happens during the next cycle. So I just put some comments in there to make sure that I was doing exactly what I was supposed to do. And basically I followed along with and basically I followed along with the cycle by cycle table that is provided in the datasheet. So so here we are. Actually, I don't think I did this. I should check that. And that's another thing that I think that I will probably do after all the psych after all the instructions are done is I'm also going to add formal verification for the VMA line, the read and the read write line, because I just want to make sure that it's doing the right thing at the right time. Also, of course, the number of cycles that the instruction takes because the programmers of the 6800 would have written timing routines. And obviously, if you're going to put in an FPGA and obviously if you're going to put the instruction set into an FPGA, then the instructions should take the same amount of time given the same clock. So for the indexed instructions, it says that valid memory address should be zero during cycles two and three. Oh, okay. Well, that is in mode indexed. So all I have to do is go to mode indexed and make sure that I do that during cycles two and three. So it seems clear that I'm not doing that. Okay, so what I'm doing here is I am setting VMA to zero during cycle two and during cycle three. And if we look at the cycle by cycle sheet, we can see that during cycle, remember my cycles start from zero not one. So during cycles two and cycle three, VMA should always be zero. So now that should be correct. Again, I'm going to want to formally verify that the the signals are doing exactly what they're supposed to at the right time, but not now. Okay, so that means that we can turn these 44 instructions green. All right, things are looking up. So on the next video, I will probably end up tackling all of the other instructions with the exception of wait for interrupt and software interrupt, because that will have to deal with the interrupt line, which we haven't even implemented yet. So until the next video. But before we go, how many LUTs are we using now? 1462 LUTs with 1700 and 17 cells. Not to worry, hardware optimization will come. How's that, cat?