Assembly Language Tutorial

Raspberry Pi LogoIn this tutorial we’ll cover numerous topics while exploring the use of Assembly Language on Raspberry PI. We’ll learn about Vim, Make, Adding, Subtracting and converting Decimals, Binaries and Hexadecimal numbers. As per Assembly Language we’ll learn about Registers, the Current Program Status Register, Receiving Input, Outputting Data to the Monitor, .global, MOV, SWI, ADD, SUB, MUL, MLA, Syscall, LDR, .text, .data, CMP, Branching and a whole lot more. All of the code and a transcript follows the video below.

If you like videos like this consider supporting their creation on Patreon.

[googleplusone]

How to Access Raspberry PI from Windows or Mac

This is what I did to access RPi from either Mac or Windows :

1. Type the following in the terminal on RPi -> sudo apt-get install xrdp

2. Get the ip address for your RPi by typing this in the terminal -> hostname -I

3. On Mac or Windows install the Microsoft Remote Desktop 
a. Windows : https://www.microsoft.com/en-us/store/apps/remote-desktop/9wzdncrfj3ps
b. Mac : https://itunes.apple.com/us/app/microsoft-remote-desktop/id715768417?mt=12

4. To login to the RPi from Windows or Mac 
a. Connection : Raspberry PI
b. PC Name : <RPi ip Address>
c. User name : pi is the default
d. Password : raspberry is the default
e. Leave everything else in its default

Code From the Video

I. Introduction

	1. Assembly language is 1 step above the binary that runs your hardware.
	The assembler converts your code into binary. We will be programming
	the ARM microprocessor in this tutorial. This code will only work
	with an ARM based chip, but by learning assembly on a low cost computer
	you'll be able to easily learn how to use assembly in general. The only
	difference is the mnemonics used. Also ARM chips are not only used 
	with Raspberry PI, but also by most mobile devices.
	
	2. Microprocessors manipulate data and this means you'll use the
	ADD and SUB codes for adding and subtracting. Like these most codes 
	are 3 letters long. MOV is another code. 
	
	3. Another reason why learning assembler on an ARM chip is a good idea
	is because the R in ARM stands for Reduced Instruction Set Computing. That
	means that there is a limited number of instructions or codes like ADD to
	remember. 

II. Setup

	1. Install Vim : sudo apt-get install vim
	
	2. Create a working directory : mkdir ASM\ Projects
	
	3. If you get the error No Space on Device message : sudo raspi-config
	expand_rootfs and then reboot
	
	4. Create the source file asmtut1.s : vim asmtut1.s
	
	5. Vim Commands 
	
		a. i : insert mode 
		
		b. <ESC> : enter command mode
			i. w : Save / Don't Exit
			ii. wq : Save / Quit
			iii. Q! : Quit / Discard Changes
			iv. w : Move to front of next word
			v. b : Move backwards to front of word
			vi. 0 : Move to start of line
			vii. $ : Move to end of line
			
		c. Move around with arrows
		
		d. :set number : Displays line numbers
		
		e. More commands will be covered as needed
		
	6. First program
	@ I'm a comment
	/* I'm a multiline comment */
	@ Define where your instructions start, but this isn't needed
	.text 
	
	@ Defines _start as a global available to the whole program
	.global _start
	
	@ Define starting point and that it is a label with :
	_start:
	
	@ Move the value 65 into register 0
	MOV R0, #65
	
	@ Move the value 1 into register 7
	MOV R7, #1
	
	@ End the program and return to the terminal
	SWI 0
	
		a. SWI stands for SoftWare Interrupt. If R7 has a value of 1 in it SWI knows to exit to the terminal. The value stored in R7 is the Syscall number. Depending on how different registers are set we can call for other Raspbian routines or libraries to be called. 
		
	7. Compile Code
		a. as -o asmtut.o asmtut.s
			1. Create an object file
			
		b. ld -o asmtut asmtut.o
			1. Make an executable file by linking files together
		
		c. Execute with ./asmtut ; echo $?
		
	8. If you type echo $? in the terminal the value stored in R0 is returned
	
	9. Second Program
	@ asmtut2.s
	.global _start
	_start:
	MOV R0, #65
	
	@ Branch to _part2 in the next part of the program
	BAL _part2
	
	@ asmtut3.s
	.global _part2
	_part2:
	MOV R7, #1
	SWI 0
	
		a. Compile program
		
			1. as -o asmtut2.o asmtut2.s
			
			2. as -o asmtut3.o asmtut3.s
			
			3. ld -o asmtut4.o asmtut2.o asmtut3.o
			
			4. ./asmtut4 ; echo $?
			
	10. We'll create a makefile to make it easier to compile. A makefile contains the following rules
	
		a. target: prerequisite
		   <TAB>   recipe
		   
		b. Target is the name of the file to generate
		
		c. The prerequisite is a file needed to create target. If the prerequisite doesn't exist we look for its creation in the targets that follow
		
		d. The recipe is an action that make executes
		
		e. Add the following to execute clean up with (make clean)
		clean:
			rm *.o asmtut
		
		f. asmtut: asmtut.o
				ld -o asmtut asmtut.o
		   asmtut.o asmtut.s
		   		as -o asmtut.o asmtut.s
		   clean:
				rm *.o asmtut
			
