simple for loop and sum - loops

I'm trying to learn HCS12 assembly language but there are no enough examples on the internet. I've tried to write a code but there is no success. I'm stuck. It's not absolutely homework. Can someone write it in HCS12 assembly language with comments? I want code because really I want to read it step by step. By the way, is there any other way more simple to define array?
;The array arr will be located at $1500 and the contents {2, 5, 6, 16, 100, 29, 60}
sum = 0;
for i = 0 : 6
x = arr[i];
if( x < 50 )
sum = sum + x
end
My try:
Entry:
;2,5,6,16,100,39,60
LDAA #2
STAA $1500
LDAA #5
STAA $1501
LDAA #6
STAA $1502
LDAA #16
STAA $1503
LDAA #100
STAA $1504
LDAA #39
STAA $1505
LDAA #60
STAA $1506
CLRA ; 0 in accumulator A
CLRB ; 0 in accumulator B
ADDB COUNT ; B accumulator has 6
loop:
;LDAA 1, X+ ; 1500 should be x because it should increase up to 0 from 6
; A accumulator has 2 now
BLO 50; number less than 50
;ADDA
DECB
BNE loop

Below is one possible way to implement your specific FOR loop.
It's mostly for the HC11 which is source level compatible to the HCS12 so it should also assemble correctly for the HCS12. However, the HCS12 has some extra instructions and addressing modes (e.g., the indexed auto-increment) which can make the code a bit shorter and even more readable. Anyway, I haven't actually tried this but it should be OK.
BTW, your code shows you have some fundamental lack of understanding for certain instructions. For example, BLO 50 does not mean branch if accumulator is below 50. It means check the appropriate CCR (Condition Code Register) flags which should be already set by some previous instruction, and branch to address 50 (obviously, not what you intended) if the value is less than the target. To compare a register to a value or some memory location you must use the CMPx instructions (e.g., CMPA).
;The array arr will be located at $1500 and the contents {2, 5, 6, 16, 100, 29, 60}
org $1500 ;(somewhere in ROM)
arr fcb 2,5,6,16,100,29,60 ;as bytes (use dw if words)
org $100 ;wherever your RAM is
;sum = 0;
sum rmb 2 ;16-bit sum
org $8000 ;wherever you ROM is
;for i = 0 : 6
clrb ;B is your loop counter (i)
stb sum ;initialize sum to zero (MSB)
stb sum+1 ; -//- (LSB)
ForLoop cmpb #6 ;compare against terminating value
bhi ForEnd ;if above, exit FOR loop
; x = arr[i];
ldx #arr ;register X now points to array
abx ;add offset to array element (byte size assumed)
ldaa ,x ;A is your target variable (x)
;;;;;;;;;;;;;;;;;;; ldaa b,x ;HCS12 only version (for the above two HC11-compatible lines)
inx ;X points to next value for next iteration
;;;;;;;;;;;;;;;;;;; ldaa 1,x+ ;HCS12 only version (for the above two HC11-compatible lines)
; if( x < 50 )
cmpa #50
bhs EndIf
; sum = sum + x
adda sum+1
staa sum+1
ldaa sum
adca #0
staa sum
EndIf
incb ;(implied i = i + 1 at end of loop)
bra ForLoop
;end
ForEnd
The above assumes your array is constant, so it is placed somewhere in ROM at assembly time. If your array is dynamic, it should be located in RAM, and you would need to use code to load it (similar to how you did). However, for efficiency, a loop is usually used when loading (copying) multiple values from one location to another. This is both more readable and more efficient in terms of needed code memory.
Hope this helps.
Edited: Forgot to initialize SUM to zero.
Edited: Unlike in the HC08, a CLRA in HC11 clears the Carry so the sequence CLRA, ADCA is wrong. Replaced with correct one: LDAA, ADCA #0

Related

LC-3 Assembly Language Sentinel-Controlled Loop How come my code ends when I press enter?

