# 8086 Segment Address to Linear

Given an 8086 segmented ("logical") memory address, output its actual linear physical address.

A segmented memory address has format xxxx:xxxx, where each of x is one of 0-9A-F, and the two parts are parsed as hexadecimal integers. Both parts are exactly 4 digits.

To get its actual linear address, multiply the first part by sixteen and add the second part. You can return it in a reasonable format, e.g. integer or a length=5 string. You can choose whether to modulo the result by $$\2^{20}\$$ (like 8086, or 286+ with the A20 gate closed). Yet input format is fixed.

This is , shortest code in each language wins.

Test cases

0000:0000 -> 0x00000
1111:1111 -> 0x12221
FFFF:0000 -> 0xFFFF0
FFFF:0010 -> 0x00000 or 0x100000

• Is an integer a "reasonable" output format? Dec 21, 2022 at 17:40
– l4m2
Dec 21, 2022 at 17:42
• What about a base-16 representation like "1111:1111" -> [4369, 4369]? Dec 21, 2022 at 17:42
• @JonathanAllan No, input is fixed format
– l4m2
Dec 21, 2022 at 17:42
• Yeah, I meant the output format i.e. [4369, 4368] is a base 16 representation of the result - just wondering what "reasonable" is and how far we can push it :) Dec 21, 2022 at 17:43

# JavaScript (ES6), 33 bytes

-2 thanks to @l4m2

Returns an integer.

s=>eval(s.replace(/:|^/g,'0+0x'))


Try it online!

Turns an input such as "ABCD:1234" into "0+0xABCD0+0x1234" and evaluates it as JS code.

• 33
– l4m2
Dec 22, 2022 at 9:29
• @l4m2 Nice one. Dec 22, 2022 at 10:00