III. Registers, Bits, Bytes and Words (Slide)

	1. A bit is either a 1 or a 0

	2. The ARM CPU is a 32 bit CPU. A byte has 8 bits. 4 bytes or 32 bits are called words with this ARM CPU because 32 bits is the unit of data used by the Raspberry PI.
	
	3. Data is stored in memory in words, but you also have access to the bytes. 
	
	4. The CPU has 16 registers that can hold a word of data each. Registers 0 - 12 are always available. 
	
	5. Register 13 is a pointer to the active stack. You put your data on the stack and the stack pointer tells you how high that stack of data is. (More on this later)
	
	6. Register 14 is the Link Register. When an instruction calls a subroutine the LR is set to the subroutine return address. (More on this later)

	7. Register 15 is the program counter. It stores the address of the next instruction to execute. It keeps track of where your program is in its execution of your code. (More on this later)
	
	8. There is also the Current Program Status Register. It stores info on your program and results of instructions it has executed. (More on this later)
			
IV. Binary Numbers (Slide)

	1. Binary numbers contain only 1s and 0s which combine to make
	base 10 numbers we use. 
	
	2. In Base 10 123 = 1 : 100, 2 : 10s, 3 : 1s
	
	3. In Binary 111 = 1 : 4, 1 : 2, 1 : 1 = 7 in Base 10
	
	4. From right to left the digits double 64, 32, 16, 8, 4, 2, 1
	
	5. Decimal to Binary : If subtraction is possible add a 1 and if not don't
	
		a. 126 - 64 = 62 : 1
		b. 62 - 32 = 30 : 1
		c. 30 - 16 = 14 : 1
		d. 14 - 8 = 6 : 1
		e. 6 - 4 = 2 : 1
		f. 2 - 2 = 0 : 1
		g. 0 - 1 = NA : 0
		h. 126 to binary is 1111110
		
V. Hexadecimal Numbers (Base 16) (Slide)

	1. It is easier to represent binary numbers as hex numbers.
	
	2. Hex numbers contain the numbers 0 - 9 and A, B, C, D, E, F for numbers
	10 - 15
	
	3. 	We can represent 4 bits of a binary number with 1 hex
	
		D	H		B
		0   0		0000
		1	1		0001
		2	2		0010
		3	3		0011
		4	4		0100
		5	5		0101
		6	6		0110
		7	7		0111
		8	8		1000
		9	9		1001
		10	A		1010
		11	B		1011
		12	C		1100
		13	D		1101
		14	E		1110
		15	F		1111
		
	4. Convert Binary to Hex
	
		a. Convert a byte in 2 into 2 nibbles
		
		b. 10101010 becomes 1010 1010 or AA
		
	5. Convert Hex to Decimal
	
		a. 0x2A7 = 
		b. 2 * 16^2 = 512
		c. 10 * 16 = 160
		d. 7 * 1 = 7
		e. 512 + 160 + 7 = 679
		
	6. Convert Decimal to Hex
	
		a. 679 / 256 (16^2) = 2.65 (Throw away the remainder)
		b. (679 - 512) 167 / 16 = 10.43 or A (Throw away the remainder)
		c. 7 is left and so 679 = 2A7
		
