Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .github/workflows/gen-pdf.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
name: Generate PDF using Pandoc (Docker)

on:
push:
branches:
- master
workflow_dispatch: null

jobs:
create_pdf:
runs-on: ubuntu-latest
container:
image: pandoc/latex:3
steps:
- uses: actions/checkout@v7

- uses: docker://pandoc/latex:3
timeout-minutes: 10
with:
entrypoint: './pandoc-docker.sh'

- id: short_sha

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would not generate it at each possible commit/PR. I think the best way will be to build PDF by tags (I will push one right after we will merge the PR) but also leave sha-based builds but only for manual builds (based on workflow_dispatch).

Something like this (I did not test it although by myself yet):

---
name: Generate PDF using Pandoc

on:
  push:
    tags:
      - 'v*'
  workflow_dispatch:

jobs:
  create_pdf:
    runs-on: ubuntu-latest

    permissions:
      contents: write

    container:
      image: pandoc/latex:3

    steps:
      - uses: actions/checkout@v7

      - uses: docker://pandoc/latex:3
        timeout-minutes: 10
        with:
          entrypoint: './pandoc-docker.sh'

      - id: release_meta
        shell: bash
        run: |
          short_sha="${GITHUB_SHA::7}"

          if [[ "$GITHUB_EVENT_NAME" == "workflow_dispatch" ]]; then
            echo "tag_name=dev-${short_sha}" >> "$GITHUB_OUTPUT"
            echo "prerelease=true" >> "$GITHUB_OUTPUT"
            echo "make_latest=false" >> "$GITHUB_OUTPUT"
          else
            echo "tag_name=${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT"
            echo "prerelease=false" >> "$GITHUB_OUTPUT"
            echo "make_latest=true" >> "$GITHUB_OUTPUT"
          fi

      - uses: softprops/action-gh-release@v3
        with:
          tag_name: ${{ steps.release_meta.outputs.tag_name }}
          target_commitish: ${{ github.sha }}
          draft: false
          prerelease: ${{ steps.release_meta.outputs.prerelease }}
          make_latest: ${{ steps.release_meta.outputs.make_latest }}
          fail_on_unmatched_files: true
          files: |
            *.pdf

@rcalixte what do you think?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see what you mean and I can work to implement this.

env:
GITHUB_SHA: ${{ github.sha }}
run: echo "short_sha=${GITHUB_SHA::7}" >> $GITHUB_OUTPUT

- uses: softprops/action-gh-release@v3
with:
tag_name: version-${{ steps.short_sha.outputs.short_sha }}
draft: false
files: |
*.pdf
11 changes: 6 additions & 5 deletions content/asm_2.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
Some days ago I wrote the first post about [Introduction to assembly](asm_1.md) which, to my surprise, caused great interest:

![newscombinator](./assets/newscombinator-screenshot.png)

![reddit](./assets/reddit-screenshot.png)

