This commit is contained in:
Perry Kivolowitz 2023-04-20 11:03:32 -05:00
commit 43014fa728
10 changed files with 342 additions and 36 deletions

View file

@ -324,6 +324,7 @@ contained therein are applicable to all languages.
| 6 | [Calling Assembly Language From Python](./python/) | [Link](./python/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) |
| 9 | [argv](./more/argv_example/jess1.S) | ASM CODE |
| - | [Debugging Lecture](./debugging/Discourses%20and%20Dialogs%20on%20Debugging.pptx) | PPTX |
## Macro Suite

Binary file not shown.

View 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
View 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.

View file

@ -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 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
@ -29,40 +31,76 @@ arguments are therefore required.
be a double.
* 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
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
Error messages must be sent to `stderr`.
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`.
This C version also demonstrates a different way of calculating the
toggle. This version flips the sign of the toggle by multiplying by -1.
The previous version used odd and even values of the term.
## Sample executions
```text
SINE % ./a.out 0 8
The sine of 0.00 degrees is 0.000000 in radians.
SINE % ./a.out 90 8
The sine of 90.00 degrees is 1.000000 in radians.
SINE % ./a.out 180 8
The sine of 180.00 degrees is -0.000001 in radians.
SINE % ./a.out 180 82
pk_taylor_series > gcc main.S -o a
pk_taylor_series > ./a 0 10
The sine of 0.00 degrees is 0.00000000.
pk_taylor_series > ./a 30 10
The sine of 30.00 degrees is 0.50000000.
pk_taylor_series > ./a 45 10
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.
SINE % ./a.out 180 -10
pk_taylor_series > ./a 360 -1
Number of terms is out of range.
SINE % echo $?
1
pk_taylor_series >
```
## 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
The following applies to Carthage College CSC3510 students.
@ -74,4 +112,3 @@ Work is to be done solo.
### What to hand in
Just the .S file. **Your name must be at the top of the file.**

Binary file not shown.

View file

@ -1,13 +1,14 @@
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
double pi = 3.14159265359;
double pi = 3.14159265358979323846;
double D2R(double d) {
return d * pi / 180.0;
}
long Factorial(int n) {
double Factorial(int n) {
long retval = 1;
if (n > 0) {
@ -15,7 +16,7 @@ long Factorial(int n) {
retval = retval * n--;
}
}
return retval;
return (double) retval;
}
double IntegerPower(double b, int e) {
@ -48,20 +49,20 @@ int main(int argc, char ** argv) {
double r_angle = D2R(angle);
double toggle = 1.0;
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 *
IntegerPower(r_angle, base) / Factorial(base);
/*
if (toggle > 0) {
printf("adding %d p/b intermediate: %f\n", base, sin);
} else {
printf("subtracting %d p/b intermediate: %f\n", base, sin);
}
*/
toggle = toggle * -1;
}
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;
}