 Hey guys, welcome back skidstone series I thought it was about time to do another lab video today's topic is going to be a countdown timer a very simple project But one that isn't more about Synthesizing different things we've done in the past. So we have to grab user input number of seconds to countdown We have to be able to use our timing functions some basic floating point math and obviously printing out the timer as The time goes by so printing floats as well So again a very simple project, but one that is perhaps useful and also helps cover a wide breadth of our previous topics So one other thing about this is that we can turn this into a utility like we turned our other programs earlier in this series Into utilities that we can call from anywhere on our Machine and so this one the way I envision it is you type countdown or whatever you want to call it timer or something number of seconds and it will begin immediately to count down that number of seconds and print out the Number here with decimal point and a fraction so a floating point number and then once they hit zero it will say times up and then return back to a Usable command prompt so that's how I see this working And so it's not particularly challenging to do this and it only involves a couple of functions here that we've made in previous videos, so For timing we have tick time and talk time those basically you can see here what they do Tick time returns the current timestamp in rex in Microseconds, but it also saves that value somewhere in memory that you can access later And that's what talk time does it basically checks the current timestamp and then subtracts off Tick times timestamp and gives you the number of elapsed Microseconds since tick so it's a very MATLAB esk implementation of tick and talk so you call tick You wait a while you call talk and it tells you how many Microseconds have elapsed from tick to talk Then we have parse int obviously when we pass in this String here countdown space 60. We have to break that into pieces which actually The OS does for us and then we have to still be able to parse this 60 Which is just to ask the characters into an actual decimal number. So from a string to a number so that's what parse int does it eat to pass in a pointer to that character array and It checks it it can check for hex numbers binary numbers whatever else and it just returns the integer in rex then obviously to print we have our print float function from the previous episode we use that all the time and then our exit Function that just literally exits the program you need that to not return an error code when you end your program So not very hard at all We do have one thing to maybe talk about and that is just a very simple algorithm and math here So the user will pass in the number of seconds They want to count down from as a string right or really we're gonna get it as an int when we parse it That has to be a float for us to do some math on it to compute subtraction and print it out So that has to happen that conversion. Thank you have instruction in our assembly language that converts from ints to floats We'll talk about that today and then also in our loop. We're printing out the number of seconds left in the timer Obviously talk time returns number of microseconds have elapsed as an integer that has to also be a float So we'll make the intro vote in the same way and then also we have to print out the number of seconds that have That we have remaining and so that's just our input number of seconds minus This microsecond value divided by one million and that's what we print out. So not a very advanced arithmetic here, but again, that's all we have to do Let's begin should be pretty quick implementation here so the idea behind this whole series is that you can You don't have to do all this Stuff that most people suggest you have to do for assembly language programming You don't have to do inline assembly. You don't have to do things with the C runtime or a linking stuff Everything can be done from scratch and that's what this entire skits on project is all about and So when you get the code download it you'll have what you see here And if you run this make bins shell scripts, it will populate a bunch of binaries in that bin directory that we can use Just in general, but also for this software itself And the idea is that what we're writing today will eventually put in that bin directory to be able to countdown So let's let's do that right now. Let's go to this lab folder and Let's copy the template you can see the template there That's just a bare bones assembly file that just returns zero so we can copy that and let's make a new folder. We'll call it Countdown if I go in there. Well, what do we have in our template? Basically we have a Run script all that does is uses nasm to make a binary from our code So it literally just transcribes our assembly code into a binary directly. There's nothing fancy going on here And then we just the next line there is we make it executable of that binary That's just turning it turning the the bits in its permissions to say. Yeah, you can execute this Then we run it. So it's a very simple straightforward process. There's nothing hard going on here And then in the code. What is the code? So the code is the bare bones Set up basic elf header basic program pattern a single include which is basically the exit sys exit function which uses the exit sys call and then we execute that here. So if we run this That will return zero so we check the last return value and it says zero so this is a bare bones program So from here we can start implementing our timer What do we do first we have to include all those functions that we just mentioned. Let me pull those in really quick So we need to include First off the printing function. So that's in lib IO print float dot asm that basically goes to that File and copies and paste the entire contents here With some caveats. There's some macros there some ifs. So it won't include this multiple times But it will include it at least once Then we have to include our Parse int that's in the same folder. Let's just grab that one live IO parse int Then we have our tick and talk time. So let's grab those so in include live Time I believe tick time Then talk time And that's probably it Right nothing else Okay, so first things first is we have to be able to get user input. That's the most fundamental thing here Pass the number of seconds to count down from so how do we do that? Well, let me pull up one thing really quick I'll open up the live sys Linux sys calls really quick so you can show you what's going on. So oops We have a bunch of OS specific things to find here. So Linux and for BSD What I'm looking for here is basically this line here where we say define so we're defining a basically a Value for the NASM assembler kind of like a macro This sys argc start pointer. So basically this points this Value whatever this number it is this gets RSP but this points to where is argc when the program starts and so when a program starts you have argc which is a number of arguments or a number of space separated things that you've hit enter on on the command line So in this case if you type in countdown seven that will be two Arguments that you've passed in one is countdown and one is the string seven and then after that you have the argv which is the actual pointers to the null terminated character arrays for the string countdown and the string seven so Basically at RSP plus zero you'll have argc at RSP plus eight you'll have The name of the function and then RSP plus sixteen you'll have the string for how many seconds to countdown so first things first let's just make sure we have a Valid input how about that or a valid Situation and so let's check it really quick So if we if we go to that location location and we say at sys argc start pointer and We compare that with one the bite one just to see if we have at least one input So if we have more than one we're good if we have one that's not enough because one would just be the program name So let's say compare. I think if I say quad work is eight bites And then you can say jump less than equal to quit. So basically this says if We've passed in zero or somehow negative number of arguments Or one that's not enough for us to get a number of seconds from in that case just end the program however, if we've Passed in a valid Number so two or more in which case we don't care about everything else We only care about the second argument, which is the number of seconds that would be We can grab that and run our parse int. So if we go back parse int that grabs a character array at pointed to by RDI and Returns the integer value of that string basically in our ex and so we can put in RDI That's pointer. So we can say move into RDI sys R2C start pointer Plus 16 again plus eight would be the program name plus 16 is the actual number of seconds And then we can call parse int and Now in rex should be the number of Seconds in an integer format that the user wants to count down from and we can check that we can move into RDI or move yeah into RDI rex and call exit. So this is just a quick check. We can see When we run this program if we pass in Nothing we should return zero Because we will have evaluated less than or equal to one and that will jump to quit If we didn't pass in one or less arguments, we will parse the number in the second argument And return that value to the output and of course, this is a maximum of like a 255 But we're gonna pass in a small number just to check. So let's do this if I run this It can it compiles just fine. And if I My recording we just check I am So we have a binary now if I run that binary with no inputs I should get an output of zero which I do If I run that binary with some input, let's say 55 I should get an output of 55. So that is big We've now just taken a string input Turned it into an int and Returned it out also having checked for the proper number of inputs Well, maybe not proper because I can still pass in like a bunch of inputs, right? I guess 55 cow Monkey banana and it would still I believe just pass out 55 So we ignore everything beyond the second argument, which is fine. Who cares? Back to the match. So what's next? We have the number of seconds to count down as an integer in Rax the problem with that is Basically, whenever you do that you all our functions Well system 5 a bi says that you return values in Rax And so I don't want to keep values are important in Rax if at all possible. So let's move into RCX You don't have to do this But let's just move into RCX the value in Rax That's just to save the number of seconds to count down Is this important probably not and we can ignore this should we know this? Yeah, screw it. Let's ignore this who cares instead of that let's Convert that immediately into a float because as you said before it has to be a float for our math to make sense So we're going to convert this Into a float So how do we do that move into? Well, it's actually converts a scalar integer to a scalar double XMM one Rax so this Says XMM one contains float for seconds her total seconds, I don't know Whatever, you know what I mean also In that in that math, we'll also need one other thing and that is let me pull it up really quick This number here 1 e 6 you do know though so we can store 1 e 6 in a very inner register That's fine and keep dividing by it. That's no big deal, but What's better than that is instead of dividing by number we can multiply by The inverse that's just a way faster to do so let's sort of inverse of this number and multiply by that each iteration in the loop So let's grab that in XMM to so let's say move SD XMM to something let's call it millionth and This will basically XMM to Contains 1 e negative 6 So let's do that. Let's put that value here somewhere millions and I'm pretty sure you can do just this fraction here is that it Five zeros and a one. I think so Okay, cool But also one other thing we should do is negative numbers don't make sense. You can't count down negative from negative five so let's Let's check if it's positive first. So let's compare rex with Zero what should we do if it's less than zero jump to quit or equal to zero I guess jump to quit Otherwise do this conversion and start our loop. So let's make a loop here. That will be our actual timing loop going on Now let's think what should we do first actually before loop happens We have to you call tick right tick is so talk returns number of seconds for microseconds since tick So you have to call tick first. Let's call tick time right here call tick time and At this point rex has been obliterated because this returns the number of microseconds or whatever the time stamp in rex So rex is now gone. We have no recollection of our integer number of seconds to count down It's now gone But who cares we have that value saved as a float in XMM one Okay, great. So the time stamp has been saved in tick now. We can eventually call talk in our loop so let's do that call talk time and Why don't we just see if this works? Well, we can't really do it yet Let's convert this to a float first, and then we'll call print float Does see if this works? Okay So this returns number of microseconds in Rax so let's say that here Rax contains number of Microseconds elapsed So let's convert this into a float like we talked about so converts scalar into scaled double XMM 3 again, I don't want to put anything XM zero because XM zero is an input to print float And so I want to keep that clean. Otherwise. I have to constantly push and pop these 16 byte floating point registers off on the stack. So yeah, we're not going to use XM zero even XM three So let's pass in The value in RX. So now in XM three will be in is a float number of megroseconds that have elapsed So it's a huge number and then we can Let's just print it out. How about that? Let's print it out and let's loop forever. So let's jump to loop And then to print out stuff. We have to be able to pass in inputs. So XMM zero is the input. So move SD XMM zero XMM three this will put the number in XM zero just for the time being Move RDI. We have a standard out File shifter we can put that in here and then move RSI the number of digits to print. So we'll say Sound of seven four six four course short, right? So let's do four And then call print float sure and then it's buffer printing. So we want to print this every single time. So we'll say call Print buffer flush But also this will print them all on one line. We could put a new line character down here Let's do that so we can get it separated by each line. So let's say grammar DB new line character And we'll put that down here. We'll say move RSI Grammar move rdx one And call print characters. This is actually included by print float. So I don't have to include it separately here It's a dependency of print float. So that's that's fine And this should print out a new line in between every float that we print. Let's see if this works So let's do binary 15 So it's printing as a float which is not really showing right here But it's pretty as a float the four digit most significant four digits of that float count it upwards So this is what six million. So this is six seconds to have elapsed since I hit enter there Let's try that again One two three a one second two seconds three seconds four seconds five seconds So you can see it's counting up as we expect Cool. So everything's working as intended so far We're not able to count up in microseconds from our tick Using talk in a loop We didn't subtract off from our number of seconds to count down from and we're still not counting In seconds we're counting microseconds. So this involves that math we talked about before This year, so let's implement this simple arithmetic right now So we'll multiply our number first by the one millionth So we'll turn that into a second number not microseconds. So more as the x and m3 x and m2 um, and then we can subtract off from our Original float for a number of seconds to count down from so let's do that. Let's move into x and m zero that original number and x and m one and we'll subtract off the Second slaps so far in x and m three And this now should basically be printing for us the countdown time So let's do that. Let's execute this and see what happens compile and run You can see now it's counting down from 15 But that's a pretty big step And you can see it has the four digits of accuracy that we specified with rsi four When it hits zero though, you'll see that it doesn't stop. It keeps going negative. We have to implement some kind of Comparison to stop when it hits zero How do we do that? well Down here There's probably other ways to do this, but I like to just set one of those values to Zero so px or x and m3 that basically makes x and m3 equal to zero as a floating point number um, and then we can compare our current countdown with that so I can say compare scalar double x and m zero x and m three and then jump Above loop So while our number is above zero Go back to the top of the loop Let's try that now and see if that works. So it's going to be compile and let's run this This one over this time five So four three two one zero And then it it stopped basically when it hits zero So that's that's good. That's the first step I guess you probably could could the person at the top but doesn't make too much of a difference Because what I want to do first is I want to Not print out a new line every single time. So if I change that to a character turn Is that enough to do that for us? So right here, you can see we're counting down and we did not put a new line in between each single one We've just overwritten itself and you can see where when it ends it ends So that that as it as it is might be enough for you You might be done with the program right as it is. I want to add a little bit more though I want to add when it ends a little like done or Times up or maybe a beep. You can do any of those things. Let's put the word times up when it's done. And so How should we do this? I think I'm going to put it in the same chunk of memory here as the grammar. So let's say time times up Eximation point new line character and then the leave that there and so At the end of this I want to print out that so right here. I will um move rsi grammar Move into rdx how many how many characters so we have one two three four five six seven eight nine 10 11 12 13 Move rdx 13 Call print characters that will print that out and I have to also call the buffer flush. Let's do that and um That does do one thing though that messes up this print out here And so we still want to print this um character turnout. That's actually going to be offset now It's going to be offset by how much um One two so wait zero one two three four five six seven eight nine ten 11 12 13 okay and the space is important because whenever you lose a digit on your Big end of your flowing point number you have to put it on the back end and so let's add a space. So let's say At grammar plus 13 print out two characters like that And yeah, that should that should be good Let's see what happens when you run this I'm pile run five four three two one zero times up So that's pretty much it we've been everything. I can't think of anything else besides adding some documentation, but who cares about that? um So yeah, we've basically enabled ourselves to Count down from any user input number of seconds Using tick and talk to count the time that's elapsed printing out Doing some flowing point math printing out the number of seconds. Let's have we have left And then looping until it's zero So it's pretty simple, uh set up here But nonetheless, perhaps very functional now what you would do Which I've already done by the way spoiler alert um In this make bins Shell script you can add a line that basically Whenever you run make bins it generates this countdown timer executable Makes it executable and puts it in that bin directory and then you can add that to your system path So let me show you how you do that. Um In your bash rc for example You can put that bin directory or any bin directory you want to put it in On your path like this And uh in that way whenever you want from anywhere on your machine so I can go a different directory that you see here I can run countdown Uh 20 and it will count down 20 seconds from anywhere on my computer. So that's pretty cool Um, I guess we're done. Thanks for watching. I'll see you in the next video