VI. Adding and Subtracting Binaries
		
	1. Adding Binaries
	
		a. 0 + 0 = 0
		b. 1 + 0 = 1
		c. 0 + 1 = 1
		d. 1 + 1 = 0 (Carry the 1)
		
		e. 0101	: 5
		 + 0011 : 3
		   1000 : 8
		   
	2. Subtracting Binaries
	
		a. When subtracting you borrow 1 from the left to make
		a 0 into a 10 (In Binary) or 2 and then subtract 1 from
		
		b. You can't borrow from a 0 so when that happens you
		keep moving left until you get a 1 to borrow from. 
	
	
	3. Twos Complement Numbers
	
		a. To convert a number to its negative form flip each bit
		and then add 1 to it.
		
		b. 7 = 00111 and -7 = 11001
		
		c. You can now add versus subtract binaries which some people find easier
		
			1. 11000 : 24
			 - 00111 :  7
			 
			   11000 : 24
			 + 11001 : -7
			   10001 : 17
			   
			2. Through out the last carry bit to get the correct answer
			
VII. Input and Output

	1. To output to the screen we need to set the parameters as such
	
		a. R0 : Output Stream (1 : Monitor)
		
		b. R1 : Address of characters to write
		
		c. R2 : Number of characters to write
		
		d. R7 : Syscall (4 to write to screen)
		
	2. @ Hello World
	.global _start
	_start:
		MOV R7, #4 @ Syscall to output to screen
		MOV R0,  #1 @ Monitor output stream
		MOV R2, #12 @ String Length
		LDR R1, =message @ Load register with address of string
		SWI 0
		
	end:
		MOV R7, #1 @ Exit syscall
		SWI 0
		
	.data @ Signify that what follows is data
	message:
		.ascii "Hello World\n"
		
	3. To output we need to set parameters as well
	
		a. R0 : Input Stream (0 : Keyboard)
		
		b. R1 : Address of buffer to save string to
		
		c. R2 : # of characters to read
		
		d. R7 : Syscall (3 to read from keyboard)
		
	4. @ Read data
	.global _start
	_start:
		MOV R7, #3 @ Syscall read from keyboard
		MOV R0,  #0 @ Input stream keyboard
		MOV R2, #10 @ Read 10 characters
		LDR R1, =message @ Put string in message
		SWI 0
		
	_write:
		MOV R7, #4 @ Syscall to output to screen
		MOV R0, #1 @ Output to monitor
		MOV R2, #5	@ # of characters to write
		LDR R1, =message @ Print string in message
		SWI 0
		
	end:
		MOV R7, #1
		SWI 0
		
	.data
	message:
		.ascii " "
	
VIII. ADD
	
	1. Most instructions follow this order 
	<Instruction> <Dest>, <Operand>, <Operand>
		
		a. Instruction Name - 3 or 4 character mnemonic
		
		b. Destination Register
		
		c. Register to manipulate
		
		d. A Register or Value to manipulate
		
	2. ADD value in register 1 plus register 2 and store in register 0
	ADD R0, R1, R2
	
	3. ADD value in register 1 plus number 1 and store in register 0
	ADD R0, R1, #1
	
	4. Add 2 values and output the result
	.global _start
	_start:
	MOV R1, #0xA @ Put 10 in R1
	ADD R0, R1, #0x14 @ Add 10 + 20, store in R0
	MOV R7, #1 @ Exit with a system call
	SWI 0 @ End program and return to terminal
	
	5. make
	./asmtut4 ; echo $?
	   
	6. The SWI instruction interacts with the operating system. Linux provides
	many services and we select the one we want by putting a value in R7. If the program returns a value that value is returned in R0. 
	
IX. SUB
	
	1. We can subtract values
	.global _start
	_start:
	MOV R1, #0x14 @ Put 20 in R1
	SUB R0, R1, #0xA @ Subtract 20 - 10, store in R0
	MOV R7, #1 @ Exit with a system call
	SWI 0 @ End program and return to terminal
	
		a. Gives a result of 10
		
