mirror of
https://github.com/pkivolowitz/asm_book.git
synced 2026-06-23 05:16:50 +08:00
Merge branch 'main' of https://github.com/pkivolowitz/asm_book
This commit is contained in:
commit
43014fa728
10 changed files with 342 additions and 36 deletions
|
|
@ -324,6 +324,7 @@ contained therein are applicable to all languages.
|
||||||
| 6 | [Calling Assembly Language From Python](./python/) | [Link](./python/README.pdf) |
|
| 6 | [Calling Assembly Language From Python](./python/) | [Link](./python/README.pdf) |
|
||||||
| 7 | [Atomic Operations](./more/atomics/README.md) | [Link](./more/atomics/README.pdf) |
|
| 7 | [Atomic Operations](./more/atomics/README.md) | [Link](./more/atomics/README.pdf) |
|
||||||
| 8 | [Jump Tables](./more/jump_tables/README.md) | [Link](./more/jump_tables/README.pdf) |
|
| 8 | [Jump Tables](./more/jump_tables/README.md) | [Link](./more/jump_tables/README.pdf) |
|
||||||
|
| 9 | [argv](./more/argv_example/jess1.S) | ASM CODE |
|
||||||
| - | [Debugging Lecture](./debugging/Discourses%20and%20Dialogs%20on%20Debugging.pptx) | PPTX |
|
| - | [Debugging Lecture](./debugging/Discourses%20and%20Dialogs%20on%20Debugging.pptx) | PPTX |
|
||||||
|
|
||||||
## Macro Suite
|
## Macro Suite
|
||||||
|
|
|
||||||
BIN
README.pdf
BIN
README.pdf
Binary file not shown.
156
more/argv_example/apple-linux-convergence.S
Normal file
156
more/argv_example/apple-linux-convergence.S
Normal file
|
|
@ -0,0 +1,156 @@
|
||||||
|
/* Macros to permit the "same" assembly language to build on ARM64
|
||||||
|
Linux systems as well as Apple Silicon systems.
|
||||||
|
|
||||||
|
See the fuller documentation at:
|
||||||
|
https://github.com/pkivolowitz/asm_book/blob/main/macros/README.md
|
||||||
|
|
||||||
|
Perry Kivolowitz
|
||||||
|
A Gentle Introduction to Assembly Language
|
||||||
|
*/
|
||||||
|
|
||||||
|
.macro GLD_PTR xreg, label
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
adrp \xreg, _\label@GOTPAGE
|
||||||
|
ldr \xreg, [\xreg, _\label@GOTPAGEOFF]
|
||||||
|
#else
|
||||||
|
ldr \xreg, =\label
|
||||||
|
ldr \xreg, [\xreg]
|
||||||
|
#endif
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro GLD_ADDR xreg, label // Get a global address
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
adrp \xreg, _\label@GOTPAGE
|
||||||
|
add \xreg, \xreg, _\label@GOTPAGEOFF
|
||||||
|
#else
|
||||||
|
ldr \xreg, =\label
|
||||||
|
#endif
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro LLD_ADDR xreg, label
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
adrp \xreg, \label@PAGE
|
||||||
|
add \xreg, \xreg, \label@PAGEOFF
|
||||||
|
#else
|
||||||
|
ldr \xreg, =\label
|
||||||
|
#endif
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro LLD_DBL xreg, dreg, label
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
adrp \xreg, \label@PAGE
|
||||||
|
add \xreg, \xreg, \label@PAGEOFF
|
||||||
|
ldur \dreg, [\xreg]
|
||||||
|
// fmov \dreg, \xreg
|
||||||
|
#else
|
||||||
|
ldr \xreg, =\label
|
||||||
|
ldur \dreg, [\xreg]
|
||||||
|
#endif
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro LLD_FLT xreg, sreg, label
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
adrp \xreg, \label@PAGE
|
||||||
|
add \xreg, \xreg, \label@PAGEOFF
|
||||||
|
ldur \sreg, [\xreg]
|
||||||
|
#else
|
||||||
|
ldr \xreg, =\label
|
||||||
|
ldur \sreg, [\xreg]
|
||||||
|
#endif
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro GLABEL label
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
.global _\label
|
||||||
|
#else
|
||||||
|
.global \label
|
||||||
|
#endif
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro MAIN
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
_main:
|
||||||
|
#else
|
||||||
|
main:
|
||||||
|
#endif
|
||||||
|
.endm
|
||||||
|
|
||||||
|
/* Fetching the address of the externally defined errno is quite
|
||||||
|
different on Apple and Linux. This macro leaves the address of
|
||||||
|
errno in x0.
|
||||||
|
*/
|
||||||
|
.macro ERRNO_ADDR
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
bl ___error
|
||||||
|
#else
|
||||||
|
bl __errno_location
|
||||||
|
#endif
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro CRT label
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
bl _\label
|
||||||
|
#else
|
||||||
|
bl \label
|
||||||
|
#endif
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro START_PROC // after starting label
|
||||||
|
.cfi_startproc
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro END_PROC // after the return
|
||||||
|
.cfi_endproc
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro PUSH_P a, b
|
||||||
|
stp \a, \b, [sp, -16]!
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro PUSH_R a
|
||||||
|
str \a, [sp, -16]!
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro POP_P a, b
|
||||||
|
ldp \a, \b, [sp], 16
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro POP_R a
|
||||||
|
ldr \a, [sp], 16
|
||||||
|
.endm
|
||||||
|
|
||||||
|
/* The smaller of src_a and src_b is put into dest. A cmp instruction
|
||||||
|
or other instruction that sets the flags must be performed first.
|
||||||
|
This macro makes it easy to remember which register does what in the
|
||||||
|
csel.
|
||||||
|
|
||||||
|
Thank you to u/TNorthover for nudge to add the cmp.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.macro MIN src_a, src_b, dest
|
||||||
|
cmp \src_a, \src_b
|
||||||
|
csel \dest, \src_a, \src_b, LT
|
||||||
|
.endm
|
||||||
|
|
||||||
|
/* The larger of src_a and src_b is put into dest. A cmp instruction
|
||||||
|
or other instruction that sets the flags must be performed first.
|
||||||
|
This macro makes it easy to remember which register does what in the
|
||||||
|
csel.
|
||||||
|
|
||||||
|
Thank you to u/TNorthover for nudge to add the cmp.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.macro MAX src_a, src_b, dest
|
||||||
|
cmp \src_a, \src_b
|
||||||
|
csel \dest, \src_a, \src_b, GT
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro AASCIZ label, string
|
||||||
|
.p2align 2
|
||||||
|
\label: .asciz "\string"
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro MOD src_a, src_b, dest, scratch
|
||||||
|
sdiv \scratch, \src_a, \src_b
|
||||||
|
msub \dest, \scratch, \src_b, \src_a
|
||||||
|
.endm
|
||||||
111
more/argv_example/jess1.S
Normal file
111
more/argv_example/jess1.S
Normal file
|
|
@ -0,0 +1,111 @@
|
||||||
|
#include "apple-linux-convergence.S"
|
||||||
|
|
||||||
|
.p2align 2
|
||||||
|
.text
|
||||||
|
GLABEL main
|
||||||
|
|
||||||
|
/* This program will get a string followed by a double followed by an
|
||||||
|
integer from the command line demonstrating how each of these types
|
||||||
|
can be retrieved.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
./a.out test 29.3 29
|
||||||
|
*/
|
||||||
|
|
||||||
|
MAIN
|
||||||
|
PUSH_P x29, x30
|
||||||
|
mov x29, sp
|
||||||
|
|
||||||
|
// Check argc to see if it is 4. This is not the only way to
|
||||||
|
// validate command line arguments but it is an easy way.
|
||||||
|
cmp w0, 4
|
||||||
|
bne 99f // take branch if argc isn't "right".
|
||||||
|
|
||||||
|
// Skip past argv[0]
|
||||||
|
add x1, x1, 8
|
||||||
|
|
||||||
|
// Fetch argv[1] as a string.
|
||||||
|
// x1 is a pointer to a pointer to chars (i.e. the string).
|
||||||
|
// Being a pointer to a pointer, it must be dereferenced to
|
||||||
|
// make a pointer.
|
||||||
|
ldr x0, [x1] // dereference
|
||||||
|
// Now x0 contains a pointer to the command line argument.
|
||||||
|
// Print the string (as a string). But doing this causes a
|
||||||
|
// function call which will destroy x1. So, save x1 temporarily.
|
||||||
|
// This could be avoided if x1 were moved to a backed up x
|
||||||
|
// register (e.g. x20).
|
||||||
|
PUSH_R x1
|
||||||
|
CRT puts // ptr is in x0 where puts() needs it.
|
||||||
|
POP_R x1
|
||||||
|
|
||||||
|
// Advance x1 once again to get to argv[2] which can be done
|
||||||
|
// in the same instruction as dereferencing it use a
|
||||||
|
// preincrement.
|
||||||
|
ldr x0, [x1, 8]! // dereference
|
||||||
|
|
||||||
|
// Now the string version of argv[2] is now pointed to by x0.
|
||||||
|
// This is exactly where atof would want it. We need atof
|
||||||
|
// because it turns strings into numbers. BUT, same as before,
|
||||||
|
// calling a function would destroy x1 so let's do the same
|
||||||
|
// trick of backing up x1 on the stack and then restoring after
|
||||||
|
// the function call.
|
||||||
|
PUSH_R x1
|
||||||
|
CRT atof // ptr is in x0 where atof() needs it.
|
||||||
|
POP_R x1
|
||||||
|
// The string value will be converted to a double left in d0.
|
||||||
|
// d0 is also a scratch register so for our next call to atoi,
|
||||||
|
// d0 will have to be preserved on the stack - alternatively,
|
||||||
|
// we could have used a high d register backed up and restored
|
||||||
|
// at the start and ending of main().
|
||||||
|
|
||||||
|
// Advance x1 once again to get to argv[3] which can be done
|
||||||
|
// in the same instruction as dereferencing it use a
|
||||||
|
// preincrement.
|
||||||
|
ldr x0, [x1, 8]! // dereference
|
||||||
|
|
||||||
|
// Now the string version of argv[3] is now pointed to by x0.
|
||||||
|
// This is exactly where atoi would want it. We need atoi
|
||||||
|
// because it turns strings into numbers. BUT, same as before,
|
||||||
|
// calling a function would destroy x1 so let's do the same
|
||||||
|
// trick of backing up x1 on the stack and then restoring after
|
||||||
|
// the function call. We must also do the same for d0. Actually,
|
||||||
|
// we won't need argv after this so we will skip backing up x1.
|
||||||
|
|
||||||
|
PUSH_R d0
|
||||||
|
CRT atoi // ptr is in x0 where atof() needs it.
|
||||||
|
POP_R d0
|
||||||
|
// d0 now contains the double.
|
||||||
|
// x0 now contains the integer.
|
||||||
|
// x0 must be copied to x1 because x0 must be a pointer to fmt
|
||||||
|
// for printf to work.
|
||||||
|
mov x1, x0
|
||||||
|
LLD_ADDR x0, fmt
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
sub sp, sp, 16
|
||||||
|
str x1, [sp, 8]
|
||||||
|
str d0, [sp]
|
||||||
|
CRT printf
|
||||||
|
add sp, sp, 16
|
||||||
|
#else
|
||||||
|
bl printf
|
||||||
|
#endif
|
||||||
|
|
||||||
|
99: POP_P x29, x30
|
||||||
|
mov w0, wzr
|
||||||
|
ret
|
||||||
|
|
||||||
|
/* What did we learn?
|
||||||
|
* x1 has argv when main begins.
|
||||||
|
* pointers to the arguments are the contents of argv NOT
|
||||||
|
the actual values. Therefore, x1, which is a pointer (to a pointer),
|
||||||
|
must be dereferenced to get to the actual pointer. In the code,
|
||||||
|
there are three lines with the comment "// dereference".
|
||||||
|
* all command line arguments are c-strings. If that's not what you
|
||||||
|
want, they must be converted - see the code for atoi and atof for
|
||||||
|
examples.
|
||||||
|
*/
|
||||||
|
.data
|
||||||
|
|
||||||
|
fmt: .asciz "double: %f integer: %d\n"
|
||||||
|
|
||||||
|
.end
|
||||||
Binary file not shown.
|
|
@ -17,7 +17,9 @@ sin x = x - x^3/3! + x^5/5! - x^7/7! ...
|
||||||
|
|
||||||
Notice each term flips from addition to subtraction.
|
Notice each term flips from addition to subtraction.
|
||||||
|
|
||||||
Notice each term is based on the odd integers starting at 1.
|
Notice each term is based on the odd integers starting at 1. While the
|
||||||
|
"1" case might look different, it is the same as all the others since
|
||||||
|
1 is just 1 to the first power divided by 1 factorial.
|
||||||
|
|
||||||
## Command line
|
## Command line
|
||||||
|
|
||||||
|
|
@ -29,40 +31,76 @@ arguments are therefore required.
|
||||||
be a double.
|
be a double.
|
||||||
|
|
||||||
* The number of terms to evaluate. The number of terms must lie between
|
* The number of terms to evaluate. The number of terms must lie between
|
||||||
1 and 10 inclusive.
|
1 and 10 inclusive. Note the value of 10 as an upper bound in new. It
|
||||||
|
was 8.
|
||||||
|
|
||||||
## C version
|
## C version
|
||||||
|
|
||||||
To assist your efforts, [here](./c_version.c) is a version of this
|
To assist your efforts, [here](./c_version.c) is a version of this
|
||||||
project written in C.
|
project written in C. This has been updated to print nice debugging
|
||||||
|
output which is not part of the project.
|
||||||
|
|
||||||
## Errors to stderr
|
This C version also demonstrates a different way of calculating the
|
||||||
|
toggle. This version flips the sign of the toggle by multiplying by -1.
|
||||||
Error messages must be sent to `stderr`.
|
The previous version used odd and even values of the term.
|
||||||
|
|
||||||
If you are using the convergence macros to allow your program to build
|
|
||||||
on both Apple Silicon Mac OS and Linux, note the special casing needed
|
|
||||||
to deal with `stderr`. If this is you, compile the C version on Mac OS
|
|
||||||
with the `-S` compiler option to see the generated assembly language and
|
|
||||||
search for `stderr`.
|
|
||||||
|
|
||||||
## Sample executions
|
## Sample executions
|
||||||
|
|
||||||
```text
|
```text
|
||||||
SINE % ./a.out 0 8
|
pk_taylor_series > gcc main.S -o a
|
||||||
The sine of 0.00 degrees is 0.000000 in radians.
|
pk_taylor_series > ./a 0 10
|
||||||
SINE % ./a.out 90 8
|
The sine of 0.00 degrees is 0.00000000.
|
||||||
The sine of 90.00 degrees is 1.000000 in radians.
|
pk_taylor_series > ./a 30 10
|
||||||
SINE % ./a.out 180 8
|
The sine of 30.00 degrees is 0.50000000.
|
||||||
The sine of 180.00 degrees is -0.000001 in radians.
|
pk_taylor_series > ./a 45 10
|
||||||
SINE % ./a.out 180 82
|
The sine of 45.00 degrees is 0.70710678.
|
||||||
|
pk_taylor_series > ./a 90 10
|
||||||
|
The sine of 90.00 degrees is 1.00000000.
|
||||||
|
pk_taylor_series > ./a 180 10
|
||||||
|
The sine of 180.00 degrees is -0.00000000.
|
||||||
|
pk_taylor_series > ./a 360 10
|
||||||
|
The sine of 360.00 degrees is -0.00104818.
|
||||||
|
pk_taylor_series > ./a 360 100
|
||||||
Number of terms is out of range.
|
Number of terms is out of range.
|
||||||
SINE % ./a.out 180 -10
|
pk_taylor_series > ./a 360 -1
|
||||||
Number of terms is out of range.
|
Number of terms is out of range.
|
||||||
SINE % echo $?
|
pk_taylor_series >
|
||||||
1
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Floating point instructions I used
|
||||||
|
|
||||||
|
These are the floating point instructions I used in my implementation.
|
||||||
|
|
||||||
|
* fmov
|
||||||
|
|
||||||
|
* scvtf
|
||||||
|
|
||||||
|
* fmul
|
||||||
|
|
||||||
|
* fdiv
|
||||||
|
|
||||||
|
* fadd
|
||||||
|
|
||||||
|
## How I broke up the program
|
||||||
|
|
||||||
|
I have functions named:
|
||||||
|
|
||||||
|
* main
|
||||||
|
|
||||||
|
* HandleOptions
|
||||||
|
|
||||||
|
* Factorial
|
||||||
|
|
||||||
|
* IntegerPower - x to the nth power
|
||||||
|
|
||||||
|
* ComputeSine - The main calculation
|
||||||
|
|
||||||
|
* PrintAnswer
|
||||||
|
|
||||||
|
* ConvertTheta - Wrap D2R
|
||||||
|
|
||||||
|
* D2R - Degrees to radians
|
||||||
|
|
||||||
## CSC3510
|
## CSC3510
|
||||||
|
|
||||||
The following applies to Carthage College CSC3510 students.
|
The following applies to Carthage College CSC3510 students.
|
||||||
|
|
@ -74,4 +112,3 @@ Work is to be done solo.
|
||||||
### What to hand in
|
### What to hand in
|
||||||
|
|
||||||
Just the .S file. **Your name must be at the top of the file.**
|
Just the .S file. **Your name must be at the top of the file.**
|
||||||
|
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -1,13 +1,14 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
double pi = 3.14159265359;
|
double pi = 3.14159265358979323846;
|
||||||
|
|
||||||
double D2R(double d) {
|
double D2R(double d) {
|
||||||
return d * pi / 180.0;
|
return d * pi / 180.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
long Factorial(int n) {
|
double Factorial(int n) {
|
||||||
long retval = 1;
|
long retval = 1;
|
||||||
|
|
||||||
if (n > 0) {
|
if (n > 0) {
|
||||||
|
|
@ -15,7 +16,7 @@ long Factorial(int n) {
|
||||||
retval = retval * n--;
|
retval = retval * n--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return retval;
|
return (double) retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
double IntegerPower(double b, int e) {
|
double IntegerPower(double b, int e) {
|
||||||
|
|
@ -48,20 +49,20 @@ int main(int argc, char ** argv) {
|
||||||
|
|
||||||
double r_angle = D2R(angle);
|
double r_angle = D2R(angle);
|
||||||
|
|
||||||
|
double toggle = 1.0;
|
||||||
for (int term = 0, base = 1; term < terms; term++, base += 2) {
|
for (int term = 0, base = 1; term < terms; term++, base += 2) {
|
||||||
double toggle = (term & 1) ? -1.0 : 1.0;
|
if (toggle > 0) {
|
||||||
|
printf("%+03.8e + %+03.8e / %+03.8e [term %2d is %+03.8e]\n", sin, IntegerPower(r_angle, base),
|
||||||
|
Factorial(base), term + 1, toggle * IntegerPower(r_angle, base) / Factorial(base));
|
||||||
|
} else {
|
||||||
|
printf("%+03.8e - %+03.8e / %+03.8e [term %2d is %+03.8e]\n", sin, IntegerPower(r_angle, base),
|
||||||
|
Factorial(base), term + 1, toggle * IntegerPower(r_angle, base) / Factorial(base));
|
||||||
|
}
|
||||||
sin += toggle *
|
sin += toggle *
|
||||||
IntegerPower(r_angle, base) / Factorial(base);
|
IntegerPower(r_angle, base) / Factorial(base);
|
||||||
/*
|
toggle = toggle * -1;
|
||||||
if (toggle > 0) {
|
|
||||||
printf("adding %d p/b intermediate: %f\n", base, sin);
|
|
||||||
} else {
|
|
||||||
printf("subtracting %d p/b intermediate: %f\n", base, sin);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
printf("The sine of %.2f degrees is %f in radians.\n", angle, sin);
|
printf("The sine of %0.4f degrees is %0.10f.\n", angle, sin);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
Loading…
Reference in a new issue