Russian Caesar cipher
JavaScript (ES6),  148 ... 110  108 bytes
Saved 12 bytes thanks to @Grimy!
This function applies some maths to the code points.
s=&gt;s.replace(/./g,s=&gt;String.fromCharCode(...(n=s.charCodeAt()-304)%80-1?[(n^16*(n&gt;0))+304]:[1060+n%48,776]))

Try it online!
How?
For each character in the input string, we define \$n\$ as its code point minus \$304\$.
 characters    | code points  | n
---------------+--------------+---------------
 А to Я        | 1040 to 1071 | 736 to 767
 а to я        | 1072 to 1103 | 768 to 799
 Ё             | 1025         | 721
 ё             | 1105         | 801
 ASCII         | 0 to 126     | -304 to -178

Then we apply the following logic:
// neither 'Ё' nor 'ё'?
n % 80 - 1 ?
  // output a single code point
  [
    // invert the case if this is a Cyrillic character
    (n ^ 16 * (n &gt; 0))
    // and restore the original offset
    + 304
  ]
:
  // output two code points
  [
    // the first one is either 1061 (Х) or 1093 (х)
    1060 + n % 48,
    // the 2nd one is the combining diaeresis
    776
  ]