RosettaCodeData/Task/Morse-code/8086-Assembly/morse-code.8086

193 lines
5.3 KiB
Plaintext

cpu 8086
bits 16
;;; I/O ports
KBB: equ 61h ; Keyboard controller port B (also controls speaker)
PITC2: equ 42h ; Programmable Interrupt Timer, channel 2 (frequency)
PITCTL: equ 43h ; PIT control port.
;;; Control bits
SPKR: equ 3 ; Lower two bits of KBB determine speaker on/off
CTR: equ 6 ; Counter select offset in PIT control byte
CBITS: equ 4 ; Size select offset in PIT control byte
B16: equ 3 ; 16-bit mode for the PIT counter
MODE: equ 1 ; Offset of mode in PIT control byte
SQWV: equ 3 ; Square wave mode
;;; Software interrupts
CLOCK: equ 1Ah ; BIOS clock function interrupt
DOS: equ 21h ; MS-DOS syscall interrupt
;;; MS-DOS syscalls
read: equ 3Fh ; Read from file
section .text
org 100h
;;; Set up the PIT to generate a 'C' note
cli
mov al,(2<<CTR)|(B16<<CBITS)|(SQWV<<MODE)
out PITCTL,al
mov ax,2280 ; Divisor of main oscillator frequency
out PITC2,al ; Output low byte first,
xchg al,ah
out PITC2,al ; Then high byte.
sti
;;; Read from stdin and sound as morse
input: mov ah,read ; Read
xor bx,bx ; from STDIN
mov cx,buf.size ; into the buffer
mov dx,buf
int DOS
jc stop ; Carry set = error, stop
test ax,ax ; Did we get any characters?
jnz go ; If not, we're done, so stop
stop: ret
go: mov cx,ax ; Loop counter = how many characters we have
mov si,buf ; Start at buffer size
dochar: lodsb ; Get current character
cmp al,26 ; End of file?
je stop ; Then stop
push cx ; Save pointer and counter
push si
call char ; Sound out this character
pop si ; Restore pointer and counter
pop cx
loop dochar ; If more characters, do the next one
jmp input ; Afterwards, try to get more input
;;; Sound ASCII character in AL
char: and al,127 ; 7-bit ASCII
sub al,32 ; Word separator? (<=32)
ja .snd ; If not, look up in table
mov bx,4 ; Otherwise, 'sound' a word space
jmp delay ; (4 more ticks to form 7-tick delay)
.snd: mov bx,morse ; Find offset in morse pulse table
xlatb
test al,al ; If it is zero, we want to ignore it
jz .out
xor ah,ah ; Otherwise, find its address
mov bx,ax
lea si,[bx+morse.P] ; and store it in SI.
xor bh,bh ; BX = BL in the following code
.byte: lodsb ; Get pulse byte
mov cx,4 ; Four pulses per byte
.pulse: mov bl,3 ; Load low pulse in BL
and bl,al
test bl,bl ; If it is zero,
jz .out ; We're done
mov bp,ax ; Otherwise, keep AX
call pulse ; Sound the pulse
mov ax,bp ; Restore AX
shr al,1 ; Next pulse
shr al,1
loop .pulse
jmp .byte ; If no zero pulse seen yet, next byte
.out: mov bl,2 ; 2 ticks more delay to form inter-char space
jmp delay
;;; Sound morse code pulse w/delay
pulse: cli ; Turn off interrupts
in al,KBB ; Read current configuration
or al,SPKR ; Turn speaker on
out KBB,al ; Write configuration back
sti ; Turn interrupts back on
call delay ; Delay for BX ticks
cli ; Turn off interrupts
in al,KBB ; Read current configuration
and al,~SPKR ; Turn speaker off
out KBB,al ; Write configuration back
sti ; Turn interrupts back on
mov bx,1 ; Intra-character delay = 1 tick
;;; Delay for BX ticks
delay: push bx ; Keep the registers we alter
push cx
push dx
xor ah,ah ; Clock function 0 = get ticks
int CLOCK ; Get current ticks (in CX:DX)
add bx,dx ; BX = time for which to wait
.wait: int CLOCK ; Wait for that time to occur
cmp dx,bx ; Are we there yet?
jbe .wait ; If not, try again
pop dx ; Restore the registers
pop cx
pop bx
ret
section .data
morse: ;;; Printable ASCII to pulse mapping (32-122)
db .n_-.P, .excl-.P, .dquot-.P, .n_-.P ; !"#
db .dolar-.P, .n_-.P, .amp-.P, .quot-.P;$%&'
db .open-.P, .close-.P, .n_-.P, .plus-.P;()*+
db .comma-.P, .minus-.P, .dot-.P, .slsh-.P;,-./
db .n0-.P, .n1-.P, .n2-.P, .n3-.P ;0123
db .n4-.P, .n5-.P, .n6-.P, .n7-.P ;4567
db .n8-.P, .n9-.P, .colon-.P, .semi-.P;89:;
db .n_-.P, .eq-.P, .n_-.P, .qm-.P ;<=>?
db .at-.P, .a-.P, .b-.P, .c-.P ;@ABC
db .d-.P, .e-.P, .f-.P, .g-.P ;DEFG
db .h-.P, .i-.P, .j-.P, .k-.P ;HIJK
db .l-.P, .m-.P, .n-.P, .o-.P ;LMNO
db .p-.P, .q-.P, .r-.P, .s-.P ;PQRS
db .t-.P, .u-.P, .v-.P, .w-.P ;TUVW
db .x-.P, .y-.P, .z-.P, .n_-.P ;XYZ[
db .n_-.P, .n_-.P, .n_-.P, .uscr-.P;\]^_
db .n_-.P, .a-.P, .b-.P, .c-.P ;`abc
db .d-.P, .e-.P, .f-.P, .g-.P ;defg
db .h-.P, .i-.P, .j-.P, .k-.P ;hijk
db .l-.P, .m-.P, .n-.P, .o-.P ;lmno
db .p-.P, .q-.P, .r-.P, .s-.P ;pqrs
db .t-.P, .u-.P, .v-.P, .w-.P ;tuvw
db .x-.P, .y-.P, .z-.P, .n_-.P ;xyz{
db .n_-.P, .n_-.P, .n_-.P, .n_-.P ;|}~
.P: ;;; Morse pulses are stored four to a byte, lowest bits first
.n_: db 0 ; To ignore undefined characters
.a: db 0Dh
.b: db 57h,0
.c: db 77h,0
.d: db 17h
.e: db 1h
.f: db 75h,0
.g: db 1Fh
.h: db 55h,0
.i: db 5h
.j: db 0FDh,0
.k: db 37h
.l: db 5Dh,0
.m: db 0Fh
.n: db 7h
.o: db 3Fh
.p: db 7Dh,0
.q: db 0DFh,0
.r: db 1Dh
.s: db 15h
.t: db 3h
.u: db 35h
.v: db 0D5h,0
.w: db 3Dh
.x: db 0D7h,0
.y: db 0F7h,0
.z: db 5Fh,0
.n0: db 0FFh,3
.n1: db 0FDh,3
.n2: db 0F5h,3
.n3: db 0D5h,3
.n4: db 55h,3
.n5: db 55h,1
.n6: db 57h,1
.n7: db 5Fh,1
.n8: db 7Fh,1
.n9: db 0FFh,1
.dot: db 0DDh,0Dh
.comma: db 5Fh,0Fh
.qm: db 0F5h,5
.quot: db 0FDh,7
.excl: db 77h,0Fh
.slsh: db 0D7h,1
.open: db 0F7h,1
.close: db 0F7h,0Dh
.amp: db 5Dh,1
.colon: db 7Fh,5
.semi: db 77h,7
.eq: db 57h,3
.plus: db 0DDh,1
.minus: db 57h,0Dh
.uscr: db 0F5h,0Dh
.dquot: db 5Dh,7
.dolar: db 0D5h,35h
.at: db 7Dh,7
section .bss
buf: resb 1024 ; 1K buffer
.size: equ $-buf