I am practicing using arrays and loops and I am trying to have the user ENTER less than 100 characters in console to fill up my array. The user can press ENTER whenever they are done entering how ever many characters they want and the program will print out what they entered again.
The program works but I am wondering how the program checks to see if the user press ENTER.
I have it so the program will add #-10 to the inputted character and ENTER is x0A which is 10 in decimal. I'm assuming once the program detects this the result is 0 which if false and exits the loop. That is my thought process.
Also, how would I change my code to make it so I can have the exit character be anything?
.orig x3000
LD R1,DATA_PTR ;load the memory address of array into R1
DO_WHILE_LOOP
GETC ;read characters into R0
OUT ;print R0 onto console as ASCII
STR R0,R1, #0 ;stores into memory location in R1
ADD R1,R1, #1 ;increment to next memory address
ADD R0,R0,#-10 ;looks at inputted character and checks if its is ASCII #10
BRp DO_WHILE_LOOP
LD R0,newline
OUT
LD R1,DATA_PTR
DO_WHILE_LOOP2
LDR R0,R1,#0 ;load R1 into R0
OUT ;print
ADD R2,R0,#0 ;move R0 to R2
LD R0,newline ;newline
OUT ;print
ADD R1,R1,#1 ;increment
ADD R2,R2,#-10 ;check if printed character is enter ASCII #10
BRp DO_WHILE_LOOP2 ;if not print next character(loop)
HALT
;Data
DATA_PTR .FILL ARRAY ;DATA_PTR gets the beginning of the ARRAY
newline .FILL x0A
ARRAY .BLKW #100
.END
I'm assuming once the program detects this the result is 0 which if false and exits the loop.
It's not — that the result 0 here has meaning of "false" — but that the difference between the input character and 10 is 0 meaning it was exactly 0xA or 10(dec).
NB:  the use of BRp can probably be considered a bug, though using usual simulators I've had trouble entering a character whose ascii value smaller than 10.
In high level language terms what it is saying is:
do { ... } while ( in > 10 );
Though using BRnp would mean:
do { ... } while ( in != 10 );
which is more specific to newline.
If you want a different terminal character, change the value subtracted to the value of another character.
LC-3 does not offer subtraction, but it can "add" a negative number.  However, it cannot add a negative number smaller than -16 using the same immediate form of ADD.  So, if you want to check for an ascii character larger than 16, you'll have to use the add register form instead, and use another instruction to load that register with the value, usually using a labeled constant, declared with .FILL and the value you want.
Instead of:
ADD R2,R2,#-10 ;check if printed character is enter ASCII #10
BRp DO_WHILE_LOOP
Do something like the following:
LD R3, value ; load value to subtract
ADD R2, R2, R3 ; subtract them
BRnp DO_WHILE_LOOP
...
...
value, .FILL #-65 ; letter A, negated.
In LC-3 the ADD instruction sets condition codes.
There are three condition codes, N, Z, and P, — N for negative, Z for zero, and P for positive.  If you add zero to some register, as part of the addition operation, those three flags(condition codes) will be set as follows: N if the original value was negative, Z if the original value is zero, P if the original value is positive — so, < 0, = 0, > 0.
If we use ADD to add a non-zero value, here X (but in its negation, -X) to a register value, V, we get flags that tell us:
N = (V < X) i.e. N is true if V < X,
Z = (V = X) i.e. Z is true if V = X, and,
P = (V > X) i.e. P is true iv V > X
(all ignoring possibilities for overflow).
The BR instruction can then test flags as follows:
If you would like to change the program flow of control on:
relation
idea
Opcode
<
N
BRn
>=
not N
BRzp
=
Z
BRz
!=
not Z
BRnp
>
P
BRp
<=
not P
BRnz

I would like to calculate the sum of the first n integers in assembly language

