Tip:
Highlight text to annotate it
X
Hello everyone, this is the online help session for lab 6 Morse Code. This one is different
because it's going to use the watchdog and the speaker, so this involves pulse width
modulation and figuring out how interrupts are supposed to work. You will definitely
want to do systematic decomposition, just like for the last lab, except this time it's
kind of hard to understand his code to start with, so I think that's what I'm going to
go over first. So this is the raw .asm file. The first thing that this does is set up your
clock speed and number of watchdog interrupts. So up here we have system equates, and then
referencing our morse_codes file because we're going to use that as a library for looking
up values. This line's very important; it's going to give you the length of time for each
base element of the code because different time intervals in morse code are defined by
different multiples of this time element. These (.bss) are your global variables; this
is how you define them and this is where you put yours, you may need to create one or two.
So this one (beep_cnt) is for the speaker, and this one (delay_cnt) is for a delay.
So this is where the program really gets started. He has the string "paris" put in, the passoff
string is "hello cs124 world". Here he's setting up the stack and the watchdog timer with those
.equ-s up here, so you can figure out exactly what values he's putting into there; and that'll
help you later with blinking your green light. This is initializing a port; it's actually
the speaker. This is initializing the global variables, and this is enabling interrupts
on the board. So just because you have the watchdog timer set up doesn't mean the board
is allowing interrupts to happen. This section has the code you'll need for outputs, so you
have your output of a dot, a dash, and a space in this section here; and all three of those
together just happen to be "a". So what this program does when you load it on your board
is it just plays "a" over and over and over and over. So, there is actually a lot of code
missing that he gives you so let's dig through here and find it... Here we go. So, this .ref
is actually already in here, so we don't need that; and then this is going to be the mainloop,
and I'll explain what all of this does. So we want to copy this and put it in our code...
we don't want this before our initialization, so let's just put it up at the top... Understanding
this is where a lot of people get stuck. This #message is this string here, so this means,
"move 'paris' into register four". But it's not ACTUALLY moving the ENTIRE string into
register 4, a string in assembly and C is an array of chars, so it's a large array just
made up of every individual letter in that string - every individual letter, symbol,
operand, every char. This is actually moving a pointer to the beginning of the message,
so it's actually a pointer to the location in memory that's storing this "p". The next
line, this mov.b @r4+, r5 - this means the thing inside r4 is a pointer. it's going to
go to memory, get what that's pointing to - in this case, "p" - take that "p" and put
it in r5; and then increment the pointer that is in r4. Now r5 holds "p", and r4 is pointing
at "a". So if you didn't understand that, watch it again; if you still on't understand
it, talk to a TA, it's important that you get this. So now we have some arbitrary char
sitting in r5; we need to see if it's a letter, a number, or a space - because if we open
the morse_codes.asm - *the internet promptly dies*. So if you open the morse_codes.asm,
you have one table for numbers, and one table for letters. They're not all in the same table,
so you have to know which table you want to reference to see what combination of dot,
dash, and end you should use. In the code here, he references the letter table for you,
so you can see how that works. You can figure out if its a letter, a number, or a space
by going to here - this is called an ascii table, you should have talked about it in
class, if you haven't yet you will very soon. Each char, shown here in red, has a corresponding
number that goes with it. So the char is actually this number here, and you can compare that
to an int to see if they're equal. We know we're only going to have letters, numbers,
and spaces. Space has a value of 32, and our numbers have a value of 48 to 57, and our
letters - if we're smart and just use all caps - have values of 65 to 90. So, what we're
supposed to put here is figuring out if it's a letter, a space, or a number. Comparing
to see if it's a space is easy, you just compare 32 to what's in register 5, and if they're
equal jump to space. However, we don't really want to compare to every number, and certainly
not to every letter, to see if it's a number or a letter. So if you look at your hack sheet
you can see there are a bunch of different types of jumps, so for those you'll want to
do a comparison and then a jump if greater than (jge) or jump if lower than (jl). So
I'll let you figure out how to systematically decompose that. These three lines are actually
what are necessary in order to look it up in the table. So up here, we'd have our comparisons
- which are formatted horribly.... And you'll need one case to handle letters, one case
to handle numbers, and one case to handle spaces. What this line does is it figures
out how far from "a" your letter is, so that it can look it up in the table in morse_codes.asm.
if you get a 3, it's going to go to the zero, one two, third thing in the table and you'll
get the sequence for "d". This is just creating the index by which it can look up the letter
in morse_codes.asm. So this line accounts for the difference between byte and a word,
because this index that we've made is a byte, and we need a word - so if you add two bytes
together you get a word, and the way Roper has it set up just adding the register to
itself will work. And then this moves the pointer to that letter into register 5. So
it goes into the table, it looks up the letter, and it puts the pointer to the first dot or
dash into r5. Then, it should go to loop two - binary two - and it doesn't matter if you're
going from the numbers or the letters loop, then can both use loop10 and it won't matter
- so the first thing it does, we have this auto-increment again, so what' it's doing
is it's grabbing the first thing in our array, as you can see here, in this case a dash if
it were a b we were doing. It puts the dash into r6 to be executed, and then it increments
this pointer so now that pointer's pointing at this dot. Then, you're going to have to
compare it to dot, dash, or end, and it will jump back to loop10 until it hits end and
then it will go up and get the next letter. If you turn these into subroutines - which
it actually says to do in the lab instructions - um, if you turn these into subroutines you
can just compare it and then call. Each of these can be turned into a subroutine, so
you'd have doDot, doDash, doSpace, and then you can compare it, and just like we did with
the stoplight lab, you can jump if it's not equal over the line that says call doDot.
So you would jump to dash, and this would be dash. So if it IS a dot, it will not take
this jump and it will call the subroutine, and then you'd probably want a line that says
to jump back up and get the next letter, so jump to loop10. And then you'll have the same
thing for dash and end... So that's basically how to get started; do your systematic decomposition
for that. Now let's talk about the watchdog timer. you can think of the watchdog timer
as a way of doing parallel processing - you can do more than that, but it's a good way
of explaining it. So basically what's going to happen is your main program is going to
be executing, and then every few milliseconds - it depends on what kind of initialization
you have up here, how often it will interrupt - it's gonna interrupt, and it will execute
all of the code int here, and then as soon as it hits this return from interrupt (reti)
it'll go right back to where it was in your main program and keep going. So you could
have two separate programs, if you will, and it would basically just switch between the
two very quickly. That's kind of how operating systems work - you have all these apps open
down here, it's just switching between them so quickly that you don't really notice that
they're not all being run simultaneously. that's what an operating system does, it allocates
time and memory to different applications. So what's in the watchdog right now is the code
for your speaker and for your delay. So when you call delay, it really just loads... there
was a call delay... where's delay?... Ah! So when you call delay, it really just loads
a value into this delay_cnt global variable and then that delay_cnt is decremented in
the watchdog, and then it'll return when that's done. So instead of decrementing delay count,
here it's just testing delay count. Is it zero yet? And then it's actually being decremented
in the watchdog timer. So this is not CONSTANTLY decrementing. it's being decremented every
few milliseconds when the watchdog timer interrupts. You can have lots of different processes going
in your watchdog time, because this code executes the speaker, this bunch of code decrements
that delay counter. You're also going to have another section for toggling the green light...
so each part of the code tests "am I doing anything?" am I worried about this clump right
now? So, this one, "Is it currently beeping?" If it's not beeping, skip this part and just
go to the next thing the watchdog's supposed to handle. "Are we currently delaying?" no,
skip this part and go to the next part the watchdog needs to handle. And that should
actually say WDT_11 now. And then if it is active, it's gonna do this stuff that's handling
it. So for toggling the light, it's always active because it's always going to be toggling
the light, so basically you'll be decrementing the counter and then when that counter hits
zero you'll toggle the light. Okay, I think that;s it, so best of luck to all of you,
and don't let the speakers drive you crazy.