Warum immer so kompliziert?
Die klassische MOVE-Anweisung regelt das automatisch und bei fehlerhaften Ziffern einfach ein Monitor drumrum:

D MYNumber 31p 0
D MyChar 30

// Woherauch immer der Wert kommt
evalr MyChar = WasAuchImmer;
monitor;
C MOVE(P) MyChar MyNumber
on-error *all;
// Fehlerbehandlung
endmon;

Der Move überträgt rechtsbündig in das Zielfeld. Ziffern werden konvertiert und vom rechten Zeichen wird die linke Tetrade (C, D, E, F) als Vorzeichen und die Rechte Tetrade (0-9) als Ziffer.
So haben wir seit gefühlten 100 Jahren konvertiert.
Die Kommaausrichtung kann dann wie gehabt erfolgen.

Und wofür CharToHex?
Ein einfaches %Bitand(Char:'0') ergibt eben x'F0' = '0'.