I have just started to learn assembly language at school, and as an exercise I have to make a program that calculate the sum of the first n integers (1+2+3+4+5+...+n).
I managed to build this program but during the comparison (line.9) I only compare the even numbers in register R1, so I would have to do another comparison for the odd numbers in R0.
MOV R0,#1 ; I put a register at 1 to start the sequence
INP R2,2 ; I ask the user up to what number the program should calculate, and I put its response in the R2 register
B myloop ; I define a loop
myloop:
ADD R1,R0,#1 ; I calculate n+1 and put it in register 1
ADD R3,R1,R0 ; I add R0 and R1, and I put the result in the register R3
ADD R0,R1,#1 ; I calculate n+2 and I put in the register R0, and so on...
ADD R4,R4,R3 ; R4 is the total result of all additions between R0 and R1
CMP R1,R2 ; I compare R1 and the maximum number to calculate
BNE myloop ; I only exit the loop if R1 and R2 are equal
STR R4,100 ; I store the final result in memory 100
OUT R4,4 ; I output the final result of the sequence
HALT ; I stop the execution of the program
I've tried several methods but I can't manage to perform this double comparison... (a bit like an "elif" in python)
Basically I would like to add this piece of code to also compare odd numbers:
CMP R0,R2
BNE myloop
But adding this like this directly after comparing even numbers doesn't work no matter if I put "BNE" or not.
You're trying to do a conjunction, in context, something like this:
do {
...
} while ( odd != n && even != n );
...
Eventually, one of those counters should reach the value n and stop the loop.  So, both tests must pass in order to continue the loop.  However, if either test fails, then the loop should stop.
First, we'll convert this loop into the if-goto-label form of assembly (while still using the C language!):
loop1:
...
if ( odd != n && even != n ) goto loop1;
...
Next, let's break down the conjunction to get rid of it.  The intent of the conjunction is that if the first component fails, to stop the loop, without even checking the second component.  However, if the first component succeeds, then go on to check the second component.  And if the second also succeeds, then, and only then return to the top of the loop (knowing both have succeeded), and otherwise fall off the bottom.  Either way, whether the first component fails or the second component fails, the loop stops.
This intent is fairly easy to accomplish in if-goto-label:
loop1:
...
if ( odd == n ) goto endLoop1;
if ( even != n ) goto loop1;
endLoop1:
...
Can you figure out how to follow this logic in assembly?
Another analysis might look like this:
loop1:
...
if ( odd == n || even == n ) goto endLoop1;
goto loop1;
endLoop1:
...
This is the same logic, but stated as how to exit the loop rather than how to continue the loop.  The condition is inverted (De Morgan) but the intended target of the if-goto statement is also changed — it is effectively doubly-negated, so holds the same.
From that we would strive to remove the disjunction, again by making two statements instead of one with disjunction, also relatively straightforward using if-goto:
loop1:
...
if ( odd == n ) goto endLoop1;
if ( even == n ) goto endLoop1;
goto loop1;
endLoop1:
...
And with an optimization sometimes known as branch over unconditional branch (the unconditional branch is goto loop1;), we perform a pattern substitution, namely: (1) reversing the condition of the conditional branch, (2) changing the target of the conditional branch to the target of the unconditional branch, and (3) removing the unconditional branch.
loop1:
...
if ( odd == n ) goto endLoop1;
if ( even != n ) goto loop1;
endLoop1:
...
In summary, one takeaway is to understand how powerful the primitive if-goto is, and that it can be composed into conditionals of any complexity.
Another takeaway is that logic can be transformed by pattern matching and substitution into something logically equivalent but more desirable for some purpose like writing assembly!  Here we work toward increasing use of simple if-goto's and lessor use of compound conditions.
Also, as #vorrade says, programs generally should not make assumptions about register values — so suggest to load 0 into the registers that need it at the beginning of the program to ensure their initialization.  We generally don't clear registers after their use but rather set them before use.  So, in another larger program, your code might run with other values from some other code left over in those registers.
Also, I answer the question posed, which is about compound conditionals, and explain how those work in some detail; though as stated elsewhere, there's no need to separate even and odd numbers in order to sum them, and, there's also a single formula that can compute the sum of numbers without iteration (though does require multiplication which may not be directly available and so would require a more bounded iteration..).
First of all your code assumes that R4 is 0 at the beginning.
This might not be true.
Your program becomes simpler and easier to understand if you add each number in a smaller loop, like this:
INP R2,2 ; I ask the user up to what number the program should calculate, and I put its response in the R2 register
MOV R0,#0 ; I put a register at 0 to start the sequence
MOV R4,#0 ; I put a register at 0 to start the sum
B testdone ; Jump straight to test if done
myloop:
ADD R0,R0,#1 ; I calculate n+1 and keep it in register 0
ADD R4,R4,R0 ; I add R4 and R0, and I put the result in the register R4
testdone:
CMP R0,R2 ; I compare R0 and the maximum number to calculate
BNE myloop ; I only exit the loop if R0 and R2 are equal
STR R4,100 ; I store the final result in memory 100
OUT R4,4 ; I output the final result of the sequence
HALT ; I stop the execution of the program
You only need 3 registers: R0 for current n, R2 for limit and R4 for the sum.
However, if you really have to add the even and odd numbers separately, you could do this way:
INP R2,2 ; I ask the user up to what number the program should calculate, and I put its response in the R2 register
MOV R0,#0 ; I put a register at 0 to start the sequence
MOV R4,#0 ; I put a register at 0 to start the sum
B testdone ; Jump straight to test if done
myloop:
ADD R0,R0,#1 ; I calculate n+1 (odd numbers), still register 0
ADD R4,R4,R0 ; I add R4 and R0, keep the result in the register R4
CMP R0,R2 ; I compare R0 and the maximum number to calculate
BEQ done ; I only exit the loop if R0 and R2 are equal
ADD R0,R0,#1 ; I calculate n+1 (even numbers) and keep it in register 0
ADD R4,R4,R0 ; I add R4 and R0, and I put the result in the register R4
testdone:
CMP R0,R2 ; I compare R0 and the maximum number to calculate
BNE myloop ; I only exit the loop if R0 and R2 are equal
done:
STR R4,100 ; I store the final result in memory 100
OUT R4,4 ; I output the final result of the sequence
HALT ; I stop the execution of the program
First of all I would like to thank you very much #vorrade , #Erik Eidt and #Peter Cordes, I read your comments and advice very carefully, and they are very useful to me :)
But in fact following the post of my question I continued to seek by myself a solution to my problem, and I came to develop this code which works perfectly!
// Date : 31/01/2022 //
// Description : A program that calculate the sum of the first n integers (1+2+3+4+5+...+n) //
MOV R0,#1 // I put a register at 1 to start the sequence of odd number
INP R2,2 // I ask the user up to what number the program should calculate, and I put its response in the R2 register
B myloop // I define a main loop
myloop:
ADD R1,R0,#1 // I calculate n+1 (even) and I put in the register R1, and so on...
ADD R3,R1,R0 // I add R0 and R1, and I put the result in the register R3
ADD R4,R4,R3 // R4 is the total result of all additions between R0 and R1, which is the register that temporarily stores the calculated results in order to increment them later in register R4
ADD R0,R1,#1 // I calculate the next odd number to add to the sequence
B test1 // The program goes to the first comparison loop
test1:
CMP R0,R2 // I compare the odd number which is in the current addition with the requested maximum, this comparison can only be true if the maximum is also odd.
BNE test2 // If the comparison is not equal, then I move on to the next test which does exactly the same thing but for even numbers this time.
ADD R4,R4,R2 // If the comparison is equal, then I add a step to the final result because my main loop does the additions 2 by 2.
B final // The program goes to the final loop
test2:
CMP R1,R2 // I compare the even number which is in the current addition with the requested maximum, this comparison can only be true if the maximum is also even.
BNE myloop // If the comparison is not equal, then the program returns to the main loop because this means that all the comparisons (even or odd) have concluded that the maximum has not yet been reached and that it is necessary to continue adding.
B final // The program goes to the final loop
final:
STR R4,100 // I store the final result in memory 100
OUT R4,4 // I output the final result of the sequence
HALT // I stop the execution of the program
I made some comments to explain my process!
I now realize that the decomposition into several loops was indeed the right solution to my problem, it allowed me to better realize the different steps that I had first written down on paper.