It motivated me to continue describing my journey through learning assembly programming for Linux x86_64. During these days I got great feedback from people all over the Internet. There were many words of gratitude, but, what is more important to me, there was also much adequate advice and very useful criticism. Especially, I want to say thank you for the great feedback to:
Expand Down Expand Up @@ -59,7 +60,7 @@ You can find a detailed description of registers in the [Intel software develope

There are 16 registers of 64 bits size, from `rax` to `r15`. Each register also has smaller parts with their own names. For example, as we may see in the table above, the lower 32 bits of the `rax` register are called `eax`. Similarly, the lower 16 bits of the `eax` register are called `ax`. Finally, the lower 8 bits of the `ax` register are called `al`, while the higher 8 bits are called `ah`. We can visualize this as:

![rax](./assets/rax.svg)
![rax](./assets/rax.png)

The general purpose registers are used in many different cases, like performing arithmetic and logical operations, transferring data, memory address calculation operations, passing parameters to functions and system calls, and many more. When going through these chapters, we will see how to use the general purpose registers to perform different operations.

Expand Down Expand Up @@ -312,15 +313,15 @@ These two instructions at the beginning of each function are called [function pr

Now that we know the rough meaning of the stack frame and the usage of the `rbp`, `rsp`, and `rip` registers, let's try to understand what happens when we call a function. Let's look at the stack before the `call foo` is executed. Our stack looks like this:

![stack-before-call](./assets/stack-before-call.svg)
![stack-before-call](./assets/stack-before-call.png)

After executing the `call` instruction, the return address (or address of the next instruction) is pushed to the stack. So our stack layout during the call looks like this:

![stack-during-call](./assets/stack-during-call.svg)
![stack-during-call](./assets/stack-during-call.png)

At the beginning of every new function, we must preserve the current value of the `rbp` register by pushing it onto the stack. This value acts as a base pointer of the previous function. In other words, the value of the `rbp` at the beginning of each function represents the address of the bottom (or the base) of the caller's stack. Since we are in a new function, it needs a new stack frame and, as a result, a new base. At this point, the stack layout looks like this:

![stack-preserve-bp](./assets/stack-preserve-bp.svg)
![stack-preserve-bp](./assets/stack-preserve-bp.png)

The next step is to put the current value of the stack pointer (`rsp`) into the `rbp` register. This marks the beginning of the new stack frame for our function `foo`. With the stack frame ready, we can start managing function parameters and local variables.

Expand All @@ -334,7 +335,7 @@ Let's read once again the sentence from the paragraph above:

What was the address stored in the `rbp` register? Our stack pointer! So after running the last `mov` instruction in the function `foo`, our stack frame looks like this:

![stack](./assets/stack.svg)
![stack](./assets/stack.png)

That is the whole point of the `rbp` register. It plays the role of an anchor or a base point in the function. Using the positive offsets, we can access the return address and parameters pushed onto the stack by the caller. On the other hand, negative offsets allow us to access local variables of the current function.

Expand Down
6 changes: 3 additions & 3 deletions content/asm_3.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,11 @@ __double(int):

After the first two lines of the `__double` function, the stack frame for this function is set and looks like this:

![asm-3-stack-fram-of__double-1](./assets/asm-3-stack-of__double-1.svg)
![asm-3-stack-fram-of__double-1](./assets/asm-3-stack-of__double-1.png)

The third instruction of the `__double` function places its first parameter to the stack with an offset of `-20`. Next, the value `2`, representing the local variable two, is also stored on the stack with an offset of `-4`. At this point, the stack frame of our function looks like this:

![asm-3-stack-fram-of__double-2](./assets/asm-3-stack-of__double-2.svg)
![asm-3-stack-fram-of__double-2](./assets/asm-3-stack-of__double-2.png)

Finally, we put the value from the stack at offset `-20` (the value of the function's parameter) into the `eax` register and multiply it by `2`, which is located on the stack at offset `-4`. The result of the multiplication is then stored in the `eax` register. This simple example shows how the stack is used to access both parameters and local variables of a function.

Expand Down Expand Up @@ -353,7 +353,7 @@ Before calculating the sum of two numbers from the command-line arguments, we ne

According to the table above, the command-line arguments are located on the stack like this:

![asm-3-args-on-stack](./assets/asm-3-args-on-stack.svg)
![asm-3-args-on-stack](./assets/asm-3-args-on-stack.png)

As we can see, the number of command-line arguments passed to the program is stored at the top of the stack, with the `rsp` register pointing to it. Fetching this value from the stack gives us the number of arguments. Additionally, we already know the `cmp` instruction, which allows us to compare two values. Using this knowledge, we can perform the first check in our program — verifying that the program got two arguments from the command-line or printing an error message otherwise:

Expand Down
Binary file added content/assets/asm-3-args-on-stack.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added content/assets/asm-3-stack-of__double-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added content/assets/asm-3-stack-of__double-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added content/assets/rax.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added content/assets/stack-before-call.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added content/assets/stack-during-call.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added content/assets/stack-preserve-bp.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added content/assets/stack.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
36 changes: 36 additions & 0 deletions include.tex

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move this and all related scripts below, pandoc configuration and so on to ./scripts/pdf directory or something like this?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be able to move things around. I'll include this in the update as well.

Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
\usepackage{enumitem}
\ifcsundef{c@none}{\newcounter{none}}{}
\setlistdepth{9}

\setlist[itemize,1]{label=$\bullet$}
\setlist[itemize,2]{label=$\bullet$}
\setlist[itemize,3]{label=$\bullet$}
\setlist[itemize,4]{label=$\bullet$}
\setlist[itemize,5]{label=$\bullet$}
\setlist[itemize,6]{label=$\bullet$}
\setlist[itemize,7]{label=$\bullet$}
\setlist[itemize,8]{label=$\bullet$}
\setlist[itemize,9]{label=$\bullet$}
\renewlist{itemize}{itemize}{9}

\setlist[enumerate,1]{label=$\arabic*.$}
\setlist[enumerate,2]{label=$\alph*.$}
\setlist[enumerate,3]{label=$\roman*.$}
\setlist[enumerate,4]{label=$\arabic*.$}
\setlist[enumerate,5]{label=$\alpha*$}
\setlist[enumerate,6]{label=$\roman*.$}
\setlist[enumerate,7]{label=$\arabic*.$}
\setlist[enumerate,8]{label=$\alph*.$}
\setlist[enumerate,9]{label=$\roman*.$}
\renewlist{enumerate}{enumerate}{9}

\usepackage{float}
\makeatletter
\let\origfigure\figure
\let\endorigfigure\endfigure
\renewenvironment{figure}[1][2]{%
\expandafter\origfigure\expandafter[H]%
}{%
\endorigfigure
}
\makeatother
30 changes: 30 additions & 0 deletions pandoc-docker.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#! /bin/sh

# Install dependencies for successful PDF generation
for i in 1 2 3 4 5; do
tlmgr update --self && break
echo "tlmgr attempt $i failed, retrying in 5s..."
sleep 5
done

for i in 1 2 3 4 5; do
tlmgr install collection-fontsrecommended ctex enumitem float \
greek-fontenc koma-script polyglossia realscripts xltxtra && break
echo "tlmgr install attempt $i failed, retrying in 5s..."
sleep 5
done

# Generate PDF using pandoc
for filename in pandoc-*yaml; do
# Create variable for language based on filename
language=$(echo "${filename}" | cut -d'.' -f1 | cut -d'-' -f2-3)

# Attempt to create the PDF
echo "Generating ${language} PDF..."
if pandoc -d "${filename}"; then
echo "Success! The ${language} PDF has been successfully created!"
else
echo "Failure! The ${language} PDF failed to be created!"
exit 1
fi
done
37 changes: 37 additions & 0 deletions pandoc-en-US.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
metadata:
title: Introduction to the Assembly Programming Language
author: Alex Kuleshov
category: "License: Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License"
keywords:
- "assembly"
- "introduction"
standalone: true
variables:
documentclass: scrbook
lang: en-US
links-as-notes: true
lot: false
lof: false
margin-top: 1.27cm
margin-left: .635cm
margin-right: .635cm
margin-bottom: 1.27cm
table-of-contents: true
toc-depth: 2
file-scope: true
from: markdown+rebase_relative_paths
include-in-header: include.tex
verbosity: ERROR
pdf-engine: xelatex
template: unicode.latex
input-files:
- ./content/asm_1.md
- ./content/asm_2.md
- ./content/asm_3.md
- ./content/asm_4.md
- ./content/asm_5.md
- ./content/asm_6.md
- ./content/asm_7.md
output-file: introduction-to-assembly.pdf
...
35 changes: 35 additions & 0 deletions pandoc.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#! /bin/bash

# Generate PDF using pandoc
generate_pdfs() {
for filename in pandoc-*yaml; do
# Create variable for language based on filename
IFS=- read _ language <<< "${filename}"
language=${language/.yaml/}

# Attempt to create the PDF
echo "Generating ${language} PDF..."
if pandoc -d "${filename}"; then
echo "Success! The ${language} PDF has been successfully created!"
else
echo "Failure! The ${language} PDF failed to be created!"
exit 1
fi
done
}

# Check for dependencies
check_dependencies () {
for dependency in "${dependencies[@]}"
do
if ! [ -x "$(command -v $dependency)" ]; then
echo "Error: $dependency is not installed." >&2
exit 1
fi
done
}

dependencies=("pandoc" "tex")

check_dependencies
generate_pdfs
Loading
Loading