@Peter, when I was learning to program my first Spin project most of what you said was way above my head. It still is.
It still looked like addition so I had to do some digging. I think if there is a '1' in either the source or the destination the result = '1' "or else" = '0'.
The simplest thing is to have a look at the truth table of boolean operations (which only is having a look at single bit sized numbers) and take it as it is:
For the OR it is
Input A 0 1 0 1
Input B 0 0 1 1
Output 0 1 1 1
How to read that?:
(You have to read it in columns)
1st column: If input A is 0 and input B is 0 then the output is 0
2nd column: If input A is 1 and input B is 0 then the output is 1
...
The OR is a little bit confusing when comparing it with an OR used in speech because there the common sense is to use it with implicit meaning "either or". But either or is called exclusive or in boolean algebra:
XOR
Input A 0 1 0 1
Input B 0 0 1 1
Output 0 1 1 0
If you do these operations on longs you have to use this truth-table for each pair of bits in source and destination, as Peter did it in his post!
I think to make the example given in the concise truth table more complete there should also be an example that shows the result for the case of 2 corresponding bits being 1!
$05 or $03 for example.
The or behaving same way as an addition is only true in the special case where all 1 bits in the source and destination are located at different bit-positions. For example:
@MagIO2, I actually thought I was making good progress after studying the bit fields of a general purpose register in a cog. There is quite a bit I have to learn! My new list of concepts to research includes OR, XOR, truth tables,boolean algebra, bitwise and conversions between hex, decimal and binary.
@MagIO2, I actually thought I was making good progress after studying the bit fields of a general purpose register in a cog. There is quite a bit I have to learn! My new list of concepts to research includes OR, XOR, truth tables,boolean algebra, bitwise and conversions between hex, decimal and binary.
If you are familiar with circuits then ORing and ANDing are like this:
BATTERY+
[SWITCH A]
[SWITCH B]
LAMP----BATTERY-
If [SWITCH A] AND [SWITCH B] are active (closed) then the LAMP will light.
BATTERY+ -|----[SWITCH A]---|
LAMP----BATTERY-
..........|----[SWITCH B]---|
If [SWITCH A] OR [SWITCH B] are active (closed) then the LAMP will light.
A truth table is just a way of representing all possible input conditions and their outputs, for the OR you would start off with both switches off even though you already know what's not going to happen!
We use 0 to indicate inactive or off and then we use 1 to indicate active or on.
B A LAMP
0 0 0
So we flip switch A on and observe the result and record it in the next truth table entry
B A LAMP
0 1 1
Then we flip switch A off and B on.
B A LAMP
1 0 1
Then finally both of them are on.
B A LAMP
1 1 1
So we put it all together and we get.
B A LAMP
0 0 0
0 1 1
1 0 1
1 1 1
Now take a look at each row as if it were a single column of 32 columns (lamps) and it's no different from a processor's OR operation, it does all 32 as individual "bitwise" operations.
OR, XOR, truth tables,boolean algebra, bitwise and conversions between hex, decimal and binary.
That is quite a list!
I like to simplify things.
OR - if A or B is high then the output is high. So the only time the output is low is when both A and B are low.
AND - if A and B are high then the output is high. In all other cases the output is low.
NOR - put an inverter on an OR gate
NAND - put an inverter on an AND gate.
XOR - I can never remember. If the inputs are different then the output is one value and if they are the same it is the other value. I look up XOR if I need to use it.
Hex, decimal and binary. Hex is just 0-9 then A=10, B=11 and F=15 and then start at zero again.
Hex to binary to decimal - check out windows calculator (in accessories I think) and there is an option to turn it into a scientific calculator and it can do the conversions. I use that all the time - much easier than trying to memorise things.
Better still, use Forth interactively as that fancy calculator. Here's a copy&paste from the terminal hooked up to a Prop running Forth and I've made the stuff I've actually typed in myself in bold red :
BINARY ok
ok 1001 ok 1100ORCR.
1101 ok
ok 1001 ok 1100ANDCR.
1000 ok
ok HEX ok 90CAND. 8 ok BINARY ok
1001 1100 AND ok
HEX . 8 ok
Well if you can get your head around entering numbers first as if you are putting them down in front of you like cards in a stack and then deciding what to do with them after you've got them there:
First instruction BINARY which just tells Forth to accept numbers we enter as binary by default and print them out as such
First card 1001 which is 9 in decimal (8+0+0+1)
Second card 1100 (8+4+0+0)which is 12 in decimal or 0C in hex (just substitute familiar symbols A..F for 10..15)
Next instruction OR which "grabs" the first two cards off the stack and just plain ORs them and puts it's result as a card back on top of the stack.
nothing doing yet but we tell Forth to give us a new line with CR (carriage return) to line up the result and immediately after....
grab the card off the top of the stack and print out the number (the command is the dot symbol)
Forth just does what you tell it.
Next couple of examples you can probably figure likewise.
Oh heck, forum code is eating up the listing, here it is from the screen:
@Dr_Acula, researching this stuff is absolutely fun. I guess that makes me a nerd. I understand some of the concepts in varying degrees but PASM requires more precise knowledge. I'm very comfortable with Spin and I need a new challenge.
I understand some of the concepts in varying degrees but PASM requires more precise knowledge.
ah - well if you are learning pasm there are a few things to make it easier. Write all the variables as binary #% and then you can see all the pins. Much easier than hex or decimal.
To set a pin high, do an OR with that particular pin set to 1 and all the rest set to 0.
To set a pin low, do an AND with that particular pin set to 0 and all the rest set to 1.
There is a third instruction, ANDN - I get confused about the exact logic, but what it means is that if you have your #%00000000_00001000_00000000_000000 variable that you used with an OR to set a pin high, you can reuse the same variable to set that pin low by using ANDN. It means you can have a lot less variables defined and that saves memory. So rather than use OR then AND to toggle pins, you tend to use OR and ANDN.
Translating spin into pasm can be fun. It is good brain exercise but it is very rewarding to see the same code run 20x faster or more.
I like to start simple and translate only a few lines at a time from spin to pasm. Have you got some spin code? Maybe we could have a go translating?
"ANDN - I get confused about the exact logic" - ups ... really?
ANDN simply means AND NOT, where NOT is the bitwise inversion of the source. In your example it means that
%00000000_00001000_00000000_000000
is first inverted to
%11111111_11110111_11111111_11111111
and then ANDed with the destination.
BTW: #%00000000_00001000_00000000_000000 looks like a immediate value (because of the #), which is not allowed to be longer than 9 bits.
PS: Your last byte has lost 2 bits ;o) And I wondered why the number with 31 times 1 is longer than your number with lots of 0.
@Dr_Acula, I will be prepared to try translating some basic Spin in a few weeks. At the moment there are too many concepts that are completely new to me. I've studied tutorials written by JonnyMac, DeSilva, Potatohead, Graham Stabler... I still get stuck. I recently posted a question about the 'physical' location of the compiler which showed I was clueless.
At this point I can't even grasp asm code that blinks an LED.
ANDN is another poor choice of terminology. NAND or NOT AND means the bits are anded together so you only get a 1 if both bits are 1, and the result is NOTed or inverted so two 1's result in a 0 out.
With ANDN the two bits are inverted first and the result input to the AND function. The end result is a 1 only if both inputs are 0.
First of all ANDN makes perfect sense to me! Let's say you want to use this operation on a register called source and a register called destination:
destination AND NOT source
perfectly describes what happens!
As NOT is an unary operation it only works on source and inverts all the bits of source
IT DOES NOT INVERT destination!
destination and the inverted source are then ANDed.
In other words: The operation ANDN deletes all bits set in source and keeps the content of destination for all bits not set in source.
@Dr_Acula, I will be prepared to try translating some basic Spin in a few weeks. At the moment there are too many concepts that are completely new to me. I've studied tutorials written by JonnyMac, DeSilva, Potatohead, Graham Stabler... I still get stuck. I recently posted a question about the 'physical' location of the compiler which showed I was clueless.
At this point I can't even grasp asm code that blinks an LED.
If you really want to understand assembly, I would recommend understanding the underlying hardware. This book is college level, but very still a very good and approachable introduction to hardware:
All of which are important to programming in assembly for any processor.
Side note: many will argue (complain!) that you don't actually need to learn the hardware to program in assembly. Sure. But then you'll be the guy working on the assembly line, putting things together according to direction. If you learn the hardware, you'll be designing the system.
In any case, with computer hardware you don't really need much of a background in anything except a comfortable understanding of basic math.
Re ANDN, it is a slightly unusual instruction. Not a "standard" logic function but it makes sense when you only have 496 longs to use in a cog. I'm glad it has been added to the pasm opcode set.
BTW: #%00000000_00001000_00000000_000000 looks like a immediate value (because of the #), which is not allowed to be longer than 9 bits.
Good point. Remember, in pasm this is not a 32 bit micro. It is a 9 bit micro. The entire instruction fits in 32 bits and by the time you add the source, the destination and the instruction there are not many bits left for a constant value. So for constant 32 bit values you have to declare them at the end of the program. It is a slightly strange setup, but it makes sense if you start with the premise that each instruction plus data has to fit in 32 bits. So the number 512 is special. Less than 512, you can declare it in an instruction. Equal or more than 512 and you have to declare it as a constant. Those constant declarations take space, and hence you have to be smart how you declare them. Hence the ANDN instruction as it decreases the number of declarations.
Re learning pasm, I'm not sure there is the 'perfect' tutorial out there on the interweb. I like to pull apart code from others, particularly code written by experts
Please post a bit of spin code. I'm sure the forum would love to have a go showing you how to translate to pasm.
Good point. Remember, in pasm this is not a 32 bit micro. It is a 9 bit micro. The entire instruction fits in 32 bits and by the time you add the source, the destination and the instruction there are not many bits left for a constant value. So for constant 32 bit values you have to declare them at the end of the program. It is a slightly strange setup, but it makes sense if you start with the premise that each instruction plus data has to fit in 32 bits. So the number 512 is special. Less than 512, you can declare it in an instruction. Equal or more than 512 and you have to declare it as a constant. Those constant declarations take space, and hence you have to be smart how you declare them. Hence the ANDN instruction as it decreases the number of declarations.
Re learning pasm, I'm not sure there is the 'perfect' tutorial out there on the interweb. I like to pull apart code from others, particularly code written by experts
Please post a bit of spin code. I'm sure the forum would love to have a go showing you how to translate to pasm
Where are constants located?
I see that the 9-bit limit = 511. This limit does not exist in main RAM. Is there an example showing how ANDN would decrease a constant declaration?
One of the things I got stuck on is from JonnyMac's "led_ctrl.spin:
dat
org 0
entry mov tmp1, par ' start of structure
rdlong ms001, tmp1 ' read ticks per ms
add tmp1, #4
mov cmdpntr, tmp1 ' save address of cmd
add tmp1, #4
mov pinpntr, tmp1 ' save address of pin
add tmp1, #4
mov pulsepntr, tmp1 ' save address of pulses
add tmp1, #4
mov timepntr, tmp1 ' save address of timing
This code snippet loads variables. Since "par" has been copied to the cog why is a "rdlong", a hub command, necessary?
With par you can only pass one value to the COG and it's a bit tricky, because 2 least significant bits are always 0.
So, what par is used for usually is for passing a HUB-RAM-address (the start address) where the COG can find/store the real values to be passed to/from the COG. As the least significant bits of par are 0 this address has to be a long-alligned address. Long addresses do not have the 2 LSBs set by "nature";o)
Another thing to take into account is, that par is a read-only register for the COG! That's why it is moved to another register first. This way the addresses of the following variables can be calculated in tmp1.
RDLONG in the given example is needed because what's passed in PAR is only the address of a variable. If the PASM program is interested in the content of that variable, it has to read it from HUB RAM first.
@MagIO2, That's a lot of info to digest. PAR is the first of the special purpose registers. It copies a long-aligned 14 bit main memory address to the start of the assembly program. "rdlong" retrieves the value from that address. I hope I got that right.
Is the LSB reserved for the Z and C flag?
When I see addtmp1,#4 is the cog retrieving the next value from main memory?
add does not read from main ram. I just realized that the other variables in that snippet are initialized to zero. JonnyMac is demonstrating how Spin shares values with PASM. This is great! I hope the forum isn't tired of me yet.
Where are constants located?
I see that the 9-bit limit = 511. This limit does not exist in main RAM.
An instruction in pasm is 32 bits. Within those 32 bits are stored all the information about that instruction - eg what it is and what some parameters might be. (There is a tutorial somewhere that shows this in more detail). eg a Jump instruction has the code to say it is a jump instruction, and also the destination to jump to.
So it is a compromise and it ends up that there are 9 bits available for a number where the number is contained within an instruction.
If you want to work with a number more than 511, then you need to declare it at the bottom of the pasm program.
When numbers are declared at the bottom of the pasm section, that section includes both constants and variables. So to conserve memory it helps to recycle constants as much as possible, and it also helps to recycle variables.
It is the recycling of variables that can confuse me with pasm, because you have to be careful not to upset another part of code using the same variable.
then scroll to near the bottom to desylva's tutorial where it says "download pdf"
then at the bottom of page 6
To finish this first chapter – and before going on to explain more
instructions and programming techniques – we shall memorize the
structure of the 32 bits of an instruction:
6 Bits: instruction or operation code (OPCODE)
3 Bits: setting flags (Z, C) and result
1 Bit: immediate addressing
4 Bits: execution condition
9 Bits: dest-register
9 Bits: source register or immediate value
This has been a very productive thread for me. I can actually read PASM now. I can understand the concise truth tables. I see the difference between RES and instruction registers. I have to study all the commands but I now know how to find the answers I need. This forum is terrific.
Comments
OR OPERATION
$0A = %0000_1010
$05 = %0000_0101
$0F = %0000_1111
It still looked like addition so I had to do some digging. I think if there is a '1' in either the source or the destination the result = '1' "or else" = '0'.
How to read that?:
(You have to read it in columns)
1st column: If input A is 0 and input B is 0 then the output is 0
2nd column: If input A is 1 and input B is 0 then the output is 1
...
The OR is a little bit confusing when comparing it with an OR used in speech because there the common sense is to use it with implicit meaning "either or". But either or is called exclusive or in boolean algebra:
If you do these operations on longs you have to use this truth-table for each pair of bits in source and destination, as Peter did it in his post!
I think to make the example given in the concise truth table more complete there should also be an example that shows the result for the case of 2 corresponding bits being 1!
$05 or $03 for example.
The or behaving same way as an addition is only true in the special case where all 1 bits in the source and destination are located at different bit-positions. For example:
%1100_0000 OR %0001_1100 = %1101_1100 -> 192 + 28 = 220 => correct
%1010_1010 OR %0100_0001 = %1110_1011 -> 170 + 65 = 235 => correct
BUT
%1010_1010 OR %0001_1100 = %1011_1110 -> 170 + 28 = 190 => not correct
In any case, the way to look at these is to look at the binary digits, not the hexadecimal or the decimal representation.
BATTERY+
[SWITCH A]
[SWITCH B]
LAMP----BATTERY-
If [SWITCH A] AND [SWITCH B] are active (closed) then the LAMP will light.
BATTERY+ -|----[SWITCH A]---|
LAMP----BATTERY-
..........|----[SWITCH B]---|
If [SWITCH A] OR [SWITCH B] are active (closed) then the LAMP will light.
A truth table is just a way of representing all possible input conditions and their outputs, for the OR you would start off with both switches off even though you already know what's not going to happen!
We use 0 to indicate inactive or off and then we use 1 to indicate active or on.
B A LAMP
0 0 0
So we flip switch A on and observe the result and record it in the next truth table entry
B A LAMP
0 1 1
Then we flip switch A off and B on.
B A LAMP
1 0 1
Then finally both of them are on.
B A LAMP
1 1 1
So we put it all together and we get.
B A LAMP
0 0 0
0 1 1
1 0 1
1 1 1
Now take a look at each row as if it were a single column of 32 columns (lamps) and it's no different from a processor's OR operation, it does all 32 as individual "bitwise" operations.
That is quite a list!
I like to simplify things.
OR - if A or B is high then the output is high. So the only time the output is low is when both A and B are low.
AND - if A and B are high then the output is high. In all other cases the output is low.
NOR - put an inverter on an OR gate
NAND - put an inverter on an AND gate.
XOR - I can never remember. If the inputs are different then the output is one value and if they are the same it is the other value. I look up XOR if I need to use it.
Hex, decimal and binary. Hex is just 0-9 then A=10, B=11 and F=15 and then start at zero again.
Hex to binary to decimal - check out windows calculator (in accessories I think) and there is an option to turn it into a scientific calculator and it can do the conversions. I use that all the time - much easier than trying to memorise things.
BINARY ok
ok
1001 ok
1100 OR CR .
1101 ok
ok
1001 ok
1100 AND CR .
1000 ok
ok
HEX ok
9 0C AND . 8 ok
BINARY ok
1001 1100 AND ok
HEX . 8 ok
Well if you can get your head around entering numbers first as if you are putting them down in front of you like cards in a stack and then deciding what to do with them after you've got them there:
First instruction BINARY which just tells Forth to accept numbers we enter as binary by default and print them out as such
First card 1001 which is 9 in decimal (8+0+0+1)
Second card 1100 (8+4+0+0)which is 12 in decimal or 0C in hex (just substitute familiar symbols A..F for 10..15)
Next instruction OR which "grabs" the first two cards off the stack and just plain ORs them and puts it's result as a card back on top of the stack.
nothing doing yet but we tell Forth to give us a new line with CR (carriage return) to line up the result and immediately after....
grab the card off the top of the stack and print out the number (the command is the dot symbol)
Forth just does what you tell it.
Next couple of examples you can probably figure likewise.
Oh heck, forum code is eating up the listing, here it is from the screen:
ah - well if you are learning pasm there are a few things to make it easier. Write all the variables as binary #% and then you can see all the pins. Much easier than hex or decimal.
To set a pin high, do an OR with that particular pin set to 1 and all the rest set to 0.
To set a pin low, do an AND with that particular pin set to 0 and all the rest set to 1.
There is a third instruction, ANDN - I get confused about the exact logic, but what it means is that if you have your #%00000000_00001000_00000000_000000 variable that you used with an OR to set a pin high, you can reuse the same variable to set that pin low by using ANDN. It means you can have a lot less variables defined and that saves memory. So rather than use OR then AND to toggle pins, you tend to use OR and ANDN.
Translating spin into pasm can be fun. It is good brain exercise but it is very rewarding to see the same code run 20x faster or more.
I like to start simple and translate only a few lines at a time from spin to pasm. Have you got some spin code? Maybe we could have a go translating?
ANDN simply means AND NOT, where NOT is the bitwise inversion of the source. In your example it means that
%00000000_00001000_00000000_000000
is first inverted to
%11111111_11110111_11111111_11111111
and then ANDed with the destination.
BTW: #%00000000_00001000_00000000_000000 looks like a immediate value (because of the #), which is not allowed to be longer than 9 bits.
PS: Your last byte has lost 2 bits ;o) And I wondered why the number with 31 times 1 is longer than your number with lots of 0.
At this point I can't even grasp asm code that blinks an LED.
With ANDN the two bits are inverted first and the result input to the AND function. The end result is a 1 only if both inputs are 0.
First of all ANDN makes perfect sense to me! Let's say you want to use this operation on a register called source and a register called destination:
destination AND NOT source
perfectly describes what happens!
As NOT is an unary operation it only works on source and inverts all the bits of source
IT DOES NOT INVERT destination!
destination and the inverted source are then ANDed.
In other words: The operation ANDN deletes all bits set in source and keeps the content of destination for all bits not set in source.
If you really want to understand assembly, I would recommend understanding the underlying hardware. This book is college level, but very still a very good and approachable introduction to hardware:
http://www.amazon.com/Computer-Organization-Design-Fourth-Edition/dp/0123744938
It covers topics such as:
* electronic gates
* datapaths
* datapath components
* pipelining
* assembly instructions
* memory hierarchy
All of which are important to programming in assembly for any processor.
Side note: many will argue (complain!) that you don't actually need to learn the hardware to program in assembly. Sure. But then you'll be the guy working on the assembly line, putting things together according to direction. If you learn the hardware, you'll be designing the system.
In any case, with computer hardware you don't really need much of a background in anything except a comfortable understanding of basic math.
http://iiusatech.com/murdocca/POCA/slides/
Ooops. My mistake.
Re ANDN, it is a slightly unusual instruction. Not a "standard" logic function but it makes sense when you only have 496 longs to use in a cog. I'm glad it has been added to the pasm opcode set.
Good point. Remember, in pasm this is not a 32 bit micro. It is a 9 bit micro. The entire instruction fits in 32 bits and by the time you add the source, the destination and the instruction there are not many bits left for a constant value. So for constant 32 bit values you have to declare them at the end of the program. It is a slightly strange setup, but it makes sense if you start with the premise that each instruction plus data has to fit in 32 bits. So the number 512 is special. Less than 512, you can declare it in an instruction. Equal or more than 512 and you have to declare it as a constant. Those constant declarations take space, and hence you have to be smart how you declare them. Hence the ANDN instruction as it decreases the number of declarations.
Re learning pasm, I'm not sure there is the 'perfect' tutorial out there on the interweb. I like to pull apart code from others, particularly code written by experts
Please post a bit of spin code. I'm sure the forum would love to have a go showing you how to translate to pasm.
Where are constants located?
I see that the 9-bit limit = 511. This limit does not exist in main RAM. Is there an example showing how ANDN would decrease a constant declaration?
One of the things I got stuck on is from JonnyMac's "led_ctrl.spin:
This code snippet loads variables. Since "par" has been copied to the cog why is a "rdlong", a hub command, necessary?
So, what par is used for usually is for passing a HUB-RAM-address (the start address) where the COG can find/store the real values to be passed to/from the COG. As the least significant bits of par are 0 this address has to be a long-alligned address. Long addresses do not have the 2 LSBs set by "nature";o)
Another thing to take into account is, that par is a read-only register for the COG! That's why it is moved to another register first. This way the addresses of the following variables can be calculated in tmp1.
RDLONG in the given example is needed because what's passed in PAR is only the address of a variable. If the PASM program is interested in the content of that variable, it has to read it from HUB RAM first.
Is the LSB reserved for the Z and C flag?
When I see add tmp1, #4 is the cog retrieving the next value from main memory?
An instruction in pasm is 32 bits. Within those 32 bits are stored all the information about that instruction - eg what it is and what some parameters might be. (There is a tutorial somewhere that shows this in more detail). eg a Jump instruction has the code to say it is a jump instruction, and also the destination to jump to.
So it is a compromise and it ends up that there are 9 bits available for a number where the number is contained within an instruction.
If you want to work with a number more than 511, then you need to declare it at the bottom of the pasm program.
When numbers are declared at the bottom of the pasm section, that section includes both constants and variables. So to conserve memory it helps to recycle constants as much as possible, and it also helps to recycle variables.
It is the recycling of variables that can confuse me with pasm, because you have to be careful not to upset another part of code using the same variable.
Keep the questions coming
Go to this website http://gadgetgangster.com/news/45-designer-news/275-propeller-assembly-tutorial.html
then scroll to near the bottom to desylva's tutorial where it says "download pdf"
then at the bottom of page 6
I found this tutorial most helpful.