How do I copy an array into another array?

I am just starting out learning assembly and I am having a hard time understanding how I would copy one array into another array.
for example, let's say I have 2 arrays J and K:
J and K both contain 5 elements which are numbers that are 8 bits wide.
J = [0, 1, 2, 3, 4]
K = [5, 6, 7, 8, 9]
J is located in register 1 and K is located in register 2
How would I go about "appending"/"copying" J to K? (If that is even the correct way to think about it)
Would it just be:
LDR R3, R1[0] ; placing 0th J element into register R3
MOV R2, R3 ; Moving the R3 element into the array K
....
....
....
Continue like that until all elements have been copied over to array K
So the result I am trying to obtain is an array with the elements from both the initial arrays result = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
I am sure this is completely wrong, so if anyone is able to shed some light on this for me it would be much appreciated!
This can definitely be done in assembly, but it's difficult for a few reasons:
You need to know where the arrays are, and make sure you don't overwrite anything important when trying to copy them. Let's say for example that you want to just copy K directly behind J. If something else important is stored behind J then it will get erased!
How big is each array? It's obvious to you and me but the computer doesn't really have a clue. You have two choices: measure it yourself or know the array sizes ahead of time.
You'll need a different routine for each data type.
For this example code I'll assume that you want to place the appended array J+K in a separate section of memory that isn't going to overwrite anything you're using. Also this code assumes that both array sizes are 5 bytes. This isn't particularly useful since chances are you're going to want your code to be able to handle arrays of various sizes, not just this one particular size.
LDR R2,=ARRAY_J
LDR R3,=ARRAY_K
LDR R4,=ARRAY_L
MOV R1,#5 # size of ARRAY_J goes here.
loop_append_J:
LDRB R0,[R2],#1 # load R0 from ARRAY_J, add 1 to the pointer so we read the
# next byte on the next pass.
STRB R0,[R4],#1 # store R0 into ARRAY_L (ARRAY J + ARRAY K), add 1 to the
# pointer
SUBS R1,R1,#1 # decrease loop counter and set the flags accordingly
BNE loop_append_J # if R1 doesn't equal zero, loop again.
MOV R1,#5 # size of ARRAY_K goes here
loop_append_K:
LDRB R0,[R3],#1 # this is pretty much the same story, just with the second
# array. R4 already points to where it needs to.
STRB R0,[R4],#1
SUBS R1,R1,#1
BNE loop_append_K
# now your program is done, do whatever you need to do to return, be it BX LR or whatever
.data # forgive me if the syntax is wrong, I'm used to VASM which doesn't
# have data directives like this.
ARRAY_J:
.byte 0,1,2,3,4 #whatever directive you use to define 8-bit values
ARRAY_K:
.byte 5,6,7,8,9 #whatever directive you use to define 8-bit values
ARRAY_L: #empty space to hold the new array.
.space 64,0 #this is more than enough room to store the new array.

