A base 40 message encoder
This is a compact representation for strings that can consist of uppercase letters, digits, space, comma, period, and trailing NUL. There's a Python implementation of the encoder and a 6800 implementation of the decoder.
b40.asm
cpu 6800
include "motorola.inc" ; Macros for things like fcc,db, etc.
CRXMIT EQU $8000 + 3 * 4
RESET EQU $8000 + 3 * 0
ORG 0
b40_j: RMB 1
b40_acc: RMB 3
b40_tmp: RMB 3
b40_cb: jmp CRXMIT
ORG $0100
entry:
ldx #encoded
jsr b40print
jmp reset
encoded: fcb 137, 98, 164, 124, 235, 2, 162, 186, 32, 248, 46, 30, 20, 217, 0
ORG $0200
b40scl: ; shift `b40acc << b` in place
asl b40_acc+2
rol b40_acc+1
rol b40_acc
deca
bne b40scl
b40done:
rts
; Call with data pointer in X.
b40print:
ldab #3
ldaa 0, x
staa b40_acc+1
inx
ldaa 0, x
staa b40_acc+2
inx
b40step:
clr b40_acc
ldaa #3
bsr b40scl
lda b40_acc
sta b40_tmp
lda b40_acc+1
sta b40_tmp+1
lda b40_acc+2
sta b40_tmp+2
; now b40_tmp = b40_acc = 8 * b40_acc(original)
ldaa #2
bsr b40scl
; now b40_acc = b40_acc(original) * 32
ldaa b40_tmp+2
adda b40_acc+2
staa b40_acc+2
ldaa b40_tmp+1
adca b40_acc+1
staa b40_acc+1
ldaa b40_tmp+0
adca b40_acc+0
staa b40_acc+0
; now b40_acc = b40_acc(original) * 40
; and a has the most significant byte of b40_acc
beq b40done
cmpa #3
ble b40sp
cmpa #12
ble b40dig
adda #8 ; alphabetic
b40dig: adda #12 ; digit
b40sp: adda #31 ; symbol
jsr b40_cb
dec b
bne b40step
bra b40print
b40.py
import string
class B40:
cset = "".join(sorted("\0" + string.digits + string.ascii_uppercase + " .,"))
assert len(cset) == 40
def dec(self, bytes):
i = 0
j = 0
while True:
if j == 0:
d = bytes[i] * 256
if i + 1 < len(bytes):
d += bytes[i+1]
print(f"[DEC] {d: 5}")
i += 2
j = 3
print(d, end=" ")
d = d * 40 # 24 bit temporary result
top = d >> 16
d = d & 0xffff
print(d, top, end=" ")
if top == 0:
break
c = self.cset[top]
print(repr(c))
yield c
j -= 1
print()
def enc(self, message):
message = [self.cset.find(c) for c in message.upper()]
assert not any(i==-1 for i in message)
print(len(message))
while len(message) % 3:
message.append(0)
for i in range(0, len(message), 3):
trio = message[i:i+3][::-1]
d = 0
for c in trio:
d = (d + (c << 16) + 39) // 40
print(f"{c:2} {repr(self.cset[c]):5}", f"{d:5d}", end=" ")
print(f" {d: 5}")
assert 0 <= d < 65536
yield d >> 8
yield d & 0xff
yield 0
b40 = B40()
encoded = list(b40.enc("hello world 12,345.67"))
print(encoded)
decoded = list(b40.dec(encoded))
print(decoded)
print(repr(b40.cset))