8086 DOS .COM file, 134 bytes
It works now!
What better language to do this in than 16-bit assembly?
00000000: b9 c8 00 89 cd 31 c0 f3 50 89 e7 e8 5d 00 92 aa .....1..P...]...
00000010: 74 f9 29 e7 87 fb 89 e6 8d 3a e8 4e 00 9c 56 57 t.)......:.N..VW
00000020: 89 d9 ac f6 e2 03 05 ab 83 15 00 4f e2 f4 5f 5e ...........O.._^
00000030: 47 9d 74 e6 80 39 00 74 01 4b 01 df 01 e5 29 ef G.t..9.t.K....).
00000040: 89 ee ac 88 c3 b1 04 d2 e8 e8 10 00 93 24 0f e8 .............$..
00000050: 0a 00 b0 20 e8 0d 00 4f 75 e8 cd 20 3c 09 76 02 ... ...Ou.. <.v.
00000060: 04 27 04 30 b4 02 88 c2 cd 21 c3 b2 00 b4 01 cd .'.0.....!......
00000070: 21 2c 30 72 0e 3c 0a 72 02 2c 27 b1 04 d2 e2 00 !,0r.<.r.,'.....
00000080: c2 eb ea 3c f0 c3 ...<..
My first, and likely last 16-bit 8086 program. 😫
Assembly (NASM with C comments because of syntax highlighting):
// sed -i -e 's#//#;#g' dosmult.asm
// nasm -f bin dosmult.asm -o dosmult.com
// The default, but never harmed anyone.
[bits 16]
// 8086 code only! If I use 386 instructions or any of that,
// I would ruin the challenge.
[cpu 8086]
// COM files start at 100h.
org 100h
section .text
start:
// Create a 400 byte buffer on the stack.
// 200 will be reserved for the input, and 200 for the output.
mov cx, 200
// Save 200 in BP for later, it will be useful.
mov bp, cx
// Push 0 200 times making a 400 byte zeroed buffer on the stack
xor ax, ax
rep push ax
mov di, sp
// Read the first line of hex digits.
.getline_loop:
call gethex
// Swap DL to AL and store with STOSB
xchg dx, ax
stosb
// Loop if it was a space
jz .getline_loop
// Get length
sub di, sp
// Store length in BX
xchg di, bx
// Save the pointer to input in SI
mov si, sp
// Load the pointer to output in DI (SI + 200)
lea di, [si + bp]
// Begin multiplication, grade school byte by byte
// Instead of creating a third buffer, we process the data
// as the user inputs it.
.loop:
call gethex
// Save flags from gethex so we know when to stop
pushfw
// Save SI and DI
push si
push di
// Loop counter
mov cx, bx
.mult_loop:
// Load next byte from first input
lodsb
// Multiply by the input byte
mul dl
// 16-bit add to [DI]
// We actually add to AX, then do STOSW.
add ax, [di]
stosw
// Add the carry bit.
adc word [di], 0
// Decrement DI after STOSW incremented it by 2 to iterate
// on each byte.
dec di
// while (--cx)
loop .mult_loop
.mult_loop_end:
// Restore DI and SI
pop di
pop si
// Increment output pointer
inc di
// Restore flags from gethex
popfw
// Jump if we got a space.
jz .loop
.loop_end:
// Was there a trailing zero?
cmp byte[di + bx], 0
jz .nodec
.dec:
// If so, decrement the count.
dec bx
.nodec:
// Calculate the length with ugly pointer arithmetic.
// I feel there is a better way to do this.
add di, bx // ptr += len
add sp, bp // sp += 200 (sp = output)
sub di, sp // ptr -= output
// Store output array in SI.
mov si, sp
// Print each byte in hex
.print_loop:
// Load byte
lodsb
// puthex(AL >> 4)
mov bl, al
mov cl, 4
shr al, cl
call puthex
// puthex(AL & 0Fh)
xchg ax, bx
and al, 0Fh
call puthex
// putc(' ')
mov al, ' '
call putc
// Loop for every byte from DI.
dec di
jnz .print_loop
.print_loop_end:
// exit, not bothering to clean anything up. :P
int 20h
// Prints hex digit in AL.
// Must be <= 0fh
puthex:
cmp al, 9
jbe .no_hexa
.hexa:
add al, 'a' - '9' - 1
.no_hexa:
add al, '0'
// Fallthrough
// Prints AL to stdout.
putc:
mov ah, 2h
mov dl, al
int 21h
ret
// Reads a hex byte.
// Returns in DL.
// Sets whether it ended in a space in the Z flag.
gethex:
mov dl, 0
.start:
// Read char into AL
mov ah, 1h
int 21h
// Subtract ASCII '0'
sub al, '0'
// If we hit a space or newline, they will trigger the 'below'
// condition
jb .ret
// Was AL '0'-'9'? If not, correct it to 'a'-'f'.
cmp al, 10
jb .no_hexa
.hexa:
// Correct AL
sub al, 'a' - '9' - 1
.no_hexa:
// DL = (DL << 4) + AL
// 8086 doesn't have shift by immediate.
mov cl, 4
shl dl, cl
// Add to BL
add dl, al
// Loop
jmp .start
.ret:
// Check if AL before the subtraction was a space.
cmp al, ' ' - '0'
ret
Naturally, being a 16-bit instruction set, I am only doing 8-bit and 16-bit arithmetic (and I don't use the 16-bit mul
).
Beats the Perl answer by 3 bytes. Granted, this is binary machine code and that is source code, so I guess this isn't that much to get excited about. However, 8086 lacks a lot of the features Perl has (about 1/3 of it is just I/O and hex conversion 😫).
I am sure there are multiple optimizations possible here. A lot of my knowledge is from writing 32-bit and 64-bit code, so I am not fully knowledgeable in the 16-bit tricks.
Input is from stdin, in space separated lowercase hex, each ending in a new line. It is not line buffered, so no typos allowed! :P
Output is printed to stdout.