Fibonacci sequence on 68HC11 using 4-byte numbers

I'm trying to figure out a way to implement the Fibonacci sequence using a 68HC11 IDE that uses a Motorolla as11 assembler.
I've done it using 2-byte unsigned in little-endian format, now I'm attempting to change it using 4-byte variables, using big-endian
My pseudo-code (which is written in c):
RESULT = 1;
PREV = 1;
COUNT = N;
WHILE(COUNT > 2){
NEXT = RESULT + PREV;
PREV = RESULT;
RESULT = NEXT;
COUNT--;
}
I'll include some of my current assembly code. Please note that count is set to unsigned int at 1-byte, and prev, next, and result are unsigned ints at 2 bytes. N is unsigned, set to 10.
ORG $C000
LDD #1
STD RESULT
STD PREV
LDAA N
STAA COUNT
WHILE LDAA COUNT
CMPA #2
BLS ENDWHILE
LDD RESULT
ADDD PREV
STD NEXT
LDD RESULT
STD PREV
LDD NEXT
STD RESULT
DEC COUNT
BRA WHILE
ENDWHILE
DONE BRA DONE
END
The issue that I'm having is now altering this (other than the obvious variable changes/declarations) N will begin at 40 now, not 10. Would altering my pseudo-code to include pointers allow me to implement it 1 to 1 better with big-endian? Since this is in little-endian, I assume I have to alter some of the branches. Yes this is an assignment for class, I'm not looking for the code, just some guidance would be nice.
Thank you!
(Your problem description is a bit vague as to what your actual problem is, so I may be guessing a bit.)
BTW, 68HC11 is big-endian.
The 68HC11 has a 16-bit accumulator, so as soon as your result overflows this, you need to do math operations in pieces.
I suppose you mean that by changing N from 10 to 40 your fibonacci number becomes too big to be stored in a 16-bit variable.
The use or not of pointers is irrelevant to your problem as you can solve it both with or without them. For example, you can use a pointer to tell your routine where to store the result.
Depending on your maximum expected result, you need to adjust your routine. I will assume you won't need to go over 32-bit result (N=47 => 2971215073).
Here's a partially tested but unoptimized possibility (using ASM11 assembler):
STACKTOP equ $1FF
RESET_VECTOR equ $FFFE
org $100 ;RAM
result rmb 4
org $d000 ;ROM
;*******************************************************************************
; Purpose: Return the Nth fibonacci number in result
; Input : HX -> 32-bit result
; : A = Nth number to calculate
; Output : None
; Note(s):
GetFibonacci proc
push ;macro to save D, X, Y
;--- define & initialize local variables
des:4 ;allocate 4 bytes on stack
tmp## equ 5 ;5,Y: temp number
ldab #1
pshb
clrb
pshb:3
prev## equ 1 ;1,Y: previous number (initialized to 1)
psha
n## equ 0 ;0,Y: N
;---
tsy ;Y -> local variables
clra
clrb
std ,x
std prev##,y
ldd #1
std 2,x
std prev##+2,y
Loop## ldaa n##,y
cmpa #2
bls Done##
ldd 2,x
addd prev##+2,y
std tmp##+2,y
ldaa 1,x
adca prev##+1,y
staa tmp##+1,y
ldaa ,x
adca prev##,y
staa tmp##,y
ldd ,x
std prev##,y
ldd 2,x
std prev##+2,y
ldd tmp##,y
std ,x
ldd tmp##+2,y
std 2,x
dec n##,y
bra Loop##
Done## ins:9 ;de-allocate all locals from stack
pull ;macro to restore D, X, Y
rts
;*******************************************************************************
; Test code
;*******************************************************************************
Start proc
ldx #STACKTOP ;setup our stack
txs
ldx #result
ldaa #40 ;Nth fibonacci number to get
bsr GetFibonacci
bra * ;check 'result' for answer
org RESET_VECTOR
dw Start