X. MUL & MLA

	1. Multiply values (Must store values in Registers)
	.global _start
	_start:
	MOV R1, #0x14 @ Put 20 in R1
	MOV R2, #0xA @ Put 10 in R2
	MUL R0, R1, #0xA @ Multiply 20 * 10, store in R0
	MOV R7, #1 @ Exit with a system call
	SWI 0 @ End program and return to terminal
	
	2. MLA (Multiply with Accumulate) Multiplies the first 2 register values and then adds that to the last register value
	.global _start
	_start:
	MOV R1, #0x14 @ Put 20 in R1
	MOV R2, #0xA @ Put 10 in R2
	MOV R3, #0x5 @ Put 5 in R3
	MLA R0, R1, R2, R3 @ (R1 * R2) + R3, store in R0
	MOV R7, #1 @ Exit with a system call
	SWI 0 @ End program and return to terminal
	
	3. VIM Copy / Cut and Paste Line
	
		a. Put the cursor on the line to copy
		
		b. Press V to select line
		
		c. Press d to cut or y to copy
		
		d. Move to place to paste
		
		e. P to paste before cursor, or p to paste after
		
		f. u undoes previous actions
		
		g. Ctrl - R Redoes undone action
		
XI. Branching & CPSR & CMP

	1. Branch instructions allow us to execute different instructions depending upon conditions. 
	
	2. Register 15 is the program counter and it stores the address of the next instruction to execute. After an instruction executes the PC is incremented by 4 bytes because each instruction is allocated 4 bytes. We can however change how much the PC increments by and execute instructions out of order. This is known as branching. 
	
	3. Branch to a label
	.global _start
	_start:
		MOV R0, #0x14 @ Put 20 in R0
		b other @ Jump to the other label
		MOV R0, #0xB @ Put 11 in R0
	other:
		MOV R7, #1 @ Exit with a system call
		SWI 0 @ End program and return to terminal
		
	4. I mentioned the Current Program Status Register before. It stores info about your program, such as operation results. A bit is set with a 1 if conditions have occurred such as : 
	
		a. Negative Flag : Is the number negative
		
			1. BPL negclear @ Move to label negclear if result is 0
			2. BMI negset @ Move to label negset if result is set
		
		b. Zero Flag : Set if the result is 0
		
			1. BEQ zeroset @ Move if 0 set
			2. BNE zeroclear @ Move if zero flag is 0
		
		c. Carry Flag : Set if there is a carry bit
		
			1. BCS carryset @ Move if carry set
			2. BCC carryclear @ Move if carry is 0
		
		d. Overflow Flag : Set if the result overflowed into the 31st bit, which is used to signify the sign of the value
		
			1. BVS overflowset @ Move if overflow set
			2. BVC overflowclear @ Move if overflow 0
		
	5. We can compare values using CMP like this : CMP R1, R2
	
		a. Values are compared by subtracting R1 - R2. When executed we can check the CPSR flags and can conditionally execute using branching like this :
		
			1. If R2 > R1 then the Negative Flag would be enabled because the result is negative
			
			2. If R2 < R1 then the Negative Flag wouldn't be set
			
			3. If R2 == R1 then the Zero Flag would be enabled
			
		b.  
		.global _start
		_start:
			MOV R1, #5
			MOV R2, #10
			CMP R1, R2 @ Compare values setting flags
			BEQ vals_equal @ If equal jump to vals_equal (Zero Flag Set)
			BGT r1_gt @ If R1 is greater jump to r1_gt (Negative Flag Set)
			
		@ Come here next by default
		r1_lt:
			MOV R0, #2
			B end
			
		r1_gt:
			MOV R0, #3
			B end
			
		vals_equal:
			MOV R0, #1
			
		end:
			MOV R7, #1 
			SWI 0
			
			1. You can also check for
			
				a. BNE : !=
				
				b. BGE : >=
				
				c. BLE : <=

Leave a Reply

Your email address will not be published. Required fields are marked *