IFS=:;read a b;echo $[16*0x$a+0x$b]  Takes input from stdin and outputs to stdout Attempt This Online! # C (GCC), 52 bytes main(a,b){scanf("%X:%X",&a,&b);printf("%X",16*a+b);}  Attempt This Online! # MS-DOS .COM (386 instructions), 71 bytes Takes input from the command line and prints the result. To convert each output character, I use a variant of AAM that operates in base 16. BE 82 00 E8 2A 00 8B DF 66 C1 E3 04 46 E8 20 00 66 03 DF 66 C1 CB 10 B9 05 00 8A C3 D4 10 3C 0A 7C 02 04 07 04 30 CD 29 66 C1 C3 04 E2 EC CD 20 66 33 FF B9 04 00 C1 E7 04 AC A8 40 74 02 2C 07 2C 30 03 F8 E2 F0 C3  TASM source: IDEAL P386 MODEL TINY CODESEG ORG 100H MAIN: MOV SI,82H ; Start of command line CALL DECODE MOV BX,DI SHL EBX,4 ; Adjust segment portion INC SI CALL DECODE ADD EBX,EDI ; Add offset ROR EBX,16 ; Rotate address to point to first digit MOV CX,5 LOOPM: MOV AL,BL DB 0D4H, 10H ; AAM 10H (base 16: AH=AL/16, AL=AL mod 16) CMP AL,0AH JL ASCADJ ADD AL,7 ; Convert digits > 9 into A-F ASCADJ: ADD AL,48 INT 29H ROL EBX,4 ; Next digit LOOP LOOPM INT 20H DECODE: XOR EDI,EDI ; Return register MOV CX,4 LOOP1: SHL DI,4 LODSB TEST AL,40H JZ DECADJ SUB AL,7 ; Convert digits A-F to 10-16 DECADJ: SUB AL,48 ADD DI,AX LOOP LOOP1 RET END MAIN ENDS  Example results: C:\WORK>segment FFFF:0000 FFFF0  • There's a slightly more compact trick for generating a hex digit from a 0..15 integer in AL: Little Endian Number to String Conversion - and al, 0Fh / cmp al, 10 / sbb al, 69h / das (7 bytes), saving 1 vs. aam/cmp/jl/add. Dec 22, 2022 at 19:52 • Does this rely on the high 16 bits of EBX being zeroed? No, I guess not, you're using a counted loop to only output the set part of BX (not like l4m2's base-10 output loop that goes until zero), and the low 20 bits are known good because of the shl ebx,4 at one point. Thanks to the question letting us truncate like the A20 gate, we don't need to support wider linear addresses (FFFF:00010 and up) Dec 22, 2022 at 19:57 • This and l4m2's could of course be smaller as functions in 32-bit code that return an int, but I don't think there's anything particularly interesting about porting the logical-address parsing to that. (Except that l4m2's clever trick of cmp/je back to a rol edx,16 before the top of the character loop stays the same size, while two call instructions become 5 bytes each instead of 3.) Dec 22, 2022 at 20:51 • mov al, bl => xchg ax, bx ah bh bl are all trash – l4m2 Dec 23, 2022 at 3:24 # x86 .COM, 64 59 bytes 0100 BE 82 00 66 31 C9 66 F7-E1 B1 08 66 C1 CA 10 66 ...f1.f....f...f 0110 C1 C2 04 AC 3C 3A 74 F3-2C 30 D4 10 D5 09 66 01 ....<:t.,0....f. 0120 C2 E2 EC 66 92 66 99 B1-0A 66 F7 F1 66 85 C0 52 ...f.f...f..f..R 0130 74 03 E8 F0 FF 58 04 30-CD 29 C3 t....X.0.).  Thank Peter Cordes for -5 bytes  org$100
mov si, $82 xor ecx, ecx mul ecx mov cl, 8 b: ror edx, 16 a: rol edx, 4 lodsb cmp al, ':' je b sub al, '0' aam$10
loop a
xchg eax, edx
p:  cdq
mov cl, 10
div ecx
test eax, eax
push dx
jz q
call p
q:  pop ax
int $29 ret  Output decimal • That's not an 8086 program; it requires 386 or higher for 32-bit operand-size (div ecx). That's fine, just make sure to label it appropriately like ErikF's answer (MS-DOS .COM (386 instructions)). The 66h prefix bytes in the machine code sets the operand-size to the non-default size, i.e. to 32-bit when the CPU's in real mode, but only on 386+. Also, ror r/m, imm8 was new in 186: pushbx.org/ecm/doc/insref.htm#insROL, same for push imm8. (But you're only using them with 32-bit operand-size so they of course require 386) Dec 22, 2022 at 19:13 • Seems like there should be a cheaper way to get ECX=10. pushd 10 (3 bytes) / pop ecx (2 bytes) is 5 total. lea ecx, [edx+10] is also 5 (the normal 3 plus 66h and 67h prefixes). Oh, you have SI=0x82 + 9, so lea ecx, [si - 82$ - 9 + 10] would work, with 16-bit address size. But (82$+9) is just barely too far away from 10, it needs a disp16: 66 8D 8C 7F FF. If you wanted to rely on an initial value of BX, DI, or BP in some DOS versions, that could work, but not be as portable. Tips for golfing in x86/x64 machine code Dec 22, 2022 at 19:37 • Can we save anything with xor ecx,ecx / mul ecx to zero 3 registers (including their upper halves) in 6 bytes? That should allow mov cl, 8 instead of mov cx,8, and mov cl, 10 instead of pushd 10 / pop ecx. (After the loop instruction, cx = 0, and thus ECX=0). It also allows optimizing away the movzx eax, al (4 bytes), since aad leaves AH=0 (felixcloutier.com/x86/aad) and the upper 16 of EAX are also known zero. Dec 22, 2022 at 20:58 • This requires upper-case hex input, right? ASCII hex digit to integer via c-='0'; (c>>4)*9 + (c&0xf) would give numbers to large for lower-case ASCII, unlike the (c>>6)*9 + (c&0xf) version of the bithack that doesn't subtract first. That's fine, just seems like it should get mentioned. Dec 22, 2022 at 21:07 •  cmp al, ':' je b jb$+4 sub al, 7 sub al, '0' can make the code without aam and aad but same size
– l4m2
Dec 23, 2022 at 3:04

# Vyxal, 7 bytes

\:/H16β


Try it Online!

\:/H16β
\:/     # Split on colon
H    # Convert each from hex string to int
16β # Convert list from base 16 to decimal


# Excel (ms365), 37 bytes

Formula in B1:

=SUM(HEX2DEC(MID(A1,{1,6},4))*{16,1})


# Python, 39 bytes

lambda x:int(x[:4],16)*16+int(x[5:],16)


Attempt This Online!

# Raku, 31 bytes

*+<4+*o{m:g/\w+/».&{:16(~$_)}}  Try it online! This is two anonymous functions composed together with the o operator. The first, * +< 4 + * returns its first argument shifted left four bits (ie, multiplied by 16) plus its second argument. The second, enclosed by braces, extracts the two hexadecimal substrings (m:g/\w+/) and parses them into integers (:16(~$_)).

# Japt-x, 13 bytes

q': ÔËnG *GpE


Try it

Output is an integer. This version includes a footer which translates to the format in the test cases (No modulus, so it outputs 0x100000 for the last test case).

Explanation:

q': ÔËnG *GpE
q':           # Split input where the character ':' appears
Ô         # Reverse the array
Ë        # Map:
nG      #  Treat the string as a base-16 number
*    #  Multiply with
GpE #  16 to the power of the current index

-x            # Output the sum of the resulting array


# Factor, 35 bytes

[ ":"split1 hex> swap hex> 16 * + ]


Try it online!

       ! "1111:1111"
":"    ! "1111:1111" ":"
split1 ! "1111" "1111"
hex>   ! "1111" 4369
swap   ! 4369 "1111"
hex>   ! 4369 4369
16     ! 4369 4369 16
*      ! 4369 69904
+      ! 74273


