Nucleo 8S208RB インラインアセンブラを使う

# 変数の受け渡し

```c:main.c（抜粋）
#define MY_DELAY                         \
  for (uint32_t i = 0; i < 1000000; i++) \
  {                                      \
  }

void main(void)
{
  UART1_DeInit();
  UART1_Init(
      (uint32_t)9600,
      UART1_WORDLENGTH_8D,
      UART1_STOPBITS_1,
      UART1_PARITY_NO,
      UART1_SYNCMODE_CLOCK_DISABLE,
      UART1_MODE_TXRX_ENABLE);

  __IO uint8_t data=0;

  while (1)
  {
    printf("Hello STM8S\n");
    MY_DELAY;
    disableInterrupts();
    (void) data; // 変数 data の値をスタックに積む
    __asm
        inc (1, sp)
    __endasm;
    enableInterrupts();
    printf("data %d\n", data);
    MY_DELAY;
  }
}
```


```実行例
--- Terminal on /dev/cu.usbmodem14203 | 9600 8-N-1
--- Available filters and text transformations: colorize, debug, default, direct, hexlify, log2file, nocontrol, printable, send_on_enter, time
--- More details at https://bit.ly/pio-monitor-filters
--- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H
data 1
Hello STM8S
data 2
Hello STM8S
data 3
Hello STM8S
data 4
Hello STM8S
data 5
Hello STM8S
data 6
```

変数 `data` をアセンブラに渡し，それを1増やしてもらうものです。

参考：

https://github.com/lujji/stm8-bare-min/blob/master/stm8s/examples/ASM/main.c

- `asm()` 関数を使う手段もあるようですが（それは使いこなせていない），ここでは `__asm` と `__endasm;` で囲みます。
- UARTで割り込みを使っているのか，アセンブラの前後でいったん禁止しないとフリーズします。とりあえず割り込み禁止を前提にします。
- `__asm` の前に `(void) 変数名`を書くと，書かれた変数の値がスタックに積まれ，アセンブラ内で参照できます。さらにそのスタックをアセンブラ内で更新すれば，アセンブラから戻ってきたあとで変数が更新されています。
- というわけで，更新に使う変数は `__IO` つまり volatile 宣言が必要になります。
- `__asm` の前に変数を書いた順番にスタックに積まれます。


使うレジスターをあらかじめ `push` します。その分受け渡しの場所が変わります。

16ビットをスタックに積むと，当然SPは2変わります

```c:main.c（抜粋）
  __IO uint16_t data1=0x1234;
  __IO uint16_t data2=0;

  while (1)
  {
    printf("Hello STM8S\n");
    MY_DELAY;
    disableInterrupts();
    (void) data1;
    (void) data2;
    __asm
        pushw x // (#1, sp) <-x (#3, sp) <- data1
        ldw x, (#3, sp) // x <- data1
        incw x
        ldw (#3, sp), x
        popw x
    __endasm;
    enableInterrupts();
    printf("data1 %x\n", data1);
    printf("data2 %x\n", data2);
    MY_DELAY;
  }
```

実行例
```
Hello STM8S
data1 1235
data2 0
Hello STM8S
data1 1236
data2 0
Hello STM8S
```

# GPIOにアクセスする

```c:main.c（抜粋）
  while (1)
  {
    volatile unsigned char *my_odr = &GPIOC->ODR;
    __IO uint16_t odr_address = (uint16_t)my_odr;
    __IO uint8_t pin_number = GPIO_PIN_5;

    disableInterrupts();
    (void)odr_address; // to 1st of stack
    (void)pin_number;  // to 2nd of stack
    __asm
      push a
      pushw x // sp = [1]x [3]a [4]odr_address [6]pin_number
      ldw x, (#4, sp) // x <- odr_address
      ld a, (x) // a <- [odr_address]
      xor a, (#6, sp) // a <- a xor pin_number
      ld (x), a // [odr_address] <- a
      popw x
      pop a
    __endasm;
    enableInterrupts();

    MY_DELAY;
  }
```

NucleoではLEDが点滅します。

## ペリフェラルのレジスタアドレスを引き渡す

ベタ書きは面白くないので，以前の知識で `(uint16_t)&GPIO->ODR` としてみましたが，エラーとなります。いろいろ試行錯誤して，2行にはなりましたが，やっと引き渡すことができました。

`__asm` の前の行で `(void)定数` とかしたいところですが，ここでは変数（のポインタ）しか受け付けてくれません。armのアセンブラとは違うのですね...

# ループする

```c:main.c（抜粋）
    volatile unsigned char *my_odr = &GPIOC->ODR;
    __IO uint16_t odr_address = (uint16_t)my_odr;
    __IO uint8_t pin_number = GPIO_PIN_5;
    
    disableInterrupts();
    (void)odr_address; // to 1st of stack
    (void)pin_number;  // to 2nd of stack
    __asm
      push a
      pushw x // sp = [1]x [3]a [4]odr_address [6]pin_number
      ldw x, (#4, sp) // let x <- odr_address
      ld a, (x) // let a <- [odr]
      or a, (#6, sp) // a <- [odr] or pin_number
      ld (x), a // [odr] <- [odr] or pin_number
      pushw y  // blink loop
      ldw y, #0xffff
      99999$:
        decw y
        jrne 99999$ // JR not equal 0
      popw y // loop end
      neg (#6, sp) // pin_number <- not(pin_number)
      dec (#6, sp)
      ld a, (x) // a <- [odr]
      and a, (#6, sp) 
      ld (x), a // [odr] = [odr] and not(pin_number)
      popw x
      pop a
    __endasm;
    enableInterrupts();
```

PC5をHにして，少しループしてLに戻します。
ループするためのラベルは数値5桁 + `$:` で定義し，数値5桁 + `$` で飛びます。
`neg`は`not`とは異なり2の補数なので，反転させるために`dec`してます。

# おまけ

buildすると `プロジェクトフォルダー - .pio - build - nucleo_8s208rb - src - main.asm` ができます。すごく勉強になります。アセンブラでエラーが出たときもここを見て対処します。