 Okay, let's get this done We need to add pseudo operations to allow us to define zero page and bss symbols. Now where is our parser? Pars expression Pars argument, Pars, here we go Now for ease of parsing all our pseudo operations are going to start with a dot So we read the token if it's a Dot we'll put this here Then this means it must be a pseudo op we will Read the next token which needs to be an ID and Then we are going to Do something based on what it is and we are going to do the simplest and cheapest Pars thing possible with store copies Pars So if this is a ZP We call this routine here and what this will do is will allocate Some zero page. We are going to Keep track of how much zero page we're using in this global variable So what this is going to take is a symbol We now want to create the symbol the symbol the symbol must not exist. We then need a comma We then need a Expression and the expression must resolve To a constant value therefore there must not be a token variable set And we're going to be using this in several places. So I will actually Common that out like so this will be how much zero page to allocate The first thing we need to do is To check for overflow see if we've run out So if the amount being allocated is Less if we do the addition and the result is smaller than the source Then that means that we have overflowed So we now have everything we need So we are going to set the The type to be symbol Computed the value is the current zero page usage Advance zero page and I believe we are done Let me just have a quick look down here. Oh That should be offset and it builds so let us define a symbol We're going to call it we can't don't want to call it a because that will get make like very confusing if It gets mixed up with the with one of these So we're going to say count One byte or that was going to fail. Why has it failed? Well, we've got a token This doesn't include the leading dot Pass buffer is zero terminator. They made sure of that So that should be Working so I think that there is something in the past buffer that is not printable Either that was either that or store copy does not in fact compare two strings Okay, that's right. That's better unexpected garbage at end of line I Did we print a one? interesting So from here that will break down to break out of the switch to here It's then expecting a end-of-line character, which there is It is a natural end of line Well, get rid of that it should fail to assemble. Yeah Interesting. Where is that one coming from? There are no printfs in the code There it is Right, that's actual debugging I put in It's received a one Which is an ID Try that Okay, it doesn't like blank lines. Okay, so let's make some padding and then we can do LDA count assemble What do we get a? 5 LDA? 0 0 Not the right value. I know what we did wrong This is not a computer symbol computers. This is a symbol ZP Being a symbol computed it will have generated raw bytes and not produced a expression node Which meant that it would have filled out the value with the raw offset Which should have been right actually There we go a 5 0 5 That is correct RTS So we should actually now be able to write real code What am I going to do a count label there's any terrible code but Compare with 10 Brunch if it's equal to 10 to exit I'll always increment and label RTS so that used 98 bytes of token memory Which is Approximately two-thirds of the size of the source file and Has produced what looks more or less like the right code, but we can Check that Right. Well, it's not right This is the wrong addressing mode No, it's not This is the right addressing mode. Yeah, this is getting very confused because it's seeing that the zero page and the actual code are Occupying the same addresses so That does actually seem to be more or less correct The disassembly is completely mangled Can I do something about that? I am not sure I can Yeah, I can set the start address for the code, but not for the zero page And if I change the start address which I will but I've done that here to something else Then It's also produced incorrect code Okay, so this jump instruction the jump label is jumped to address for which is no longer in this code but I do see that the beq seems to have generated garbage in Fact that has generated three bytes from the CPX to the INX and it's all wrong We have an FC 0f and a 0 0 so That will be somewhere in this If it should have noticed that it was relative and Gone through either we don't want to write code we want Place code it should have set the length either five or two a length of Three which is what we seem to have here is going to be totally wrong so let's See what's going on there? My suspicion is it's not actually passing through that code at all Yeah, it's not and That The wrong opcode is being generated in fact. I know the wrong opcode is being generated because of that FC and There is in fact no FC instruction Okay, so that will be Somewhere up here in Paz. We should have Well the one we wanted was beq Which is this instruction so has it managed to add? Has it got f0 and then added? 0c to it. Well, there's our f0 and there is a 12 which is the wrong number Right get B of AM is the code that converts our addressing mode The numeration to B value So rel should be here No, that's the wrong bit you want this one. I don't see rel on that Here is our addressing mode Okay, yeah So the addressing mode for the branch instructions is a label that is an absolute value the What I actually want for the encoding is a relative value so When doing this conversion, I do need to figure out whether this opcode wants a relative result and Convert So that should happen Yeah, if get Vincent props of the opcode is a Is relative then if it's not relative fetch the opcode is this a relative instruction? If it's not add on the B value Generate the code There you go And that beq is pointing at the right place at the RTS So if we have our first Working program and we have our zero page working the zero page is possibly the simplest thing you can possibly do Because we don't need forward references. We don't need We know the values statically during powers phase because it like starts zero and works up the When the binary is loaded then all the zero page offsets will be Adjusted for the base address we still need to generate the relocation tables correctly So why is that failed? So we read the token it should be an end-of-line token, but it's not it's skipped ahead So in our Lexa Where it was got to here see here if we see a new line turn it into an end line and this code is Actually reading the next byte what this does is it skips white space? And It deals with making comments go away We want our code to work in both Unix files and DOS files Unix files are terminated with carriage return line feed Unix files are terminated with carriage return PBC micro and old macOS files are terminated with Carriage return which is annoying So we are treating carriage returns back slash ours as white space ignoring them. So this should be working So this is what we have read dot Z P comma five end of file Each of these is going to be the first character of the word So the Z is the ZP the P is padding So end of line dot ZP count comma One end of line end of line again, and it hits the LDA and gets confused Okay, so it's nothing to do with the Lexa but I can tell from looking at this that Here it is here it is so We try to read a line we see a lone end of line character By doing break here. We consume the character Which means that that end of line character is considered to be a statement That's that one So we skip to the end and we try to read the end of line character that terminates that statement But we're actually in the next statement, which is LDA. That's why it's producing this error so what we actually want to do here is to either Push that back on to the power stack or Do a continue that actually restarts the loop. There you go. Okay. You've got zero page You've got zero page working. You've got text working now. We need bss bss is Referring to the section of memory that comes after the loaded part of the program so None of the symbols defined in the bss section Actually appear as labels we what we are going to do is define them the same way we did bss at the same way we did zero page and Say that we want say 10 bytes of bss reserved for a thing called table So the way we are going to do this is the same way as for zero page In fact, we could very nearly reuse the same piece of code here. I think we can okay So let's make this a UN 16 this will take a pointer to the Variable itself the usage variable and a maximum value Now we can't rely on overflow. So we're actually going to do so we do the computation if the new value is smaller than the old value or The new value Is greater than the maximum then we have run out of space You know what I don't think that's worth it Because this is going to have to be different too. So let's just back all that stuff out put that back to a human date But we can common out the common bit This is going to be comma number. Okay, so pause Dock said P is going to be the same as it was Come a number and All the bss bss usage, but if this overflows run out of bss This will actually this will very rarely happen if this overflows. It's because you've used 64k of bss Which is the total amount of memory on the system, which is ridiculously high So this then becomes a bss symbol bss usage Like that and then this gets added here Okay, and that works However, we are not done. Let me take a grid of Let me get rid of these And to find a byte of bss instead now We assemble this When we see LDA 0 SDA and a 16-bit address LDA and a 16-bit address and the rest of it is out to perform This 16-bit address is zero because when we I ran this statement the bss Pointer was at zero, but zero is not the value we want in the actual code so We are going to have a Third variable So after each call to place code we are going to update text usage with The program counter that we've calculated for a pass through here Then when we write the code, so if when we write the code we wish to if this is a bss address We write we add that value on so if variable type equals symbol bss text usage So now when we run it our symbol is at 0011 Which is this address here i.e. the byte after the RTS that is working excellent Okay, what Is Next I think in terms of features. Oh, yeah, we do want to do this and this Let's comment that one out for now. So back down here and let's find the parser code again Okay, now This then needs to read a comma separated list of values So we go loop We wish to read a Read a number. Well read an expression now Let me think if We have a variable Then this is a complex expression and we need to emit a Expression node for it, but we don't want an opcode Now we can I'm going to cheat like mad We're going to generate a normal expression node with an opcode of zero Zero is break here, which is a implicit argument opcode So should never be going through that code. So we're going to special case zero So we wish to add I've forgotten how ad expression record works Isn't the argument of this always record expert because I didn't never Got around to adding anymore and in fact we are going to have to do a little bit more than that Because we need to tell the expression record whether this is supposed to be a One byte or a two byte value So I'm going to use zero zero for one byte and ff for two bytes again Hockey as hell, but so you pass the expression you then want to look at the next token without consuming it if it's not a comma stop otherwise consume it and Go again Okay, let's fix This will make the code smaller. Okay, so this will actually work, but it'll produce the wrong thing. So here we have The right thing I was not expecting that Okay, that has worked because break being an implicit in imply implicit whatever opcode it is actually emitted the the The value we wanted as the opcode wait How has that worked? So it'll go through here. It was emitted the opcode then if there's a variable Yeah, it does that Then we write the address Length will be one. It should have written two things. Oh Oh, I'm stupid. Um, it has worked because My test program use constant values. So let's Create some zero page values like so add those on There we go These three values here. It's emitted the opcode which is zero and has not touched the The actual values so here we need to Override some of this Just wondering about the cleanest and simplest way to do this So we do want to override the length We know from here that These are the various different Conditions Yeah, okay so if opcode is zero Override the length to one Otherwise do this otherwise do this Then down here in write code This will fall through to here So then it will write the first bite of the payload and then this will decide to write the second bite of the payload If the length is three which it never will So here we have our three zero page values. Good So this is annoyingly complicated because remember we have to duplicate a lot of that logic down here under right Text relocations So back up here in PAHs. Let's put the words in PAHs.pss No, PAHs.byte as that is the same code Okay Now we go back to write code Sorry place code and we put in Another condition here and the length for this will always be two Down here under write code So this will write to the first bite of the variable and this will write the second bite of the variable Okay, so what's this gonna do? so one one one one one That ain't right. Have we actually correctly set the opcode correctly? R's.constant ox ff At expression record op this code is wrong We are in fact going to have to have separate routines for this and cut and paste it because this Because this is a 16 bit value one one one one two two two three three four four followed by 0 0 0 0 which is the address of zero page value one 0 1 0 0 0 2 0 0 That's correct 0 5 0 0 Which is the correct address of label here And 1 0 0 which I expect is the correct address of exit so that looks like it is working Okay, one more thing we need to do for this Which is string constants are special string constants are parsed by the lexer and Written into the parse buffer Complete with things like string escapes. So all we have to do is to directly emit the cons the contents of the parse buffer into the Into the output and we know that there cannot be any expressions in this therefore It's just a mit byte So it's literally that easy So now we can go down to here and we do for a zero terminated constant and There is our constant Hello world followed by a Exclamation mark followed by Not a zero That's a zero that should be a zero Is there a problem with zero in general? Yeah No, this is working zero Hang on Zero zero six oh that's RTS 0 5 0 6 0 7 0 8 0 9 5 6 7 8 9 that's very wrong So emit byte here should be getting the correct results. So let's just verify that We're not getting any tracing Parse expression here is setting Token variable, which it shouldn't be It's a number It should just bail out here Having cleared token variable, so let's do some stepping Right, we're here We call parse expression, which is here We set our things to zero we call read token Which appears to be inlined REToken isn't called lots of places. I'd be surprised if that was inlined, but I Don't know what it's doing Okay This is where it's calling read token. Here is where it's getting the result So put a break point there Go. I am looking at the wrong piece of code. I should be up here So two is a number So we got here so that just returns There you go. So that takes us down to parse dot byte parse expression, so now we're looking at token variable that does not look like Comparing to zero, but it has Done it and then it's trying to call add expression record So it's just read token variable from one e three eight, which we saw it Clear to zero here so Has read token Set it. It can't possibly have it should have gone through this piece of code. I Mean token variable should not be set by this at all Okay, well, that's a lot of them so So we've gone into parse expression with token variable set and It looks like we've come out of it again with token variable still set parse expression here has Has set token variable to null so We go in We see that token variable is 3cdd inside parse expression We do before read and we see that the value is null zero then we call read token and After the read we see it is back to 3cdd again. I do not understand what's going on here It's more of this stupid stuff read token Doesn't touch token variable Read token touches the parse buffer So we shouldn't be saving and restoring the variable here But I think that's going to cause things to break elsewhere So parse expression Should set token variable so can look ahead variable that looks more sensible and Here we have two zeros after the String and our numbers here are looking sensible Okay All right How are we doing this foot? Damage ouch ouch This is actually more to do we are most of the way there. There's only a few more bits to do which is the the relocation stuff so We have expression nodes expression records and we want to put the We want to write relocations so Let's get the length of the instruction here. This should be set correctly. So one two or three For a zero page byte This should be one for a address word. This should be two. We now want two this Needs relocating so if it's a absolute address or a zero page address And there is a variable set then we need a relocation record. So text address and In fact, this will also apply for bss addresses the same code Here we are calculating the address to fix up assuming it's a three byte instruction We don't know that anymore So let's go with The last byte And we ignore zero page so zero Actually, yes, we can just chop this out So the same code applies here Although it's easier because we have to relocate so the thing is up here it's straightforward because One of these can only appear in A absolute relocation. So in fact, we don't need to do that check that test We know that if there is a variable and the variable is a text or bss reference Then we have to do a 16-bit relocation down here if the variable is zero page Then we need to do zero page relocation, but a Zero page value may appear in both a abs or as Zp opcode so if Get in some props S opcode is Zero page do I want to do that? Well, I've just noticed that this is wrong We actually want this rather than being the length of the opcode. We have the length in the The record itself, which is going to be more accurate So down here If this is a absolute relocation Then the address is going to be if this is one of our special addresses a special opcodes The address is right here Otherwise If it's an absolute opcode Addresses a PC plus one In fact, we know that if it's not one of these the address will always be a PC plus one So that's straightforward and then we just do the relocation thing Okay, so Let's take a look at our program Here are our relocation records this chunk is for I can't remember which order they are in now text text goes first then zero page So we skip four bytes one two three four Wait one two three four this one that's correct. We skip three bytes one two three that's correct We skip another four one two three four That's a beq We don't want to fix those up and in fact now I think of it Beqs are going to be have to be fixed up specially so Figure out the type of opcode if this is a Relative and The length is five bytes We need a relocation. In fact, let's just do If the length is five bytes, we need a relocation. Otherwise, don't do anything otherwise Write the relocation as normal So if the length is five bytes, then we're generating a relative branch displacement jump high byte low byte High byte. So that's one two three four So we actually want to do In fact, the high byte is the last byte of the chunk so We only want to do nothing if this is a relative branch and The length is two so let's invert that That's the wrong end If it is not relative or The length is not to Then write the relocation. Why is it complaining about my? Braces because I need another Parenthesis there, okay, zero one two three four. Yep zero one Four Zero one two three four Zero one two three one two three four five six seven eight That looks correct That's the last byte before the RTS. So yes, that's the end of label one two three four five six seven eight nine a B C D E But we don't relocate this because it's an E One two three four five six seven eight nine ten B C D E likewise One two three four five Six Seven eight nine a B C That is The high byte of a label. That's correct. And the last one is a two one two Those all look like valid fix ups Okay, zero page E is here Eight one two three four five six seven eight That's correct That's this address, but the next nibble is a zero And the zero means don't advance so we relocate that again and again, and then We advance E. Yeah, it's not advanced over these so that is all bogus Check the zero page relocation Right PC plus one is the problem I think because we're currently at PC if the opcode is oh oh or the opcode is FF Relocate here Yeah, that shouldn't kind of never happen because we should always advance a bit That so let's change that to that which is like more correct Still not right Wait a minute Okay, sorry Yeah, I know what it is We have Shadowed Len up here, so let's make that length So Len is the length of the record Lenks is the length of the instruction But it still hasn't helped Yeah, it's oh oh E nine oh oh Get rid of that break. It's because it's never been advancing here Okay, that looks better. Right one one So we were fixing up our zero page. We were here eight put us here one one e one two three four five six seven eight nine a B C D a Why is it's that thing? Why is it fixing that up? That's that zero Should not be fixing that up. That's wrong. So it must have come through here with variable set So peak token here forces it to pass the string and fill the pass buffer Retoken then consumes it we do it in that order because if the read token is Consuming something that also uses the pass buffer i.e. nearly everything it will overwrite the pass buffer So we consume the string. Is it a comma which it is? Consume the comma Back to the top interesting Okay, that was a so one two That's wrong. We're here now. I Think I may have miscounted. Let me try that again Okay, let's go over So e One two three four five six seven eight one one e one two three four five six seven eight nine a B C D e Huh a One two three four five six seven eight nine a So yes, that's correct. That is the low byte of one two one two low byte of two Another two low byte of three and More than it's the end. We get an F So I don't know why it's trying to pass why it's trying to fix that up. So that's the next thing to look at Okay short break then let's take a look at what's going on here Okay, so we should Right so We have come out of string and we have read a zero Both token value and token variable So there are only after the string there are only two more Bites The rest are all words and both of them are zeros. So there should in fact be no relocation generated there So I am kind of confused So down here in You don't want to write code. We want to write text relocations. We only generate the relocation If there is a variable and the variable type is a text or bss Uh Sorry, this is zero page relocation. So I should be down here. So e eight one one e a Well, the one one is One one one one two three four five six seven eight nine a Bcd It's an e it doesn't do a relocation right then we go straight into a One two three four five six seven eight nine a Okay, that's the right address. Now. That was just idiocy Okay, we are in fact nearly finished I will just write a Few things here That will just show us how big our program is and I will actually put this up here So it happens before we do the writing And I'm also going to put in So it will now print dots after every Analysis pass just to give you an idea that something is happening So here you can see we've done two analysis parsers for this code Okay, there is one final thing remaining Apart from, you know, adding a full expression parser, which I haven't done yet Uh, and that is well first, let's okay. There are no more printfs And that is to actually generate the cpm 65 Header which is seven bytes long I've got got it documented in Here roughly So every place where we do pc equals zero, we actually want to start at start address And the header is very simple It just contains the Amount of memory being used Where the relocation table is et cetera So we wish to Amount of zero page being used Okay, this is actually more complicated than it looks The number of tpa memory pages required is the amount needed to Load and run the program And to load the program You have to have enough space for the relocation table This stuff down here And if I actually look at asm.com itself You go down to the end. So there's actually quite a lot of relocation table. That's all relocation table starting from about I believe it's this a4 maybe this e so We don't know how big that is at this point Which is annoying Um, I am just going to ignore that for the time being So what we want to do is take the text usage Round it up And turn that into the number of pages Of 256 bytes Okay, now we need the address of the relocation table Which we know is going to be immediately after the text. So this is going to be Text usage itself Uh low byte high byte We then have a jump instruction followed by two zeros Uh, this will contain the address This will be patched up when the program is loaded To contain the bedos entry point address And there's actually no reason why the header couldn't put something in there That would save another couple of bytes. You could for example put the text usage in there Uh, but then you wouldn't get access to text usage value in the branding program. So It's only two bytes. Okay. What have we got? Uh, oh three three bytes of zero page Oh one one byte one page of tpa 4100 is the address of the uh fixed up table, which is There that looks plausible For c 000 and then the code. I think that's worked. Okay. Let's write a program We'll get rid of all this program There is our program containing a whole one byte of instruction So if you consider the overhead we are up to uh that's 10 bytes Of program for one byte of opcode In other words 90 percent of that program is overhead Which is like Horribly wasteful So let's run it It works excellent. We are finished No, of course, we're not finished. Um, I actually completely forgot about one thing that we're going to need to do Which is to do anything useful at all We have to be able to refer to the high and low addresses of expressions And that is something that we're going to have to put in the expression record I don't want to eat another bite for this. Uh, I could use a bit field because we don't need eight bits for length But I have no idea how well llvm moss handles bit fields So let's just do uh post-processing none low byte high byte We don't need those I'm just trying to think how to do byte sized enums I can't remember. There is a way Maybe that only works in c++. Anyway, let's just use This Okay, so parse expression Before we do anything else we want to Um Check to see if there's a left or right angle bracket and then we need to put the result somewhere great, uh And in fact, this is going to be used In lots of places um And we're going to have to put it into a computer node two Okay, this is actually pretty ugly. Uh, I hadn't thought about this. It's a rather more complicated than I originally thought Okay, so In the expression record, it's fine. We can just set the value We can have our parse expression stick any Post-processing value into a global and then pick that up and stash it in the expression record For a symbol record For a symbol record, we're just going to forbid it for now so Token variable Token post-processing Okay jrc equals peak token If c is left angle bracket Token post-processing equals uh Least significant byte token else token post-press else if c equals greater than equals pp most significant byte Otherwise proceed as usual Okay Now if this is a number, this is a constant value We actually want to Apply the post-processing Immediately so What did I call that post-process constant? So if it's an id Then you'll end up with a post-processing value if it's a constant you won't So for an am-im you'll end up with no post-processing required add expression record Where are we using this? parse argument This as an indirect operation Uh, we are going to forbid Fact, I think there's no reason why we couldn't do it there Yeah, there's no reason why we can't do it there and it just makes life More complicated. I mean it doesn't make any sense, but This will be encoded as a Uh an expression record parse symbol comma number number is a const expression Uh parse dot byte Yep, that will work out Dot word Uh here we do want to forbid it Uh And that's the lot Okay, that's not going to work because uh, we have to put the actual, you know work in so Load the low byte of label Load the high byte of label. This is what we want it for And on the 6502 it's hard to do anything without it, which is why I'm obsessing over it Uh So this is Oh, I keep forgetting that's got the right. That's not actually assembled it There so here's the header. Here's where the program starts Um, it's noticed it's an immediate It's in fact given us the low byte So that's ldx Uh oc and ldy oc rts Okay, now this is going to be irritating So place code is unchanged It doesn't care about any of this stuff Right code Uh is Changed So Relative You're not going to post process on relative and I should put in some code to check that Uh If s post process constant equals ppmsb then Shift write the address If it's the least significant if you want the least significant byte Mask out the top two bytes Okay, that's straightforward Now we come to Writing the text relocations So this is the address of the text relocation If We are only returning the most significant byte Then this means that the whole address is shifted down by 16 bits Which means that we want to relocate the low byte not the high byte So we need to move the address up one for zp relocations Uh Post processing doesn't make any sense If you try to take the most significant byte of a Zero page value you will get Uh zero So we can actually check that up here in the place code This is adjusting the size I want to do it here. Let's do it down here actually Actually We do want to allow this Because you might be giving a zero page address to Code like this. So you do actually want this one to safely produce a zero So if we are So only emit the relocation If If the user didn't ask for a most significant byte think that will work Okay. Well, what do we have? Uh a to o c a zero zero zero That looks right Shame about the relocation. No, hang on the relocations are here eight one so eight Is correct No, wait a minute. That's not correct That is the least Significant byte that should not be being relocated So write text relocations Um, we want to go here So in fact, we don't want to return any We don't want to emit any relocations for least significant byte values So that just gives us a straight nine Which is still the wrong address Because you want the next one. Um, okay The reason for this is because we are looking at the last byte of the instruction But The instruction is in fact a two byte Thing And we weren't expecting to get a 16 bit relocation into a two byte instruction We wanted this because of Long relative long conditional branches So in fact If Only decrement the address if this is not an immediate addressing mode I do not like this code. This is getting complicated and full of edge conditions But we now have an a here. So that's eight nine a that is the right address Okay Let's write a real program Not a very big real program, but So a contains the low address of our message X contains the high address We are going to call system core number nine and We are going to call the address at start minus three which contains the bdos entry point Does this assemble it thinks it assembled What do we have here? Well, uh, lda message 11 is the right low address LDx zero ldy Zero Uh jsr Four that's the right address. That's the address of this 4c here And a six zero So that's not right. Let's get rid of the comment just in case that was upsetting things That's still wrong So this should not have touched the value of the constant Given that there was no post processing applied So if you look back token value Those are all zeros Let's sit that here Okay post process constant is in fact corrupting our Uh our values presumably Um, this is going to be a push and peek thing again. I do not like this stuff. Um, there must be a better way to do it So that will be something that I will want to look into in the future Okay, that's now I believe worked So we now have a test.com that should have the correct code in it And It doesn't token post processing is zero There we go. That's what's wrong Okay a zero a nine. I think that's worked. So It doesn't work a nine one one a two one one that says it's the wrong address So that will be the uh Wait a minute. Wait a minute So both of these load instructions will be getting a relocation So text relocation here will be trying to deal with them both So It's actually write code here that must be writing the wrong things So if the post processing was msb, we should shift it by right Shift uh, that should be okay So why did that not print anything? Because I didn't run the assembler. That's why Okay, here are our two relocations and they both say they both specify a post processing value of zero That is because this is going wrong up here Yeah, I'm getting tired. I should stop We only want to We only want to actually change the Change the value of token value if we can Okay And it's still wrong So if there's a variable then it's not a constant and we don't want to apply the post processing now We want to leave it until the uh Until we actually do the emission right we are seeing a post processing value of zero Which is sort of wrong So this should have left the Correct post processing value in and it's always a zero. So power's argument is Doing something wrong. Well power's expression here should be Returning a post processing value Which it's not So we go back up to power's expression So what we got was an id Let's just put that there That has not set either of these Okay. Well, these are there. So the Lexus seeing it. We did verify that this was working elsewhere So, uh So has read token cleared it Yes, of course read token has cleared it because read token has pushed the thing that Was previously popped from there So this is a hacky workaround in fact this This should be okay because this will fetch a new token Disney's properly fixing there you go a 9 11 a 2 o o. Okay Let's try this then All right, let's debug it LDA 11 LDx o o this is after loading So that's wrong That should not be o o that should be o 2 this emulator loads binaries at o 2 o o So look at the fix up table a 5 8 9 a That's actually the right place So why hasn't it Why hasn't cpm emu fixed that up because it should have it's the uh To fix up nibbles at a 5 so The first thing to be fixed up is here. We should get to added to that and 5 on from there 1 2 3 4 5 Um Because that's the jsr start that should be o 2 o 4 that address I suspect this is a bug in cpm emu which is out of scope for this video So I will take a break and take a look at that The answer is very simple. My documentation is wrong This is my own documentation. I wrote for my own operating system. The zero page comes first not the tpa So this is going to be Uh first one is for zero page second is for addresses. Okay, so I did get that right I just need to swap these two around To write those in the other order assemble Okay, that has loaded the right address o 2 1 1 That is the wrong instruction That's a bit instruction Not a jsr right because jsr is special And we've actually added the b value for absolute to it Now one way of doing it is to add a special addressing mode for jsr But the other way to do it Is to hack it And the b value for abs for abs is three So if we subtract Three shifted left by two from that then once the b value is added on that should be right jsr We get our holo world Okay, well, we now have a fully functional if extremely dubious. Let's get rid of that Assembler for cpm 65. How big is it? 8k I suspect that once I put the expression piles are in that will go up to nine But I think that is working Okay, let's try it on simulated real hardware boot cpm 65 Do we have a hello world here? Type hello dozen We do not and we now need to actually do a proper build. There we go That will regenerate the file system Okay So Let's turn hello.asm into hello.com And then it crashes fabulous, um Okay I think I can actually guess what happened Which is that we are falling off the bottom of the program here Except we've probably overwritten Um Some stuff the tpa needed So if you force a warm boot that will reinitialize it and you know should just be better Let's try that one more time And again, I keep forgetting to Test to see whether the disc noises are showing up on the video There you go, and we're back to the aprompt And now we have a hello.com Which looks like it's got the same code that we had before And it runs fabulous Well, the assembler is way bigger than I would like it to be but it is written in C. So that's pretty good C is notoriously hard to compile on the 6502 I'm sure there's cleanup that needs doing but I also suspect That that cleanup will be Counterbalanced by adding the expression parser Which we kind of need for a real assembler I need to investigate what actual code is being produced And persuade it to do better But it does work and even on this very limited uh machine I mean it's only got 25 odd k of available ram to cpm We still get 16 k of available space That means looking at how big the The internal state that this thing uses the the records it assembles in memory It seems to be pretty reliably about two-thirds the size of the source code So that's actually a reasonable size of program you could write non-trivial programs in that So I think that is a success This was supposed to be a prototype to see whether it was actually going to work at all, but I think it has So that's nice And I'm still not very happy with a lot of this code anyway As always this is going to be pushed to github and I'll make it part of the cpm 65 distribution Um, if anyone wants to try it, you're more than welcome And try programs in it just try not to You know use it with care because it's likely to break so anyway I think we're going to call that one the last video of the series. I think that's up to five now And uh, I won't do any more because I don't think there's won't be anything particularly interesting left to do So I'm assuming you're still watching I hope you enjoyed this video As always, please let me know what you think in the comments