13
\$\begingroup\$

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
\$\endgroup\$
8
  • 1
    \$\begingroup\$ Is an integer a "reasonable" output format? \$\endgroup\$ Commented Dec 21, 2022 at 17:40
  • \$\begingroup\$ @JonathanAllan Yes. Added \$\endgroup\$
    – l4m2
    Commented Dec 21, 2022 at 17:42
  • \$\begingroup\$ What about a base-16 representation like "1111:1111" -> [4369, 4369]? \$\endgroup\$ Commented Dec 21, 2022 at 17:42
  • \$\begingroup\$ @JonathanAllan No, input is fixed format \$\endgroup\$
    – l4m2
    Commented Dec 21, 2022 at 17:42
  • 1
    \$\begingroup\$ 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 :) \$\endgroup\$ Commented Dec 21, 2022 at 17:43

25 Answers 25

7
\$\begingroup\$

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.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ 33 \$\endgroup\$
    – l4m2
    Commented Dec 22, 2022 at 9:29
  • 1
    \$\begingroup\$ @l4m2 Nice one. \$\endgroup\$
    – Arnauld
    Commented Dec 22, 2022 at 10:00
5
\$\begingroup\$

Bash, 35 bytes

IFS=:;read a b;echo $[16*0x$a+0x$b]

Takes input from stdin and outputs to stdout

Attempt This Online!

\$\endgroup\$
4
\$\begingroup\$

C (GCC), 52 bytes

main(a,b){scanf("%X:%X",&a,&b);printf("%X",16*a+b);}

Attempt This Online!

\$\endgroup\$
4
\$\begingroup\$

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
\$\endgroup\$
4
  • 1
    \$\begingroup\$ 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. \$\endgroup\$ Commented Dec 22, 2022 at 19:52
  • \$\begingroup\$ 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) \$\endgroup\$ Commented Dec 22, 2022 at 19:57
  • \$\begingroup\$ 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.) \$\endgroup\$ Commented Dec 22, 2022 at 20:51
  • 1
    \$\begingroup\$ mov al, bl => xchg ax, bx ah bh bl are all trash \$\endgroup\$
    – l4m2
    Commented Dec 23, 2022 at 3:24
4
\$\begingroup\$

Excel (ms365), 37 bytes

enter image description here

Formula in B1:

=SUM(HEX2DEC(MID(A1,{1,6},4))*{16,1})
\$\endgroup\$
0
4
\$\begingroup\$

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
    aad 9
    add edx, eax
    loop a
    xchg eax, edx
p:  cdq
    mov cl, 10
    div ecx
    test eax, eax
    push dx
    jz q
    call p
q:  pop ax
    add al, '0'
    int $29
    ret

Output decimal

\$\endgroup\$
6
  • 1
    \$\begingroup\$ 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) \$\endgroup\$ Commented Dec 22, 2022 at 19:13
  • \$\begingroup\$ 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 \$\endgroup\$ Commented Dec 22, 2022 at 19:37
  • \$\begingroup\$ 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. \$\endgroup\$ Commented Dec 22, 2022 at 20:58
  • \$\begingroup\$ 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. \$\endgroup\$ Commented Dec 22, 2022 at 21:07
  • 1
    \$\begingroup\$ ` cmp al, ':' je b jb $+4 sub al, 7 sub al, '0'` can make the code without aam and aad but same size \$\endgroup\$
    – l4m2
    Commented Dec 23, 2022 at 3:04
3
\$\begingroup\$

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
\$\endgroup\$
2
\$\begingroup\$

Python, 39 bytes

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

Attempt This Online!

\$\endgroup\$
2
\$\begingroup\$

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(~$_)).

\$\endgroup\$
2
\$\begingroup\$

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
\$\endgroup\$
2
\$\begingroup\$

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
\$\endgroup\$
2
\$\begingroup\$

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

enter image description here


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

\$\endgroup\$
1
\$\begingroup\$

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.

\$\endgroup\$
1
\$\begingroup\$

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
\$\endgroup\$
1
\$\begingroup\$

Charcoal, 14 bytes

⍘↨E⪪S:⍘ι⊗⁸⊗⁸⊗⁸

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

    S           Input string
   ⪪            Split on
     :          Literal string `:`
  E             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.)

\$\endgroup\$
1
\$\begingroup\$

Retina 0.8.2, 87 bytes

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

M!`..
[a-f]
55$&
T`l`d
.
$*
+`¶1{16}
1¶
1{16}

%M`1
T`d`l`..
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$&
T`l`d
.
$*

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).

%M`1

Convert each resulting "digit" to decimal.

T`d`l`..
b\B|¶

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

\$\endgroup\$
1
\$\begingroup\$

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

\$\endgroup\$
3
  • \$\begingroup\$ Can you shift bits in Pip? \$\endgroup\$
    – jdt
    Commented Dec 22, 2022 at 2:57
  • \$\begingroup\$ @jdt not that I know of \$\endgroup\$
    – jezza_99
    Commented Dec 22, 2022 at 3:55
  • \$\begingroup\$ Rather belatedly, I am here to save the day. ;) If you want to keep a similar structure with the MS and a global variable determining whether to multiply by 16, {aFB16*16E!:x}MSa^': is one option (20 bytes). But it seems cheaper to put a 0 on the end of the first part before converting from hex. One fun way to do that is _FB16MSCHaR':00 (replace the colon with two zeros, chop in half, convert each from base 16 and sum). \$\endgroup\$
    – DLosc
    Commented Apr 14 at 18:31
1
\$\begingroup\$

C (gcc), 40 bytes

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

Try it online!

\$\endgroup\$
1
\$\begingroup\$

R, 44 36 bytes

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

Attempt This Online!

\$\endgroup\$
1
\$\begingroup\$

Perl -aF:, 29 + 4 = 33 bytes

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

Attempt This Online!

\$\endgroup\$
1
\$\begingroup\$

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
\$\endgroup\$
1
  • \$\begingroup\$ l,r:=0,0 is shorter than var l,r int \$\endgroup\$
    – naffetS
    Commented Dec 22, 2022 at 22:54
1
\$\begingroup\$

05AB1E, 7 bytes

':¡H16β

Outputs as an integer.

Try it online or verify all test cases.

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)
\$\endgroup\$
0
\$\begingroup\$

Arturo, 44 bytes

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

Try it

\$\endgroup\$
0
\$\begingroup\$

Python 3, 42 bytes

Python translation of @Arnauld's JavaScript solution

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

Try it online!

\$\endgroup\$
0
\$\begingroup\$

Pip, 14 bytes

(a^':)FB16FD16

Outputs an integer. Attempt This Online!

Explanation

Pip has two types of base-conversion builtins: FB takes a string, while FD takes a list of digits. The digits for FD don't have to be valid digits for the given base, and so:

(a^':)FB16FD16
 a^':           ; Split command-line argument on colon
(    )FB16      ; Convert each of those strings from base 16
          FD16  ; Convert that list from base 16
                ; I.e. multiply the first element by 16 and add to the second
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.