Storing an array into another array

I'm trying to copy array A into array N and then print the array (to test that it has worked) but all it outputs is -1
Here is my code:
ORG $1000
START: ; first instruction of program
clr.w d1
movea.w #A,a0
movea.w #N,a2
move.w #6,d2
for move.w (a0)+,(a2)+
DBRA d2,for
move.w #6,d2
loop
move.l (a2,D2),D1 ; get number from array at index D2
move.b #3,D0 ; display number in D1.L
trap #15
dbra d2,loop
SIMHALT ; halt simulator
A dc.w 2,2,3,4,5,6
N dc.l 6
END START ; last line of source
Why is -1 in the output only? If there is a better solution for this that would be very helpful
Since I don't have access to whatever assembler/simulator you're using, I can't actually test it, but here a few things (some of which are already noted in the comments):
dc.l declares a single long, you want ds.l (or similar) to allocate storage for 6 longs
dbra branches until the operand is equal to -1, so you'll probably want to turn
movw #loop_times, d0
loop
....
dbra d0, loop
into
movw #loop_times-1, d0
loop
....
dbra d0, loop
(this works as long as loop_times is > 0, otherwise you'll have to check the condition before entering the loop)
You display loop has a few problems: 1. On entry a2 points past the end of the N array. 2. Even fixing that, the way you're indexing it will cause problems. On the first entry you're trying to fetch a 4-byte long from address a2 + 6,then a long from a2 + 5...
What you want is to fetch longs from address a2 + 0, a2 + 4 .... One way of doing that:
move.w #6-1, d2 ; note the -1
movea.l #N, a2
loop
move.l (a2)+,D1 ; get next number from array
; use d1 here
dbra d2,loop
As already pointed out, your new array is only 4 bytes in size, you should change
dc.l 6 to ds.w 6
and also you work on 7 elements, since DBRA counts down to -1.
Second, and thats why you get -1 everywhere, you use A2 as pointer to the new array, but you do not reset it to point at the first word in new array. Since you increased it by one word per element during the copy, after the for loop has completed, A2 points to the first word after the array.
Your simulator outputting more than one number with your display loop indicates that your simulator does not emulate an MC68000, a real MC68000 would take a trap at "MOVE.L (A2,D2),D1" as soon as the sum of A2+D2 is odd - the 68000 does not allow W/L sized accesses to odd addresses (MC68020 and higher do).
A cleaned MC68000 compatible code could look like this:
lea A,a0
lea N,a2
moveq #5,d2
for move.w (a0)+,(a2)+
dbra d2,for
lea N,a2
moveq #5,d2
loop
move.w (a2)+,D1 ; get number (16 bits only)
ext.l d1 ; make the number 32 bits
moveq #3,D0 ; display number in D1.L
trap #15
dbra d2,loop
It probably contains some instructions you haven't encountered yet.

Resources