anchor.diff:  
   : bit ( u -- u )  \ turn a bit position into a single-bit mask
     1 swap lshift  1-foldable ;
  +: bit! ( mask addr f -- )  \ set or clear specified bit(s)
  +  if bis! else bic! then ;
   
   : io ( port# pin# -- pin )  \ combine port and pin into single int
     swap 8 lshift or  2-foldable ;
  
positive.code: : iox! ( pin -- )  \ toggle pin
    dup io@ 0= swap io! ;
  
  %0000 constant IMODE-ADC    \ input, analog
  %0100 constant IMODE-FLOAT  \ input, floating
  %1000 constant IMODE-PULL   \ input, pull-up/down
  
  %0001 constant OMODE-PP     \ output, push-pull
  %0101 constant OMODE-OD     \ output, open drain
  %1001 constant OMODE-AF-PP  \ alternate function, push-pull
  %1101 constant OMODE-AF-OD  \ alternate function, open drain
  
    %01 constant OMODE-SLOW   \ add to OMODE-* for 2 MHz iso 10 MHz drive
    %10 constant OMODE-FAST   \ add to OMODE-* for 50 MHz iso 10 MHz drive
  
  : io-mode! ( mode pin -- )  \ set the CNF and MODE bits for a pin
    dup io-base GPIO.MODER + over 8 and shr + >r ( R: crl/crh )
    io# 7 and 4 * ( mode shift )
    $F over lshift not ( mode shift mask )
    r@ @ and -rot lshift or r> ! ;
  
  : io. ( pin -- )  \ display readable GPIO registers associated with a pin
    cr
  
negative.code:   8 rshift  1-foldable ;
  : io-base ( pin -- addr )  \ convert pin to GPIO base address
    $F00 and 2 lshift GPIO-BASE +  1-foldable ;
  : io@ ( pin -- u )  \ get pin value (0 or -1)
    dup io-base GPIO.IDR + @ swap io# rshift 1 and negate ;
  : ios! ( pin -- )  \ set pin to high
    dup io-mask swap io-base GPIO.BSRR + ! ;
  : ioc! ( pin -- )  \ clear pin to low
  