# Python, 53 bytes

lambda x:int((s:=x.split(':'))[0],16)*16+int(s[1],16)


Attempt This Online!

Returns an integer (ok as per OP). Footer on ATO converts to hexadecimal.

# Jelly,  13  11 bytes

Jelly has no built-in standard hex string conversion functions :/

ØHiⱮ’ṣ-ḅ⁴ḅ⁴


A monadic Link that accepts a list of characters (as defined) and yields an integer.

Try it online!

### How?

ØHiⱮ’ṣ-ḅ⁴ḅ⁴ - Link: list of characters, S
ØH         - hex-characters -> "0123456789ABCDEF"
Ɱ       - map across c in S with:
i        -   first 1-indexed index of c in the hex-characters or 0
’      - decrement (vectorises)
-    - literal -1
ṣ     - split the ØHiⱮ’ result at -1's (this is what ':' will have mapped to)
ḅ⁴   - convert from base sixteen (vectorises)
ḅ⁴ - convert from base sixteen


# Charcoal, 14 bytes

⍘↨Ｅ⪪Ｓ:⍘ι⊗⁸⊗⁸⊗⁸


Try it online! Link is to verbose version of code. Explanation:

    Ｓ           Input string
⪪            Split on
:          Literal string :
Ｅ             Map over parts
ι        Current part
⍘         Convert from base
⁸      Literal integer 8
⊗       Doubled
↨              Convert from base
⁸    Literal integer 8
⊗     Doubled
⍘               Convert to base
⁸  Literal integer 8
⊗   Doubled
Implicitly print


Consecutive integers would require a separator so I double 8 instead of using a literal 16. (The first one doesn't need to be double 8 but I wanted to be consistent.)

# Retina 0.8.2, 87 bytes

:
0¶0
O#$.$.%
¶

M!..
[a-f]
55$& Tld .$*
+¶1{16}
1¶
1{16}

%M1
Tdl..
b\B|¶



Try it online! Link includes test cases. Explanation:

:
0¶0


Replace the separator with a newline, but also suffixing a 0 to the first number and prefixing a 0 to the second number, reducing the problem to hexadecimal addition.

O#$.$.%
¶

M!..


Transpose the digits.

[a-f]
55$& Tld .$*


Convert each digit to unary. As they are now transposed, each line now corresponds to the sum of the two digits in unary.

+¶1{16}
1¶
1{16}



Propagate carries, but drop the 20th carry (allowed per spec).

%M1


Convert each resulting "digit" to decimal.

Tdl..
b\B|¶



Convert each decimal number to a hexadecimal digit and join everything together.

# Pip, 23 bytes

Uo{aFB16*16E Do}MSa^":"


Try It Online!

I couldn't quite figure out a nice way to multiply the first part by 16, I'm sure @DLosc will be able to save the day

• Can you shift bits in Pip?
– jdt
Dec 22, 2022 at 2:57
• @jdt not that I know of Dec 22, 2022 at 3:55

# C (gcc), 40 bytes

i;f(s){sscanf(s,"%x:%x",&i,&s);s+=i<<4;}


Try it online!

# R, 44 36 bytes

strtoi(scan(,"",,,":"),16)%*%c(16,1)


Attempt This Online!

# Perl-aF:, 29 + 4 = 33 bytes

print 16*hex($F[0])+hex$F[1]


Attempt This Online!

# Nibbles, 7 bytes (14 nibbles)

@4.=$$Nhex  Outputs decimal address value. @4.=$Nhex =$         # chunk the input by
\$# its character class: N # is it alphanumeric? . # now map over the 3 chunks # (the middle one is ":") hex # get hex value # (value of ":" is zero) @4 # and convert the 3-element list # to base-4  For the same bytes, we could also explicitly split % the input on the string ":", and convert from base-16, similarly to some other answers:  @16.%$":"hex

# Go, 83 80 bytes

import."fmt"
func f(s string)int{l,r:=0,0
Sscanf(s,"%x:%x",&l,&r)
return l*16+r}


Attempt This Online!

Very similar to the other answers with some sort of Scanf: Look for 2 hex numbers, put them into variables, and return the result.

• -3 bytes from @Steffan: var l,r int => l,r:=0,0
• l,r:=0,0 is shorter than var l,r int Dec 22, 2022 at 22:54

# 05AB1E, 7 bytes

':¡H16β


Outputs as an integer.

Explanation:

':¡     '# Split the (implicit) input-string on ":"
H     # Convert both strings in the pair from hexadecimal to base-10 integers
16β  # Convert this list to a base-16 integer
# (which is output implicitly as result)


# Arturo, 44 bytes

\$[s][+*16from.hex take s 4from.hex drop s 5]


Try it

# Python 3, 42 bytes

Python translation of @Arnauld's JavaScript solution

lambda a:eval('0x'+a.replace(':','0+0x'))
`

Try it online!