Giả lập CPU Intel 8086


157

Lưu ý: Một vài câu trả lời đã đến. Xem xét nâng cao câu trả lời mới hơn quá.


Các 8086 là của Intel x86 vi xử lý đầu tiên. Nhiệm vụ của bạn là viết một trình giả lập cho nó. Vì điều này là tương đối tiên tiến, tôi muốn giới hạn nó một litte:

  • Chỉ các opcodes sau cần được thực hiện:
    • Mov, đẩy, pop, xchg
    • thêm, adc, phụ, sbb, cmp, và, hoặc, xor
    • inc, dec
    • gọi, ret, jmp
    • jb, jz, jbe, js, jnb, jnz, jnbe, jns
    • stc, clc
    • hlt, nop
  • Do đó, bạn chỉ cần tính toán các cờ mang, số 0 và ký hiệu
  • Đừng thực hiện các phân đoạn. Giả sử cs = ds = ss = 0.
  • Không có tiền tố
  • Không có loại ngắt hoặc cổng IO
  • Không có chức năng chuỗi
  • Không có opcodes hai byte (0F ..)
  • Không có số học dấu phẩy động
  • (rõ ràng) không có thứ 32 bit, sse, mmx, ... bất cứ thứ gì chưa được phát minh vào năm 1979
  • Bạn không phải đếm chu kỳ hoặc thực hiện bất kỳ thời gian nào

Bắt đầu với ip = 0sp = 100h.


Đầu vào: Trình giả lập của bạn sẽ lấy một chương trình nhị phân theo bất kỳ định dạng nào bạn muốn làm đầu vào (đọc từ tệp, mảng được xác định trước, ...) và tải nó vào bộ nhớ tại địa chỉ 0.

Đầu ra: RAM video bắt đầu tại địa chỉ 8000h, mỗi byte là một ký tự (ASCII-). Giả lập màn hình 80x25 để điều khiển. Xử lý các byte bằng không như dấu cách.

Thí dụ:

08000   2E 2E 2E 2E 2E 2E 2E 2E 2E 00 00 00 00 00 00 00   ................
08010   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
08020   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
08030   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
08040   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
08050   48 65 6C 6C 6F 2C 20 77 6F 72 6C 64 21 00 00 00   Hello,.world!...

Lưu ý: Điều này rất giống với chế độ video thực, thường ở mức 0xB8000 và có một byte khác cho mỗi ký tự cho màu sắc.

Tiêu chí chiến thắng:

  • Tất cả các hướng dẫn được đề cập cần phải được thực hiện
  • Tôi đã thực hiện một chương trình thử nghiệm không bị lỗi ( liên kết , nguồn nasm ) nên chạy đúng. Nó xuất ra

    .........                                                                       
    Hello, world!                                                                   
    0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ 
    
    
    ################################################################################
    ##                                                                            ##
    ##  0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987                          ##
    ##                                                                            ##
    ##  0 1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 289 324 361 400     ##
    ##                                                                            ##
    ##  2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97    ##
    ##                                                                            ##
    ##                                                                            ##
    ##                                                                            ##
    ##                                                                            ##
    ##                                                                            ##
    ##                                                                            ##
    ##                                                                            ##
    ##                                                                            ##
    ##                                                                            ##
    ##                                                                            ##
    ##                                                                            ##
    ##                                                                            ##
    ################################################################################
    
  • Tôi không chắc chắn nếu điều này nên là codegolf; đó là một nhiệm vụ khó khăn, vì vậy bất kỳ bài nộp nào cũng sẽ giành được nhiều lượt ủng hộ. Hãy bình luận.

Dưới đây là một số liên kết để giúp bạn về nhiệm vụ này:

Đây là mục đầu tiên của tôi đến nền tảng này. Nếu có bất kỳ sai lầm, xin vui lòng chỉ ra chúng; nếu tôi bỏ lỡ một chi tiết, chỉ cần hỏi.


5
quá tiến bộ đối với tôi, nhưng tôi rất háo hức muốn xem câu trả lời cho câu hỏi này vì đây chính xác là thứ tôi quan tâm nhất! Tôi có thể sẽ có một vết nứt sau đó nếu tôi cảm thấy đặc biệt là bạo dâm ...
Chris Browne

3
@ChrisBrowne chúc may mắn được khổ dâm! Tôi hiện đang biến 8086 của mình thành 80386 và đã học được rất nhiều từ dự án này cho đến nay.
sao chép

2
+1 + yêu thích ... tôi không thể bắt đầu bày tỏ cảm giác khi nhận được câu hỏi này.
ixtmixilix

2
@copy Không bao giờ là quá muộn để thực hiện một cuộc thi golf cho mỗi cặp ngôn ngữ / chủ nhà
Yauhen Yakimovich

2
@ MartinBüttner Chắc chắn, câu hỏi cũ hơn thẻ đó và về cơ bản vẫn là một cuộc thi phổ biến
sao chép

Câu trả lời:


84

Hãy thoải mái rẽ nhánh và chơi gôn: https://github.com/julienaubert/py8086

Kết quả Tôi cũng bao gồm một trình gỡ lỗi tương tác.

CF:0 ZF:0 SF:0 IP:0x0000
AX:0x0000  CX:0x0000  DX:0x0000  BX:0x0000  SP:0x0100  BP:0x0000  SI:0x0000  DI:0x0000
AL:  0x00  CL:  0x00  DL:  0x00  BL:  0x00  AH:  0x00  CH:  0x00  DH:  0x00  BH:  0x00
stack: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 ...
cmp SP, 0x100
[Enter]:step [R]:run [B 0xadr]:add break [M 0xadr]:see RAM [Q]:quit

B 0x10
M 0x1
M 0x1: 0xfc 0x00 0x01 0x74 0x01 0xf4 0xbc 0x00 0x10 0xb0 0x2e 0xbb ...
R

CF:0 ZF:0 SF:1 IP:0x0010
AX:0x002e  CX:0x0000  DX:0x0000  BX:0xffff  SP:0x1000  BP:0x0000  SI:0x0000  DI:0x0000
AL:  0x2e  CL:  0x00  DL:  0x00  BL:  0xff  AH:  0x00  CH:  0x00  DH:  0x00  BH:  0x00
stack: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 ...
cmp BX, 0xffff
[Enter]:step [R]:run [B 0xadr]:add break [M 0xadr]:see RAM [Q]:quit

Có ba tệp: emu8086.py (bắt buộc) console.py (tùy chọn cho đầu ra hiển thị), disasm.py (tùy chọn, để có danh sách mã asm trong codegolf).

Để chạy với màn hình (lưu ý sử dụng lời nguyền):

python emu8086.py 

Để chạy với trình gỡ lỗi tương tác:

python emu8086.py a b

Để chạy với "trình gỡ lỗi" không tương tác:

python emu8086.py a

Chương trình " codegolf " phải nằm trong cùng thư mục.

emu8086.py

giao diện điều khiển

disasm.py

Trên github


9
Đó là một địa ngục của bài Code Golf đầu tiên. +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 ...
Dillon Cower

@DC cảm ơn :) là một thử thách thú vị!
ja

1
Vẫn không thể tin ai đó thực sự đã làm điều này :-) Công việc tuyệt vời!
sao chép

1
Kinh ngạc! Xin chúc mừng! Có bao nhiêu dòng cuối cùng?
Sẽ Lp

59

Haskell, 256 234 196 dòng

Tôi đã có một công việc đang tiến hành này một thời gian, tôi dự định sẽ đánh bóng nó thêm một chút trước khi xuất bản, nhưng bây giờ cuộc vui đã chính thức bắt đầu, không còn nhiều điều phải giấu nó nữa. Tôi nhận thấy trong khi giải nén nó dài chính xác 256 dòng, vì vậy tôi cho rằng nó đang ở điểm "đáng chú ý" về sự tồn tại của nó.

Những gì trong: hầu như không đủ của lệnh 8086 được thiết lập để chạy ví dụ nhị phân hoàn hảo. Mã tự sửa đổi được hỗ trợ. (prefetch: zero byte)
Trớ trêu thay, các lần lặp đủ đầu tiên của mã dài hơn và được hỗ trợ ít hơn trong khoảng opcode. Tái cấu trúc kết thúc có lợi cả về độ dài mã và phạm vi bảo hiểm opcode.

Những gì đã xảy ra: rõ ràng, các phân đoạn, tiền tố và opcodes multibyte, ngắt, cổng I / O, hoạt động chuỗi và FP. Ban đầu tôi đã làm theo PUSH SPhành vi ban đầu , nhưng phải bỏ nó sau vài lần lặp.

Kết quả cờ mang có lẽ rất lộn xộn trong một vài trường hợp ADC/ SBB.

Dù sao, đây là mã:

------------------------------------------------------------
-- Imports

-- They're the only lines I allow to go over 80 characters.
-- For the simple reason the code would work just as well without the
-- actual symbol list, but I like to keep it up to date to better
-- grasp my dependency graph.

import           Control.Monad.Reader      (ReaderT,runReaderT,ask,lift,forever,forM,when,void)
import           Control.Monad.ST          (ST,runST)
import           Control.Monad.Trans.Maybe (MaybeT,runMaybeT)
import           Data.Array.ST             (STUArray,readArray,writeArray,newArray,newListArray)
import           Data.Bits                 (FiniteBits,(.&.),(.|.),xor,shiftL,shiftR,testBit,finiteBitSize)
import           Data.Bool                 (bool)
import qualified Data.ByteString as B      (unpack,getContents)
import           Data.Char                 (chr,isPrint) -- for screen dump
import           Data.Int                  (Int8)
import           Data.STRef                (STRef,newSTRef,readSTRef,writeSTRef,modifySTRef)
import           Data.Word                 (Word8,Word16)

------------------------------------------------------------
-- Bytes and Words
-- Bytes are 8 bits.  Words are 16 bits.  Addressing is little-endian.

-- Phantom types.  Essentially (only?) used for the ALU
byte = undefined :: Word8
word = undefined :: Word16

-- Byte to word conversion
byteToWordSE = (fromIntegral :: Int8 -> Word16) .
               (fromIntegral :: Word8 -> Int8)

-- Two-bytes to word conversion
concatBytes :: Word8 -> Word8 -> Word16
concatBytes l h = fromIntegral l .|. (fromIntegral h `shiftL` 8)

-- Word to two bytes conversion
wordToByteL,wordToByteH :: Word16 -> Word8
wordToByteL = fromIntegral
wordToByteH = fromIntegral . (`shiftR` 8)

-- A Place is an lvalue byte or word.  In absence of I/O ports, this
-- means RAM or register file.  This type synonym is not strictly
-- needed, but without it it's unclear I could keep the alu function
-- type signature under twice 80 characters, so why not keep this.
type Place s = (STUArray s Word16 Word8,Word16)

-- Read and write, byte or word, from RAM or register file

class (Ord a,FiniteBits a,Num a) => Width a where
  readW  :: Place s ->      MonadCPU s a
  writeW :: Place s -> a -> MonadCPU s ()

instance Width Word8 where
  readW  =  liftST    . uncurry readArray
  writeW = (liftST .) . uncurry writeArray

instance Width Word16 where
  readW (p,a) = concatBytes <$> readW (p,a) <*> readW (p,a+1)
  writeW (p,a) val = do
    writeW (p,a)   $ wordToByteL val
    writeW (p,a+1) $ wordToByteH val

------------------------------------------------------------
-- CPU object

-- The actual CPU state.  Yeah, I obviously don't have all flags in! :-D
data CPU s = CPU { ram  :: STUArray s Word16 Word8
                 , regs :: STUArray s Word16 Word8
                 , cf :: STRef s Bool
                 , zf :: STRef s Bool
                 , sf :: STRef s Bool }

newCPU rawRam = do ramRef <- newListArray (0,0xFFFF) rawRam
                   regFile <- newArray (0,17) 0
                   cf <- newSTRef False
                   zf <- newSTRef False
                   sf <- newSTRef False
                   return $ CPU ramRef regFile cf zf sf

-- Register addresses within the register file.  Note odd placement
-- for BX and related.  Also note the 16-bit registers have a wider
-- pitch.  IP was shoehorned in recently, it doesn't really need an
-- address here, but it made other code shorter, so that's that.

-- In the 8-bit subfile, only regAl is used in the code (and it's 0,
-- so consider that a line I could totally have skipped)
[regAl,regAh,regCl,regCh,regDl,regDh,regBl,regBh] = [0..7]

-- In the 16-bit file, they're almost if not all referenced.  8086
-- sure is clunky.
[regAx,regCx,regDx,regBx,regSp,regBp,regSi,regDi,regIp] = [0,2..16]

-- These functions look like I got part of the Lens intuition
-- independently, come to look at it after the fact.  Cool :-)
readCpu  ext   = liftST .      readSTRef    . ext =<< ask
writeCpu ext f = liftST . flip writeSTRef f . ext =<< ask

-- It looks like the only operations IP can receive are relative moves
-- (incrIP function below) and a single absolute set: RET.  I deduce
-- only short jumps, not even near, were in the spec.
incrIP i = do old <- readReg regIp
              writeReg regIp (old + i)
              return old

-- Read next instruction.  Directly from RAM, so no pipeline prefetch.
readInstr8 = incrIP 1 >>= readRam
readInstr16 = concatBytes <$> readInstr8 <*> readInstr8

-- RAM/register file R/W specializers
readReg  reg      = ask >>= \p -> readW  (regs p,reg)
readRam  addr     = ask >>= \p -> readW  (ram p ,addr)
writeReg reg val  = ask >>= \p -> writeW (regs p,reg)  val
writeRam addr val = ask >>= \p -> writeW (ram p ,addr) val

-- I'm not quite sure what those do anymore, or why they're separate.
decodeReg8  n = fromIntegral $ (n `shiftL` 1) .|. (n `shiftR` 2)
decodeReg16 n = fromIntegral $  n `shiftL` 1
readDecodedReg8 = readReg . decodeReg8
readDecodedReg16 = readReg . decodeReg16

-- The monad type synonym make type signatures easier :-(
type MonadCPU s = MaybeT (ReaderT (CPU s) (ST s))

-- Specialized liftST, because the one from Hackage loses the
-- parameter, and I need it to be able to qualify Place.
liftST :: ST s a -> MonadCPU s a
liftST = lift . lift

------------------------------------------------------------
-- Instructions

-- This is arguably the core secret of the 8086 architecture.
-- See statement links for actual explanations.
readModRM = do
  modRM <- readInstr8
  let mod   =  modRM           `shiftR` 6
      opReg = (modRM .&. 0x38) `shiftR` 3
      rm    =  modRM .&. 0x07
  cpu <- ask
  operand <- case mod of
               0 -> do
                 addr <- case rm of
                           1 -> (+) <$> readReg regBx <*> readReg regDi
                           2 -> (+) <$> readReg regBp <*> readReg regSi
                           6 -> readInstr16
                           7 -> readReg regBx
                 return (ram cpu,addr)
               2 -> do
                 addr <- case rm of
                           5 -> (+) <$> readReg regDi <*> readInstr16
                           7 -> (+) <$> readReg regBx <*> readInstr16
                 return (ram cpu,addr)
               3 -> return (regs cpu,2*fromIntegral rm)
  return (operand,opReg,opReg)

-- Stack operations.  PUSH by value (does NOT reproduce PUSH SP behavior)
push16 val = do
  sp <- subtract 2 <$> readReg regSp
  writeReg regSp sp
  writeRam sp (val :: Word16)
pop16 = do
  sp <- readReg regSp
  val <- readRam sp
  writeReg regSp (sp+2)
  return (val :: Word16)

-- So, yeah, JMP seems to be relative (short) only.  Well, if that's enough…
jump cond = when cond . void . incrIP . byteToWordSE =<< readInstr8

-- The ALU.  The most complicated type signature in this file.  An
-- initial argument as a phantom type I tried to get rid of and
-- failed.
alu :: Width w => w -> MonadCPU s w -> MonadCPU s w -> Place s
    -> (w -> w -> MonadCPU s (Bool,Maybe Bool,w)) -> MonadCPU s ()
alu _ a b r op = do
  (rw,c,v) <- a >>= (b >>=) . op
  when rw $ writeW r v
  maybe (return ()) (writeCpu cf) c
  writeCpu zf (v == 0)
  writeCpu sf (testBit v (finiteBitSize v - 1))
decodeALU 0 = \a b -> return (True, Just (a >= negate b),       a   +   b)
decodeALU 1 = \a b -> return (True, Just False,                 a  .|.  b)
decodeALU 2 = \a b -> bool 0 1 <$> readCpu cf >>= \c ->
                      return (True, Just (a >= negate (b + c)), a + b + c)
decodeALU 3 = \a b -> bool 0 1 <$> readCpu cf >>= \c ->
                      return (True, Just (a < b + c),           a - b - c)
decodeALU 4 = \a b -> return (True, Just False,                 a  .&.  b)
decodeALU 5 = \a b -> return (True, Just (a <= b),              a   -   b)
decodeALU 6 = \a b -> return (True, Just False,                 a `xor` b)
decodeALU 7 = \a b -> return (False,Just (a <= b),              a   -   b)
opIncDec :: Width w => w -> w -> MonadCPU s (Bool,Maybe Bool,w)
opIncDec    = \a b -> return (True, Nothing,                    a   +   b)

-- Main iteration: process one instuction
-- That's the rest of the meat, but that part's expected.
processInstr = do
  opcode <- readInstr8
  regs <- regs <$> ask
  let zReg = (regs,decodeReg16 (opcode .&. 0x07))
  if opcode < 0x40 then -- no segment or BCD
    let aluOp = (opcode .&. 0x38) `shiftR` 3 in case opcode .&. 0x07 of
    0 -> do
      (operand,reg,_) <- readModRM
      alu byte (readW operand) (readDecodedReg8 reg) operand (decodeALU aluOp)
    1 -> do
      (operand,reg,_) <- readModRM
      alu word (readW operand) (readDecodedReg16 reg) operand (decodeALU aluOp)
    4 -> alu byte (readReg regAl) readInstr8 (regs,regAl) (decodeALU aluOp)
  else case opcode .&. 0xF8 of -- 16-bit (mostly) reg ops
    0x40 -> alu word (readW zReg) (return   1 ) zReg opIncDec -- 16b INC
    0x48 -> alu word (readW zReg) (return (-1)) zReg opIncDec -- 16b DEC
    0x50 -> readW zReg >>= push16                       -- 16b PUSH reg
    0x58 -> pop16 >>= writeW zReg                       -- 16b POP reg
    0x90 -> do v1 <- readW zReg                         -- 16b XCHG (or NOP)
               v2 <- readReg regAx
               writeW zReg (v2 :: Word16)
               writeReg regAx (v1 :: Word16)
    0xB0 -> readInstr8  >>= writeW zReg -- (BUG!)       -- 8b MOV reg,imm
    0xB8 -> readInstr16 >>= writeW zReg                 -- 16b MOV reg,imm
    _ -> case bool opcode 0x82 (opcode == 0x80) of
      0x72 -> jump       =<< readCpu cf                 -- JB/JNAE/JC
      0x74 -> jump       =<< readCpu zf                 -- JE/JZ
      0x75 -> jump . not =<< readCpu zf                 -- JNE/JNZ
      0x76 -> jump       =<< (||) <$> readCpu cf <*> readCpu zf -- JBE
      0x77 -> jump . not =<< (||) <$> readCpu cf <*> readCpu zf -- JA
      0x79 -> jump . not =<< readCpu sf                 -- JNS
      0x81 -> do                                        -- 16b arith to imm
        (operand,_,op) <- readModRM
        alu word (readW operand) readInstr16 operand (decodeALU op)
      0x82 -> do                                        -- 8b arith to imm
        (operand,_,op) <- readModRM
        alu byte (readW operand) readInstr8 operand (decodeALU op)
      0x83 -> do                                        -- 16b arith to 8s imm
        (operand,_,op) <- readModRM
        alu word (readW operand) (byteToWordSE <$> readInstr8) operand
            (decodeALU op)
      0x86 -> do                                        -- 8b XCHG reg,RM
        (operand,reg,_) <- readModRM
        v1 <- readDecodedReg8 reg
        v2 <- readW operand
        writeReg (decodeReg8 reg) (v2 :: Word8)
        writeW operand v1
      0x88 -> do                                        -- 8b MOV RM,reg
        (operand,reg,_) <- readModRM
        readDecodedReg8 reg >>= writeW operand
      0x89 -> do                                        -- 16b MOV RM,reg
        (operand,reg,_) <- readModRM
        readDecodedReg16 reg >>= writeW operand
      0x8A -> do                                        -- 8b MOV reg,RM
        (operand,reg,_) <- readModRM
        val <- readW operand
        writeReg (decodeReg8 reg) (val :: Word8)
      0x8B -> do                                        -- 16b MOV reg,RM
        (operand,reg,_) <- readModRM
        val <- readW operand
        writeReg (decodeReg16 reg) (val :: Word16)
      0xC3 -> pop16 >>= writeReg regIp                  -- RET
      0xC7 -> do (operand,_,_) <- readModRM             -- 16b MOV RM,imm
                 readInstr16 >>= writeW operand
      0xE8 -> readInstr16 >>= incrIP >>= push16         -- CALL relative
      0xEB -> jump True                                 -- JMP short
      0xF4 -> fail "Halting and Catching Fire"          -- HLT
      0xF9 -> writeCpu cf True                          -- STC
      0xFE -> do                                        -- 8-bit INC/DEC RM
        (operand,_,op) <- readModRM
        alu byte (readW operand) (return $ 1-2*op) operand
            (\a b -> return (True,Nothing,a+b)) -- kinda duplicate :(

------------------------------------------------------------

main = do
  rawRam <- (++ repeat 0) . B.unpack <$> B.getContents
  putStr $ unlines $ runST $ do
    cpu <- newCPU rawRam
    flip runReaderT cpu $ runMaybeT $ do
      writeReg regSp (0x100 :: Word16)
      forever processInstr

    -- Next three lines is the screen dump extraction.
    forM [0..25] $ \i -> forM [0..79] $ \j -> do
      c <- chr . fromIntegral <$> readArray (ram cpu) (0x8000 + 80*i + j)
      return $ bool ' ' c (isPrint c)

Đầu ra cho nhị phân mẫu được cung cấp phù hợp hoàn hảo với đặc điểm kỹ thuật. Dùng thử bằng cách sử dụng một lời mời như:

runhaskell 8086.hs <8086.bin

Hầu hết các hoạt động không được thực hiện sẽ chỉ dẫn đến một lỗi khớp mẫu.

Tôi vẫn có ý định tính thêm một chút nữa và thực hiện đầu ra trực tiếp thực tế bằng những lời nguyền.

Cập nhật 1: đã giảm xuống còn 234 dòng. Tổ chức tốt hơn mã theo chức năng, sắp xếp lại những gì có thể, đã cố gắng bám vào 80 cột. Và tái cấu trúc ALU nhiều lần.

Cập nhật 2: đã năm năm, tôi đã tìm ra một bản cập nhật để biên dịch nó hoàn hảo trên GHC mới nhất có thể theo thứ tự. Dọc đường:

  • đã thoát khỏi thang máyM, nângM2 và như vậy. Tôi thích có <$><*>trong khúc dạo đầu.
  • Data.Bool và Data.ByteString, tiết kiệm một chút và dọn dẹp.
  • Thanh ghi IP từng là đặc biệt (không thể cài đặt được), bây giờ nó nằm trong tệp đăng ký. Nó không có ý nghĩa 8086 nhiều, nhưng hey tôi là một người chơi gôn.
  • Bây giờ tất cả là mã dựa trên ST thuần túy. Từ quan điểm chơi gôn, điều này thật tệ, bởi vì nó tạo ra rất nhiều chữ ký loại cần thiết. Mặt khác, tôi đã có một hàng với lương tâm của tôi và tôi đã mất, vì vậy bây giờ bạn có được mã dài, sạch sẽ.
  • Vì vậy, bây giờ điều này là theo dõi git.
  • Thêm ý kiến ​​nghiêm túc hơn. Kết quả là, cách tôi đếm các dòng đã thay đổi: Tôi đang bỏ các dòng bình luận trống rỗng và thuần túy. Tôi đảm bảo tất cả các dòng nhưng nhập khẩu dài dưới 80 ký tự. Tôi sẽ không bỏ chữ ký loại vì cái tôi đã bỏ đi thực sự cần thiết để biên dịch đúng (cảm ơn bạn rất nhiều về độ sạch ST).

Như các nhận xét về mã cho biết, 5 dòng (nhập dữ liệu.Char, ánh xạ đăng ký 8 bit và kết xuất màn hình) nằm ngoài thông số kỹ thuật, vì vậy bạn rất sẵn lòng giảm giá chúng nếu bạn cảm thấy quá nghiêng :-)


3
Đẹp một. Nó thực sự ngắn, đặc biệt là so với giải pháp của tôi và giải pháp khác. Mã của bạn trông cũng rất tốt, mặc dù tôi cần học Haskell trước.
sao chép

3
Công việc tốt đẹp! Rất ngắn. Tôi nên học haskell.
ja

Có gì .|.? / 10char
Soham Chowdhury

@octatoan hoạt động được biết đến trong x86 opcodes là OR.
JB

46

C - 7143 dòng (CPU 3162 dòng)

EDIT: Bản dựng Windows hiện có các menu thả xuống để thay đổi đĩa ảo.

Tôi đã viết một trình giả lập PC 80186 / V20 đầy đủ (với CGA / MCGA / VGA, blaster âm thanh, adlib, chuột, v.v.), nó không phải là một điều tầm thường để mô phỏng 8086 bằng mọi cách. Phải mất nhiều tháng để có được hoàn toàn chính xác. Đây là mô-đun CPU chỉ ra khỏi trình giả lập của tôi.

http://sourceforge.net/p/fake86/code/ci/master/tree/src/fake86/cpu.c

Tôi sẽ là người đầu tiên thừa nhận tôi sử dụng quá nhiều biến toàn cục trong trình giả lập này. Tôi bắt đầu viết bài này khi tôi vẫn còn khá mới với C, và nó cho thấy. Tôi cần phải dọn dẹp một vài trong số đó. Hầu hết các tệp nguồn khác trong đó trông không được đẹp lắm.

Bạn có thể xem tất cả các mã (và một số ảnh chụp màn hình, một ở bên dưới) thông qua đây: http://sourceforge.net/p/fake86

Tôi sẽ rất rất vui khi được giúp đỡ bất kỳ ai khác muốn viết riêng cho họ, bởi vì điều đó rất thú vị và bạn học được RẤT NHIỀU về CPU! Tuyên bố miễn trừ trách nhiệm: Tôi đã không thêm phần mô phỏng 8080 của V20 vì nó gần như chưa bao giờ được sử dụng trong chương trình PC. Có vẻ như rất nhiều công việc không có lợi.

Máy bay chiến đấu đường phố 2!


3
Làm tốt lắm! Các trò chơi có thực sự chạy ở tốc độ tối đa?
sao chép

1
Cảm ơn. Vâng, nó chạy nhanh hơn nhiều lần so với 8088. Trên một hệ thống hiện đại, nó có thể thực hiện tốc độ như 486. Trên một bộ xử lý thực sự tốt, nó giống như một Pentium cấp thấp. Thật không may, việc mô phỏng CPU không thể được đa luồng thực sự. Tôi làm tất cả các kết xuất video trong chủ đề riêng của nó mặc dù. Tôi cũng đã chạy nó trên PowePC G3 400 MHz cũ của mình, với tốc độ xuống tới 8088.
Mike C

1
Tuyệt vời! Tôi cũng muốn thực hiện nhiều mã op và phân khúc hơn; tuy nhiên, không thể tìm thấy rất nhiều chương trình thử nghiệm để chạy trên nó. Bạn đã tải về các bản rom cũ?
Dave C

1
Dave, thực sự không có sự thiếu hụt nghiêm trọng 8086 bài kiểm tra ngoài kia một cách đáng ngạc nhiên khi bạn cũng phát hiện ra. Cách tôi đã làm là bắt đầu bằng cách làm cho ROM BIOS BIOS chung chạy chính xác. Nếu nhiều công việc, phân khúc của bạn có khả năng tốt. Sau đó, nó chỉ gỡ lỗi cho đến khi DOS bắt đầu hoạt động ... sau đó chuyển sang ứng dụng và trò chơi! :)
Mike C

1
@MikeC Tôi muốn một số trợ giúp hoặc người mới bắt đầu! (Pun dự định: P). Tôi đã là một nhà phát triển Ứng dụng Máy tính để bàn và Web trong nhiều năm nay và dần dần tôi đã đến một điểm mà tôi có mã nguồn linux. Tôi thường hiểu làm thế nào nhiều phần của một chức năng hệ điều hành và tôi đã có thể chơi với các dự án hệ điều hành đồ chơi nhỏ. Nhưng tương tác với phần cứng trực tiếp chỉ làm tôi khó hiểu!
gideon

41

Bản thảo (130 200 367 517 531 222 246 dòng)

Vẫn đang trong quá trình hoàn thành, nhưngTôi muốn hiển thị một số mã trong một nỗ lực để khuyến khích những người khác hiển thị một số mã .

Bộ thanh ghi được biểu diễn dưới dạng một chuỗi, vì vậy các thanh ghi có kích thước byte và từ khác nhau có thể chồng lên nhau một cách tự nhiên bằng cách tham khảo các chuỗi con. Các lớp nền được sử dụng như các con trỏ trong suốt, do đó một thanh ghi và một vị trí bộ nhớ (chuỗi con của chuỗi bộ nhớ) có thể được xử lý thống nhất trong các hàm toán tử.

Sau đó, có một số từ để nhận và lưu trữ dữ liệu (byte hoặc từ) từ một "con trỏ", từ bộ nhớ, từ mem [(IP)] (tăng IP). Sau đó, có một vài hàm để tìm nạp byte MOD-REG-R / M và đặt các biến REG và R / M và MOD, và giải mã chúng bằng các bảng. Sau đó, các hàm toán tử, được khóa với byte opcode. Vì vậy, vòng lặp thực hiện là đơn giản fetchb load exec.

Tôi chỉ có một số opcodes được triển khai, nhưng gNhận được giải mã toán hạng cảm thấy như một cột mốc mà tôi muốn chia sẻ nó.

chỉnh sửa: Đã thêm từ để mở rộng số âm. Nhiều opcodes hơn. Sửa lỗi trong bài tập đăng ký. Bình luận. Vẫn làm việc trên cờ và điền vào các toán tử. Đầu ra trình bày một số lựa chọn: xuất văn bản thành thiết bị xuất chuẩn khi kết thúc, xuất liên tục bằng mã vt100, xuất ra cửa sổ hình ảnh bằng phông chữ CP437.

chỉnh sửa: Viết xong, bắt đầu gỡ lỗi. Nó được bốn điểm đầu tiên của đầu ra! Sau đó mang đi sai. Ngái ngủ.

chỉnh sửa: Tôi nghĩ rằng tôi đã sắp xếp Cờ mang theo. Một số câu chuyện đã xảy ra trên comp.lang.postscript . Tôi đã thêm một số thiết bị gỡ lỗi và đầu ra đi vào cửa sổ đồ họa (sử dụng phông chữ Code-Page 437 được viết trước đó của tôi ), vì vậy đầu ra văn bản có thể chứa đầy dấu vết và kết xuất. Nó viết "Xin chào thế giới!" và sau đó có sự quan tâm đáng ngờ đó. Sau đó, toàn bộ lotta nothin '. :( Chúng tôi sẽ đến đó. Cảm ơn tất cả các khuyến khích!

chỉnh sửa: Chạy thử nghiệm để hoàn thành. Một vài lỗi cuối cùng là: XCHG thực hiện 2 {read store} lặp lại tất nhiên các bản sao thay vì trao đổi, VÀ không đặt cờ, (FE) INC cố gắng lấy một từ từ một con trỏ byte.

chỉnh sửa: Tổng số viết lại từ đầu bằng cách sử dụng bảng ngắn gọn từ hướng dẫn sử dụng ( đã chuyển sang một trang mới! ). Tôi bắt đầu nghĩ rằng bao thanh toán ra khỏi cửa hàng từ các opcodes là một ý tưởng tồi, nhưng nó đã giúp giữ cho optab đẹp. Không có ảnh chụp màn hình lần này. Tôi đã thêm một bộ đếm hướng dẫn và một trình kích hoạt mod để kết xuất bộ nhớ video, để nó dễ dàng xen kẽ với thông tin gỡ lỗi.

chỉnh sửa: Chạy chương trình thử nghiệm, một lần nữa! Một vài lỗi cuối cùng cho việc viết lại ngắn hơn là bỏ qua việc mở rộng byte ngay lập tức trong opcodes 83 (nhóm "Ngay lập tức") và EB (JMP ngắn). Tăng 24 dòng bao gồm các thói quen sửa lỗi bổ sung cần thiết để theo dõi những lỗi cuối cùng.

%!
%a8086.ps Draught2:BREVITY
[/NULL<0000>/nul 0
/mem 16#ffff string %16-bit memory
/CF 0 /OF 0 /AF 0 /ZF 0 /SF 0
/regs 20 string >>begin %register byte storage
0{AL AH CL CH DL DH BL BH}{regs 2 index 1 getinterval def 1 add}forall pop
0{AX CX DX BX SP BP SI DI IP FL}{regs 2 index 2 getinterval def 2 add}forall pop

%getting and fetching
[/*b{0 get} %get byte from pointer
/*w{dup *b exch 1 get bbw} %get word from pointer
/*{{*b *w}W get exec} %get data(W) from pointer
/bbw{8 bitshift add} %lo-byte hi-byte -> word
/shiftmask{2 copy neg bitshift 3 1 roll 1 exch bitshift 1 sub and}
/fetchb{IP *w mem exch get bytedump   IP dup *w 1 add storew} % byte(IP++)
/fetchw{fetchb fetchb bbw} % word(IP),IP+=2

%storing and accessing
/storeb{16#ff and 0 exch put} % ptr val8 -> -
/storew{2 copy storeb -8 bitshift 16#ff and 1 exch put} % ptr val16 -> -
/stor{{storeb storew}W get exec} % ptr val(W) -> -
/memptr{16#ffff and mem exch {1 2}W get getinterval} % addr -> ptr(W)

%decoding the mod-reg-reg/mem byte
/mrm{fetchb 3 shiftmask /RM exch def 3 shiftmask /REG exch def /MOD exch def}
/REGTAB[[AL CL DL BL AH CH DH BH][AX CX DX BX SP BP SI DI]]
/decreg{REGTAB W get REG get} % REGTAB[W][REG]
%2 indexes,   with immed byte,   with immed word
/2*w{exch *w exch *w add}/fba{fetchb add}/fwa{fetchw add}
/RMTAB[[{BX SI 2*w}{BX DI 2*w}{BP SI 2*w}{BP DI 2*w}
    {SI *w}{DI *w}{fetchw}{BX *w}]
[{BX SI 2*w fba}{BX DI 2*w fba}{BP SI 2*w fba}{BP DI 2*w fba}
    {SI *w fba}{DI *w fba}{BP *w fba}{BX *w fba}]
[{BX SI 2*w fwa}{BX DI 2*w fwa}{BP SI 2*w fwa}{BP DI 2*w fwa}
    {SI *w fwa}{DI *w fwa}{BP *w fwa}{BX *w fwa}]]
/decrm{MOD 3 eq{REGTAB W get RM get} %MOD=3:register mode
    {RMTAB MOD get RM get exec memptr}ifelse} % RMTAB[MOD][RM] -> addr -> ptr

%setting and storing flags
/flagw{OF 11 bitshift SF 7 bitshift or ZF 6 bitshift or AF 4 bitshift CF or}
/wflag{dup 1 and /CF exch def dup -4 bitshift 1 and /AF exch def
    dup -6 bitshift 1 and /ZF exch def dup -7 bitshift 1 and /SF exch def
    dup -11 bitshift 1 and /OF exch def}
/nz1{0 ne{1}{0}ifelse}
/logflags{/CF 0 def /OF 0 def /AF 0 def %clear mathflags
    dup {16#80 16#8000}W get and nz1 /SF exch def
    dup {16#ff 16#ffff}W get and 0 eq{1}{0}ifelse /ZF exch def}
/mathflags{{z y x}{exch def}forall
    /CF z {16#ff00 16#ffff0000}W get and nz1 def
    /OF z x xor z y xor and {16#80 16#8000}W get and nz1 def
    /AF x y xor z xor 16#10 and nz1 def
    z} %leave the result on stack

%opcodes (each followed by 'stor')  %% { OPTAB fetchb get exec stor } loop
/ADD{2 copy add logflags mathflags}
/OR{or logflags}
/ADC{CF add ADD}
/SBB{D 1 xor {exch}repeat CF add 2 copy sub logflags mathflags}
/AND{and logflags}
/SUB{D 1 xor {exch}repeat 2 copy sub logflags mathflags}
/XOR{xor logflags}
/CMP{3 2 roll pop NULL 3 1 roll SUB} %dummy stor target
/INC{t CF exch dup * 1 ADD 3 2 roll /CF exch def}
/DEC{t CF exch dup * 1 SUB 3 2 roll /CF exch def}
/PUSH{SP dup *w 2 sub storew   *w SP *w memptr exch}
/POP{SP *w memptr *w   SP dup *w 2 add storew}

/jrel{w {CBW IP *w add IP exch}{NULL exch}ifelse}
/JO{fetchb OF 1 eq jrel }
/JNO{fetchb OF 0 eq jrel }
/JB{fetchb CF 1 eq jrel }
/JNB{fetchb CF 0 eq jrel }
/JZ{fetchb ZF 1 eq jrel }
/JNZ{fetchb ZF 0 eq jrel }
/JBE{fetchb CF ZF or 1 eq jrel }
/JNBE{fetchb CF ZF or 0 eq jrel }
/JS{fetchb SF 1 eq jrel }
/JNS{fetchb SF 0 eq jrel }
/JL{fetchb SF OF xor 1 eq jrel }
/JNL{fetchb SF OF xor 0 eq jrel }
/JLE{fetchb SF OF xor ZF or 1 eq jrel }
/JNLE{fetchb SF OF xor ZF or 0 eq jrel }

/bw{dup 16#80 and 0 ne{16#ff xor 1 add 16#ffff xor 1 add}if}
/IMMTAB{ADD OR ADC SBB AND SUB XOR CMP }cvlit
/immed{ W 2 eq{ /W 1 def
            mrm decrm dup * fetchb bw
    }{ mrm decrm dup * {fetchb fetchw}W get exec }ifelse
    exch IMMTAB REG get dup == exec }

%/TEST{ }
/XCHG{3 2 roll pop 2 copy exch * 4 2 roll * stor }
/AXCH{w dup AX XCHG }
/NOP{ NULL nul }
/pMOV{D{exch}repeat pop }
/mMOV{ 3 1 roll pop pop }
/MOV{ }
/LEA{w mrm decreg RMTAB MOD get RM get exec }

/CBW{dup 16#80 and 0 ne {16#ff xor 1 add 16#ffff xor 1 add } if }
/CWD{dup 16#8000 and 0 ne {16#ffff xor 1 add neg } if }
/CALL{w xp /xp{}def fetchw IP PUSH storew IP dup *w 3 2 roll add dsp /dsp{}def }
%/WAIT{ }
/PUSHF{NULL dup flagw storew 2 copy PUSH }
/POPF{NULL dup POP *w wflag }
%/SAHF{ }
%/LAHF{ }

%/MOVS{ }
%/CMPS{ }
%/STOS{ }
%/LODS{ }
%/SCAS{ }
/RET{w IP POP storew SP dup * 3 2 roll add }
%/LES{ }
%/LDS{ }

/JMP{IP dup fetchw exch *w add}
/sJMP{IP dup fetchb bw exch *w add}

/HLT{exit}
/CMC{/CF CF 1 xor def NULL nul}
/CLC{/CF 0 def NULL nul}
/STC{/CF 1 def NULL nul}

/NOT{not logflags }
/NEG{neg logflags }
/GRP1TAB{TEST --- NOT NEG MUL IMUL DIV IDIV } cvlit
/Grp1{mrm decrm dup * GRP1TAB REG get
dup ==
exec }
/GRP2TAB{INC DEC {id CALL}{l id CALL}{id JMP}{l id JMP} PUSH --- } cvlit
/Grp2{mrm decrm GRP2TAB REG get
dup ==
exec }

%optab shortcuts
/2*{exch * exch *}
/rm{mrm decreg decrm D index 3 1 roll 2*} % fetch,decode mrm -> dest *reg *r-m
/rmp{mrm decreg decrm D index 3 1 roll} % fetch,decode mrm -> dest reg r-m
/ia{ {{AL dup *b fetchb}{AX dup *w fetchw}}W get exec } %immed to accumulator
/is{/W 2 def}
/b{/W 0 def} %select byte operation
/w{/W 1 def} %select word operation
/t{/D 1 def} %dest = reg
/f{/D 0 def} %dest = r/m
/xp{} /dsp{}
%/far{ /xp { <0000> PUSH storew } /dsp { fetchw pop } def }
/i{ {fetchb fetchw}W get exec }

/OPTAB{
{b f rm ADD}{w f rm ADD}{b t rm ADD}{w t rm ADD}{b ia ADD}{w ia ADD}{ES PUSH}{ES POP} %00-07
 {b f rm OR}{w f rm OR}{b t rm OR}{w t rm OR}{b ia OR}{w ia OR}{CS PUSH}{}            %08-0F
{b f rm ADC}{w f rm ADC}{b t rm ADC}{w t rm ADC}{b ia ADC}{w ia ADC}{SS PUSH}{SS POP} %10-17
 {b f rm SBB}{w f rm SBB}{b t rm SBB}{w t rm SBB}{b ia SBB}{w ia SBB}{DS PUSH}{DS POP}%18-1F
{b f rm AND}{w f rm AND}{b t rm AND}{w t rm AND}{b ia AND}{w ia AND}{ES SEG}{DAA}     %20-27
 {b f rm SUB}{w f rm SUB}{b t rm SUB}{w t rm SUB}{b ia SUB}{w ia SUB}{CS SEG}{DAS}    %28-2F
{b f rm XOR}{w f rm XOR}{b t rm XOR}{w t rm XOR}{b ia XOR}{w ia XOR}{SS SEG}{AAA}     %30-37
 {b f rm CMP}{w f rm CMP}{b t rm CMP}{w t rm CMP}{b ia CMP}{w ia CMP}{DS SEG}{AAS}    %38-3F
{w AX INC}{w CX INC}{w DX INC}{w BX INC}{w SP INC}{w BP INC}{w SI INC}{w DI INC}      %40-47
 {w AX DEC}{w CX DEC}{w DX DEC}{w BX DEC}{w SP DEC}{w BP DEC}{w SI DEC}{w DI DEC}     %48-4F
{AX PUSH}{CX PUSH}{DX PUSH}{BX PUSH}{SP PUSH}{BP PUSH}{SI PUSH}{DI PUSH}              %50-57
 {AX POP}{CX POP}{DX POP}{BX POP}{SP POP}{BP POP}{SI POP}{DI POP}                     %58-5F
{}{}{}{}{}{}{}{}  {}{}{}{}{}{}{}{}                                                    %60-6F
{JO}{JNO}{JB}{JNB}{JZ}{JNZ}{JBE}{JNBE} {JS}{JNS}{JP}{JNP}{JL}{JNL}{JLE}{JNLE}         %70-7F

{b f immed}{w f immed}{b f immed}{is f immed}{b TEST}{w TEST}{b rmp XCHG}{w rmp XCHG}   %80-87
 {b f rm pMOV}{w f rm pMOV}{b t rm pMOV}{w t rm pMOV}                                 %88-8B
   {sr f rm pMOV}{LEA}{sr t rm pMOV}{w mrm decrm POP}                                 %8C-8F
{NOP}{CX AXCH}{DX AXCH}{BX AXCHG}{SP AXCH}{BP AXCH}{SI AXCH}{DI AXCH}             %90-97
 {CBW}{CWD}{far CALL}{WAIT}{PUSHF}{POPF}{SAHF}{LAHF}                                  %98-9F
{b AL m MOV}{w AX m MOV}{b m AL MOV}{b AX m MOV}{MOVS}{MOVS}{CMPS}{CMPS}              %A0-A7
 {b i a TEST}{w i a TEST}{STOS}{STOS}{LODS}{LODS}{SCAS}{SCAS}                         %A8-AF
{b AL i MOV}{b CL i MOV}{b DL i MOV}{b BL i MOV}                                      %B0-B3
 {b AH i MOV}{b CH i MOV}{b DH i MOV}{b BH i MOV}                                     %B4-B7
 {w AX i MOV}{w CX i MOV}{w DX i MOV}{w BX i MOV}                                     %B8-BB
 {w SP i MOV}{w BP i MOV}{w SI i MOV}{w DI i MOV}                                     %BC-BF
{}{}{fetchw RET}{0 RET}{LES}{LDS}{b f rm i mMOV}{w f rm i mMOV}                       %C0-B7
 {}{}{fetchw RET}{0 RET}{3 INT}{fetchb INT}{INTO}{IRET}                               %C8-CF
{b Shift}{w Shift}{b v Shift}{w v Shift}{AAM}{AAD}{}{XLAT}                            %D0-D7
 {0 ESC}{1 ESC}{2 ESC}{3 ESC}{4 ESC}{5 ESC}{6 ESC}{7 ESC}                             %D8-DF
{LOOPNZ}{LOOPZ}{LOOP}{JCXZ}{b IN}{w IN}{b OUT}{w OUT}                                 %E0-E7
 {CALL}{JMP}{far JMP}{sJMP}{v b IN}{v w IN}{v b OUT}{v w OUT}                         %E8-EF
{LOCK}{}{REP}{z REP}{HLT}{CMC}{b Grp1}{w Grp}                                         %F0-F7
 {CLC}{STC}{CLI}{STI}{CLD}{STD}{b Grp2}{w Grp2}                                       %F8-FF
}cvlit

/break{ /hook /pause load def }
/c{ /hook {} def }
/doprompt{
    (\nbreak>)print
    flush(%lineedit)(r)file
    cvx {exec}stopped pop }
/pause{ doprompt }
/hook{}

/stdout(%stdout)(w)file
/bytedump{ <00> dup 0 3 index put stdout exch writehexstring ( )print }
/regdump{ REGTAB 1 get{ stdout exch writehexstring ( )print }forall
    stdout IP writehexstring ( )print
    {(NC )(CA )}CF get print
    {(NO )(OV )}OF get print
    {(NS )(SN )}SF get print
    {(NZ )(ZR )}ZF get print
    stdout 16#1d3 w memptr writehexstring
    (\n)print
}
/mainloop{{
    %regdump
    OPTAB fetchb get
    dup ==
    exec
    %pstack flush
    %hook
    stor
    /ic ic 1 add def ictime
}loop}

/printvideo{
    0 1 28 {
        80 mul 16#8000 add mem exch 80 getinterval {
            dup 0 eq { pop 32 } if
                    dup 32 lt 1 index 126 gt or { pop 46 } if
            stdout exch write
        } forall (\n)print
    } for
    (\n)print
}
/ic 0
/ictime{ic 10 mod 0 eq {onq} if}
/timeq 10
/onq{ %printvideo
}
>>begin
currentdict{dup type/arraytype eq 1 index xcheck and
    {bind def}{pop pop}ifelse}forall

SP 16#100 storew
(codegolf.8086)(r)file mem readstring pop
pop[

mainloop
printvideo

%eof

Và đầu ra (với phần cuối của đầu ra gỡ lỗi viết tắt).

75 {JNZ}
19 43 {w BX INC}
83 {is f immed}
fb 64 CMP
76 {JBE}
da f4 {HLT}
.........
Hello, world!
0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~


################################################################################
##                                                                            ##
##  0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987                          ##
##                                                                            ##
##  0 1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 289 324 361 400     ##
##                                                                            ##
##  2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97    ##
##                                                                            ##
##                                                                            ##
##                                                                            ##
##                                                                            ##
##                                                                            ##
##                                                                            ##
##                                                                            ##
##                                                                            ##
##                                                                            ##
##                                                                            ##
##                                                                            ##
##                                                                            ##
################################################################################





GS<1>

5
Tôi tự hỏi ... Có phải phím nóng để đóng ứng dụng alt-F4 không F4h là opcode 8086 HLT?
luser droog

5
Tôi chỉ muốn nói với bạn rằng bạn hoàn toàn tuyệt vời khi thực hiện điều này trong Postcript.
cemper93

1
Mã đó ngắn . Nó xứng đáng được nâng cao hơn. Có của tôi, để bắt đầu.
JB

2
chờ đã ... bài viết là một ngôn ngữ lập trình?! ;)
n611x007

32

Javascript

Tôi đang viết một trình giả lập 486 trong javascript lấy cảm hứng từ jslinux. Nếu tôi biết nó sẽ có bao nhiêu công việc, có lẽ tôi sẽ không bao giờ bắt đầu, nhưng bây giờ tôi muốn hoàn thành nó.

Sau đó, tôi đã gặp thử thách của bạn và rất vui khi có chương trình 8086 để thử nghiệm.

http://i.stack.imgur.com/54a6S.png

Bạn có thể "thấy" nó chạy trực tiếp tại đây: http://codinguncut.com/jsmachine/

Tôi đã có một vấn đề khi in ra bộ đệm đồ họa. Khi cần có khoảng trắng, bộ nhớ chứa các phần tử "00". Là chính xác để giải thích "0x00" là không gian hoặc tôi có một lỗi trong trình giả lập của mình?

Chúc mừng

Julian


Thật thú vị, tôi thực sự biết tên của bạn từ Screencasts của bạn, tôi đã xem sau phản hồi của Haskell trong thử thách này (và tôi cũng đã bắt đầu trình giả lập x86 trong Javascript). Có, không byte nên xuất hiện dưới dạng khoảng trắng. Tôi cũng đã thêm ảnh chụp màn hình vào bài viết của bạn. +1 dù sao :-)
sao chép

@Johannes Tôi đã xem nhanh mã mycpu-min.js. Từ những gì tôi có thể nói bạn chỉ sử dụng một vài ý tưởng từ cpux86.js (của jslinux của FB). Chúc mừng! Một công việc tốt. Bất kỳ cơ hội để xem mycpu.js không được biên dịch ở đâu đó? Hy vọng trên github.com/codinguncut
Yauhen Yakimovich

@YauhenYakimovich Không, tôi chưa sử dụng lại bất kỳ mã jslinux nào. Tôi đã thực hiện cho đến nay tất cả 286 hướng dẫn trừ phân trang và phân đoạn (mmu). Kế hoạch của tôi là phát hành mã theo GPL, nhưng tôi thực sự muốn thương mại hóa ý tưởng để chạy tức là Freedo hoặc Reactos vì vậy tôi vẫn không chắc chắn về việc cấp phép. Sự thật là, tôi sẽ mất nhiều thời gian để thực hiện đầy đủ bộ nhớ mgmt. và sau đó một thời gian dài để làm cho nó chạy ở tốc độ. Tôi chắc chắn sẽ chia sẻ tại github.com/codinguncut. Cảm ơn phản hồi của bạn, Johannes
fluquid

1
Các liên kết bị hỏng cho tôi. (IE trên windows 8)
Conor O'Brien

Điều này đến rất muộn. Ký tự số 0 là một không gian khác trong RAM video.
Joshua

30

C ++

Tôi muốn gửi mục của chúng tôi cho thử thách mã này. Nó được viết bằng c ++ và chạy chương trình thử nghiệm một cách hoàn hảo. Chúng tôi đã triển khai 90% mã Op Byte và phân đoạn cơ bản (một số bị vô hiệu hóa vì nó không hoạt động với chương trình thử nghiệm).

Chương trình Viết lên: http://davecarruth.com/index.php/2012/04/15/creating-an-8086-emulator

Bạn có thể tìm thấy mã trong một tệp zip ở cuối Bài đăng trên Blog.

Ảnh chụp màn hình thực hiện chương trình kiểm tra: nhập mô tả hình ảnh ở đây

Điều này mất khá nhiều thời gian ... nếu bạn có bất kỳ câu hỏi hoặc nhận xét nào thì hãy nhắn tin cho tôi. Đó chắc chắn là một bài tập tuyệt vời trong lập trình đối tác.


3
Thật tốt khi mọi người vui vẻ với thử thách này :) Chỉ cần một vài lưu ý: Chương trình thử nghiệm của tôi sẽ hoạt động (và đã được thử nghiệm) với tất cả các phân đoạn bằng không. Nhìn vào một số mã của bạn, tôi nhận thấy rằng ret immhướng dẫn là sai (xem tại đây ) và bạn đang thiếu 0xffnhóm. Tôi thích thông báo lỗi của bạn mặc dù: ném "Giá trị ngay lập tức không thể lưu trữ giá trị, chậm lại.";
sao chép

Chúng tôi có hai vấn đề chính với chương trình thử nghiệm: 1) Phân đoạn - khi có CALL chúng tôi đã đẩy CS lên ngăn xếp ... một trong những chức năng trong chương trình thử nghiệm không như thế này. 2) Chương trình thử nghiệm dự kiến ​​bộ nhớ của chúng tôi sẽ được khởi tạo về không. Dù sao, chúng tôi đã có rất nhiều niềm vui, cảm ơn rất nhiều vì đã đăng!
Dave C

Bạn có thể đã mắc lỗi ở đó: Nhảy gần ( 0xE8 ) không đẩy csđăng ký
sao chép

Đó sẽ là vấn đề, bắt tốt! Bạn có vẻ rất có kinh nghiệm với 8086, bạn đã lập trình cho nó?
Dave C

1
Tôi thực sự đang làm việc trên một dự án giả lập x86. Nó chạy freedo khá tốt và tôi hiện đang làm việc với sự hỗ trợ đầy đủ 32 bit; chỉ không đăng ở đây vì nó có thể không công bằng với các áp phích khác (và mã nguồn hơi bị rối).
sao chép

28

C

Thử thách lớn và lần đầu tiên của tôi. Tôi đã tạo một tài khoản chỉ vì thử thách hấp dẫn tôi rất nhiều. Mặt trái là tôi không thể ngừng nghĩ về thử thách khi tôi có công việc thực tế, trả tiền, lập trình.

Tôi cảm thấy bắt buộc phải chạy mô phỏng 8086, nhưng đó là một thách thức khác ;-)

Mã được viết bằng ANSI-C, vì vậy chỉ cần biên dịch / liên kết các tệp .c với nhau, chuyển vào nhị phân codegolf và đi.

nguồn nén

nhập mô tả hình ảnh ở đây


Làm tốt lắm RichTX!
Dave C

Cảm ơn Dave. Bạn cũng vậy. Tôi đã không hiểu được kỳ vọng làm cho mã càng nhỏ càng tốt khi tôi bắt đầu, nhưng nó vẫn là một thách thức.
RichTX

+1 Tôi đã xem mã của bạn để tìm ra cách hoạt động của cờ mang.
luser droog

Các liên kết là xuống cho tôi.
Tyilo

25

Dòng C ++ 1064

Dự án tuyệt vời. Tôi đã làm một trình giả lập Intellivision từ nhiều năm trước, vì vậy thật tuyệt khi uốn cong cơ bắp đập của tôi một lần nữa.

Sau khoảng một tuần làm việc, tôi không thể hào hứng hơn khi điều này xảy ra:

.........
╤╤╤╤╤╤╤╤╤╤╤╤╤╤
0123456789:;? @ ABCDEFGHIJKLMNOPQRSTUVWXYZ [\] ^ _ `abcdefghijklmnopqrstuvwxyz {|} ~


###################################################### ################################
###################################################### #######################
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

    0 1 4 9 a ♣ b ♠ cd ♦ f ☺h `§☺b, ♦ d E f` i ↓ ♣ b 8 ♠ e Y h ↑ ♦ b = ☺f `

    2 3 4 5 6 7 8 9 a ☺a a ♥ a ♦ a ♣ a ♠ a aa ab ☺b b ♥ b ♦ b ♣ b ♠ b bb b
 c ☺c c ♥ c ♦ c ♣ c ♠ c cc cd d ☻d ♥ d ♦ d ♣ d ♠ d dd de ☺e ☻e ♥ e ♦ e ♣ e e
 ee ef f ☻f ♥ f ♦ f ♣ f ♠ f ff fg g g ♥ g ♦ g ♣ g ♠ gggh ☺h ☻h ♥
h ♦ h h ♠ h hh hi ☺i ☻i ♥ i ♦ i ♣ i ♠ i ii i `

Một chút gỡ lỗi sau và ... SHAZAM! nhập mô tả hình ảnh ở đây

Ngoài ra, tôi đã xây dựng lại chương trình thử nghiệm ban đầu mà không cần tiện ích mở rộng 80386, vì tôi muốn xây dựng trình giả lập của mình đúng với 8086 và không làm mờ trong bất kỳ hướng dẫn bổ sung nào. Liên kết trực tiếp đến mã ở đây: Tệp zip .

Ok tôi có quá nhiều niềm vui với điều này. Tôi đã phá vỡ bộ nhớ và quản lý màn hình, và bây giờ màn hình cập nhật khi bộ đệm màn hình được ghi vào. Tôi đã làm một video :)

http://www.youtube.com/watch?v=qnAssaTpmnA

Cập nhật: Lần đầu tiên phân đoạn được thực hiện. Rất ít hướng dẫn được thực hiện, nhưng tôi đã kiểm tra nó bằng cách di chuyển CS / DS và SS, và mọi thứ vẫn chạy tốt.

Cũng thêm xử lý ngắt thô sơ. Rất thô sơ. Nhưng tôi đã thực hiện int 21h để in một chuỗi. Đã thêm một vài dòng vào nguồn kiểm tra và cũng tải lên.

start:
    sti
    mov ah, 9
    mov dx, joetext
    int 21h
...

joetext:
    db 'This was printed by int 21h$', 0

nhập mô tả hình ảnh ở đây

Nếu bất cứ ai có một số mã lắp ráp khá đơn giản sẽ kiểm tra các phân đoạn, tôi rất thích chơi với nó.

Tôi đang cố gắng tìm hiểu xem tôi muốn đi bao xa. Thi đua CPU đầy đủ? Chế độ VGA? Bây giờ tôi đang viết DOSBox.

12/6: Kiểm tra xem, chế độ VGA!

nhập mô tả hình ảnh ở đây


Bất kỳ cơ hội nào bạn có thể đăng mã của mình trên một trang web miễn phí không yêu cầu đăng ký? Cảm ơn
Dave C

Tôi không nhận ra rằng nó cần phải đăng ký. Xin lỗi vì điều đó! Tôi sẽ cố gắng làm điều đó khi tôi về nhà tối nay.
JoeFish

@DaveC, kiểm tra chỉnh sửa mới nhất.
JoeFish

Tôi tự hỏi nếu có một cổng camelForth. Điều đó sẽ kiểm tra các phân khúc.
luser droog

Thật tuyệt vời! +1 lại. btw, có một cổng 8086 của lạc đà ra bradrodriguez.com/papers/index.html .
luser droog 7/12 '

25

C ++ - 4455 dòng

Và không, tôi không chỉ làm các yêu cầu của câu hỏi. Tôi đã thực hiện ENTIRE 8086, bao gồm 16 opcode chưa từng có trước đây. reenigne đã giúp tìm ra những opcodes đó.

https://github.com/Alegend45/IBM5150


tập tin 4455 ở đâu? oh, tôi đã tìm thấy nó các #include "cpu.h"khó có thể nhìn thấy.
kẻ lừa đảo người lái xe

2
(w) tuyên bố chuyển đổi thần thánh!
kẻ lừa đảo người lái xe

Vâng, nó cũng sẽ trở nên tồi tệ hơn, vì tôi cũng sắp có hỗ trợ NEC V20.
Darius Goad

Tôi đã xem qua blog của reenigne . Không thể tìm thấy bất cứ điều gì về các opcodes thêm. Nó có trực tuyến ở đâu đó không?
kẻ lừa đảo người lái xe

1
Anh ấy đã không cập nhật blog của mình trong một thời gian. Tuy nhiên, anh ấy ở # ibm5150 trên EFNET, vì vậy bạn có thể hỏi anh ấy ở đó.
Darius Goad

20

Javascript - 4.404 dòng

Tôi tình cờ thấy bài đăng này khi nghiên cứu thông tin cho trình giả lập của riêng tôi. Bài viết Codegolf này là hoàn toàn vô giá đối với tôi. Chương trình ví dụ và lắp ráp liên quan giúp dễ dàng gỡ lỗi và xem những gì đang xảy ra.

Cảm ơn bạn!!!

Và đây là phiên bản đầu tiên của trình giả lập Javascript 8086 của tôi.

Đã chạy xong

Đặc trưng:

  • Tất cả các mã yêu cầu cho thử thách này cộng với một số tính năng bổ sung tương tự nhau để chúng dễ mã hóa
  • Video chế độ văn bản một phần chức năng (80x25) (chưa bị gián đoạn)
  • Chức năng ngăn xếp
  • Bộ nhớ cơ bản (không phân đoạn)
  • Gỡ lỗi khá tốt (phải có cái này)
  • Bộ mã trang 437 được đặt động tải từ biểu diễn bitmap

Bản giới thiệu

Tôi có một bản demo trực tuyến, vui lòng chơi với nó và cho tôi biết nếu bạn tìm thấy lỗi :)

http://js86emu.chadrempp.com/

Để chạy chương trình codegolf

1) nhấp vào nút cài đặt

nhập mô tả hình ảnh ở đây

2) sau đó chỉ cần nhấp vào tải (bạn có thể chơi với các tùy chọn gỡ lỗi ở đây, như bước qua chương trình). Chương trình codegolf là chương trình duy nhất có sẵn tại thời điểm này, tôi đang làm việc để có thêm trực tuyến.

nhập mô tả hình ảnh ở đây

Nguồn

Nguồn đầy đủ ở đây. https://github.com/crempp/js86emu

Tôi đã cố gắng dán can đảm của mô phỏng 8086 ở đây (như được đề xuất bởi tay nắm cửa) nhưng nó đã vượt quá giới hạn ký tự ("Cơ thể bị giới hạn ở 30000 ký tự; bạn đã nhập 158.272").

Đây là một liên kết nhanh đến mã tôi sẽ dán vào đây - https://github.com/crempp/js86emu/blob/39dbcb7106a0aaf59e003cd7f722acb4b6923d87/src/js/emu/cpus/80

*Edit - updated for new demo and repo location


Wow, thật tuyệt vời! Tuy nhiên, sẽ thật lý tưởng nếu mã nằm trong chính bài đăng của bạn, vì chúng tôi thích các bài đăng của chúng tôi được khép kín.
Doorknob

@Doorknob, tôi không chắc là tôi hiểu. Bạn muốn tôi đăng 4.400 dòng mã, trong bài viết?
crempp

Umm ... Tôi đã không nhận ra nó là rằng lâu. Có phù hợp trong giới hạn ký tự tối đa? Nếu vậy, thì có, sẽ thật tuyệt nếu bài viết của bạn được khép kín. Cảm ơn! :-)
Doorknob

13

Java

Tôi đã muốn thực hiện thử thách này quá lâu và cuối cùng tôi đã dành thời gian để làm điều đó. Đó là một trải nghiệm tuyệt vời cho đến nay và tôi tự hào thông báo rằng cuối cùng tôi đã hoàn thành nó.

Đầu ra chương trình thử nghiệm

Nguồn

Mã nguồn có sẵn trên GitHub tại NeatMonster / Intel8086 . Tôi đã cố gắng ghi lại khá nhiều thứ, với sự giúp đỡ của Hướng dẫn sử dụng gia đình holly 8086 .

Tôi dự định triển khai tất cả các mã và tính năng còn thiếu, vì vậy bạn có thể muốn kiểm tra phiên bản 1.0 cho một phiên bản chỉ có những phiên bản cần thiết cho thử thách này.

Rất cám ơn @copy!


13

Lisp phổ biến - 580 loc (438 w / o dòng trống và nhận xét)

Tôi đã sử dụng thử thách này như một cái cớ để học Common Lisp. Đây là kết quả:

;;; Program settings

(defparameter *disasm* nil "Whether to disassemble")

(defmacro disasm-instr (on-disasm &body body)
  `(if *disasm*
       ,on-disasm
       (progn ,@body)))

;;; State variables

(defparameter *ram* (make-array (* 64 1024) :initial-element 0 :element-type '(unsigned-byte 8)) "Primary segment")
(defparameter *stack* (make-array (* 64 1024) :initial-element 0 :element-type '(unsigned-byte 8)) "Stack segment")
(defparameter *flags* '(:cf 0 :sf 0 :zf 0) "Flags")
(defparameter *registers* '(:ax 0 :bx 0 :cx 0 :dx 0 :bp 0 :sp #x100 :si 0 :di 0) "Registers")
(defparameter *ip* 0 "Instruction pointer")
(defparameter *has-carried* nil "Whether the last wraparound changed the value")
(defparameter *advance* 0 "Bytes to advance IP by after an operation")

;;; Constants

(defconstant +byte-register-to-word+ '(:al (:ax nil) :ah (:ax t) :bl (:bx nil) :bh (:bx t) :cl (:cx nil) :ch (:cx t) :dl (:dx nil) :dh (:dx t)) "Mapping from byte registers to word registers")
(defconstant +bits-to-register+ '(:ax :cx :dx :bx :sp :bp :si :di) "Mapping from index to word register")
(defconstant +bits-to-byte-register+ '(:al :cl :dl :bl :ah :ch :dh :bh) "Mapping from index to byte register")

;;; Constant mappings

(defun bits->word-reg (bits)
  (elt +bits-to-register+ bits))

(defun bits->byte-reg (bits)
  (elt +bits-to-byte-register+ bits))

(defun address-for-r/m (mod-bits r/m-bits)
  (disasm-instr
      (if (and (= mod-bits #b00) (= r/m-bits #b110))
      (list :disp (peek-at-word))
      (case r/m-bits
        (#b000 (list :base :bx :index :si))
        (#b001 (list :base :bx :index :di))
        (#b010 (list :base :bp :index :si))
        (#b011 (list :base :bp :index :di))
        (#b100 (list :index :si))
        (#b101 (list :index :di))
        (#b110 (list :base :bp))
        (#b111 (list :base :bx))))
    (if (and (= mod-bits #b00) (= r/m-bits #b110))
    (peek-at-word)
    (case r/m-bits
      (#b000 (+ (register :bx) (register :si)))
      (#b001 (+ (register :bx) (register :di)))
      (#b010 (+ (register :bp) (register :si)))
      (#b011 (+ (register :bp) (register :di)))
      (#b100 (register :si))
      (#b101 (register :di))
      (#b110 (register :bp))
      (#b111 (register :bx))))))

;;; Convenience functions

(defun reverse-little-endian (low high)
  "Reverse a little-endian number."
  (+ low (ash high 8)))

(defun negative-p (value is-word)
  (or (if is-word (>= value #x8000) (>= value #x80)) (< value 0)))

(defun twos-complement (value is-word)
  (if (negative-p value is-word)
      (- (1+ (logxor value (if is-word #xffff #xff))))
      value))

(defun wrap-carry (value is-word)
  "Wrap around an carried value."
  (let ((carry (if is-word (>= value #x10000) (>= value #x100))))
    (setf *has-carried* carry)
    (if carry
    (if is-word (mod value #x10000) (mod value #x100))
    value)))

;;; setf-able locations

(defun register (reg)
  (disasm-instr reg
    (getf *registers* reg)))

(defun set-reg (reg value)
  (setf (getf *registers* reg) (wrap-carry value t)))

(defsetf register set-reg)

(defun byte-register (reg)
  (disasm-instr reg
    (let* ((register-to-word (getf +byte-register-to-word+ reg)) (word (first register-to-word)))
      (if (second register-to-word)
      (ash (register word) -8)
      (logand (register word) #x00ff)))))

(defun set-byte-reg (reg value)
  (let* ((register-to-word (getf +byte-register-to-word+ reg)) (word (first register-to-word)) (wrapped-value (wrap-carry value nil)))
    (if (second register-to-word)
    (setf (register word) (+ (ash wrapped-value 8) (logand (register word) #x00ff)))
    (setf (register word) (+ wrapped-value (logand (register word) #xff00))))))

(defsetf byte-register set-byte-reg)

(defun flag (name)
  (getf *flags* name))

(defun set-flag (name value)
  (setf (getf *flags* name) value))

(defsetf flag set-flag)

(defun flag-p (name)
  (= (flag name) 1))

(defun set-flag-p (name is-set)
  (setf (flag name) (if is-set 1 0)))

(defsetf flag-p set-flag-p)

(defun byte-in-ram (location segment)
  "Read a byte from a RAM segment."
  (elt segment location))

(defsetf byte-in-ram (location segment) (value)
  "Write a byte to a RAM segment."
  `(setf (elt ,segment ,location) ,value))

(defun word-in-ram (location segment)
  "Read a word from a RAM segment."
  (reverse-little-endian (elt segment location) (elt segment (1+ location))))

(defsetf word-in-ram (location segment) (value)
  "Write a word to a RAM segment."
  `(progn
     (setf (elt ,segment ,location) (logand ,value #x00ff))
     (setf (elt ,segment (1+ ,location)) (ash (logand ,value #xff00) -8))))

(defun indirect-address (mod-bits r/m-bits is-word)
  "Read from an indirect address."
  (disasm-instr
      (if (= mod-bits #b11) (register (if is-word (bits->word-reg r/m-bits) (bits->byte-reg r/m-bits)))
      (let ((base-index (address-for-r/m mod-bits r/m-bits)))
        (unless (getf base-index :disp)
          (setf (getf base-index :disp)
            (case mod-bits
              (#b00 0)
              (#b01 (next-instruction))
              (#b10 (next-word)))))
        base-index))
    (let ((address-base (address-for-r/m mod-bits r/m-bits)))
      (case mod-bits
    (#b00 (if is-word (word-in-ram address-base *ram*) (byte-in-ram address-base *ram*)))
    (#b01 (if is-word (word-in-ram (+ address-base (peek-at-instruction)) *ram*) (byte-in-ram (+ address-base (peek-at-instruction)) *ram*)))
    (#b10 (if is-word (word-in-ram (+ address-base (peek-at-word)) *ram*) (byte-in-ram (+ address-base (peek-at-word)) *ram*)))
    (#b11 (if is-word (register (bits->word-reg r/m-bits)) (byte-register (bits->byte-reg r/m-bits))))))))

(defsetf indirect-address (mod-bits r/m-bits is-word) (value)
  "Write to an indirect address."
  `(let ((address-base (address-for-r/m ,mod-bits ,r/m-bits)))
    (case ,mod-bits
      (#b00 (if ,is-word (setf (word-in-ram address-base *ram*) ,value) (setf (byte-in-ram address-base *ram*) ,value)))
      (#b01 (if ,is-word (setf (word-in-ram (+ address-base (peek-at-instruction)) *ram*) ,value) (setf (byte-in-ram (+ address-base (peek-at-instruction)) *ram*) ,value)))
      (#b10 (if ,is-word (setf (word-in-ram (+ address-base (peek-at-word)) *ram*) ,value) (setf (byte-in-ram (+ address-base (peek-at-word)) *ram*) ,value)))
      (#b11 (if ,is-word (setf (register (bits->word-reg ,r/m-bits)) ,value) (setf (byte-register (bits->byte-reg ,r/m-bits)) ,value))))))

;;; Instruction loader

(defun load-instructions-into-ram (instrs)
  (setf *ip* 0)
  (setf (subseq *ram* 0 #x7fff) instrs)
  (length instrs))

(defun next-instruction ()
  (incf *ip*)
  (elt *ram* (1- *ip*)))

(defun next-word ()
  (reverse-little-endian (next-instruction) (next-instruction)))

(defun peek-at-instruction (&optional (forward 0))
  (incf *advance*)
  (elt *ram* (+ *ip* forward)))

(defun peek-at-word ()
  (reverse-little-endian (peek-at-instruction) (peek-at-instruction 1)))

(defun advance-ip ()
  (incf *ip* *advance*)
  (setf *advance* 0))

(defun advance-ip-ahead-of-indirect-address (mod-bits r/m-bits)
  (cond
    ((or (and (= mod-bits #b00) (= r/m-bits #b110)) (= mod-bits #b10)) 2)
    ((= mod-bits #b01) 1)
    (t 0)))

(defun next-instruction-ahead-of-indirect-address (mod-bits r/m-bits)
  (let ((*ip* *ip*))
    (incf *ip* (advance-ip-ahead-of-indirect-address mod-bits r/m-bits))
    (incf *advance*)
    (next-instruction)))

(defun next-word-ahead-of-indirect-address (mod-bits r/m-bits)
  (let ((*ip* *ip*))
    (incf *ip* (advance-ip-ahead-of-indirect-address mod-bits r/m-bits))
    (incf *advance* 2)
    (next-word)))

;;; Memory access

(defun read-word-from-ram (loc &optional (segment *ram*))
  (word-in-ram loc segment))

(defun write-word-to-ram (loc word &optional (segment *ram*))
  (setf (word-in-ram loc segment) word))

(defun push-to-stack (value)
  (decf (register :sp) 2)
  (write-word-to-ram (register :sp) value *stack*))

(defun pop-from-stack ()
  (incf (register :sp) 2)
  (read-word-from-ram (- (register :sp) 2) *stack*))

;;; Flag effects

(defun set-cf-on-add (value)
  (setf (flag-p :cf) *has-carried*)
  value)

(defun set-cf-on-sub (value1 value2)
  (setf (flag-p :cf) (> value2 value1))
  (- value1 value2))

(defun set-sf-on-op (value is-word)
  (setf (flag-p :sf) (negative-p value is-word))
  value)

(defun set-zf-on-op (value)
  (setf (flag-p :zf) (= value 0))
  value)

;;; Operations

;; Context wrappers

(defun with-one-byte-opcode-register (opcode fn)
  (let ((reg (bits->word-reg (mod opcode #x08))))
    (funcall fn reg)))

(defmacro with-mod-r/m-byte (&body body)
  `(let* ((mod-r/m (next-instruction)) (r/m-bits (logand mod-r/m #b00000111)) (mod-bits (ash (logand mod-r/m #b11000000) -6)) (reg-bits (ash (logand mod-r/m #b00111000) -3)))
     ,@body))

(defmacro with-in-place-mod (dest mod-bits r/m-bits &body body)
  `(progn
     ,@body
     (when (equal (car ',dest) 'indirect-address)
       (decf *advance* (advance-ip-ahead-of-indirect-address ,mod-bits ,r/m-bits)))))

;; Templates

(defmacro mov (src dest)
  `(disasm-instr (list "mov" :src ,src :dest ,dest)
     (setf ,dest ,src)))

(defmacro xchg (op1 op2)
  `(disasm-instr (list "xchg" :op1 ,op1 :op2 ,op2)
     (rotatef ,op1 ,op2)))

(defmacro inc (op1 is-word)
  `(disasm-instr (list "inc" :op1 ,op1)
     (set-sf-on-op (set-zf-on-op (incf ,op1)) ,is-word)))

(defmacro dec (op1 is-word)
  `(disasm-instr (list "dec" :op1 ,op1)
     (set-sf-on-op (set-zf-on-op (decf ,op1)) ,is-word)))

;; Group handling

(defmacro parse-group-byte-pair (opcode operation mod-bits r/m-bits)
  `(,operation ,mod-bits ,r/m-bits (oddp ,opcode)))

(defmacro parse-group-opcode (&body body)
  `(with-mod-r/m-byte
     (case reg-bits
       ,@body)))

;; One-byte opcodes on registers

(defun clear-carry-flag ()
  (disasm-instr '("clc")
    (setf (flag-p :cf) nil)))

(defun set-carry-flag ()
  (disasm-instr '("stc")
    (setf (flag-p :cf) t)))

(defun push-register (reg)
  (disasm-instr (list "push" :src reg)
    (push-to-stack (register reg))))

(defun pop-to-register (reg)
  (disasm-instr (list "pop" :dest reg)
    (setf (register reg) (pop-from-stack))))

(defun inc-register (reg)
  (inc (register reg) t))

(defun dec-register (reg)
  (dec (register reg) t))

(defun xchg-register (reg)
  (disasm-instr (if (eql reg :ax) '("nop") (list "xchg" :op1 :ax :op2 reg))
    (xchg (register :ax) (register reg))))

(defun mov-byte-to-register (opcode)
  (let ((reg (bits->byte-reg (mod opcode #x08))))
    (mov (next-instruction) (byte-register reg))))

(defun mov-word-to-register (reg)
  (mov (next-word) (register reg)))

;; Flow control

(defun jmp-short ()
  (disasm-instr (list "jmp" :op1 (twos-complement (next-instruction) nil))
    (incf *ip* (twos-complement (next-instruction) nil))))

(defmacro jmp-short-conditionally (opcode condition mnemonic)
  `(let ((disp (next-instruction)))
     (if (evenp ,opcode)
       (disasm-instr (list (concatenate 'string "j" ,mnemonic) :op1 (twos-complement disp nil))
     (when ,condition
       (incf *ip* (twos-complement disp nil))))
       (disasm-instr (list (concatenate 'string "jn" ,mnemonic) :op1 (twos-complement disp nil))
     (unless ,condition
       (incf *ip* (twos-complement disp nil)))))))

(defun call-near ()
  (disasm-instr (list "call" :op1 (twos-complement (next-word) t))
    (push-to-stack (+ *ip* 2))
    (incf *ip* (twos-complement (next-word) t))))

(defun ret-from-call ()
  (disasm-instr '("ret")
    (setf *ip* (pop-from-stack))))

;; ALU

(defmacro parse-alu-opcode (opcode operation)
  `(let ((mod-8 (mod ,opcode 8)))
     (case mod-8
       (0
    (with-mod-r/m-byte
      (,operation (byte-register (bits->byte-reg reg-bits)) (indirect-address mod-bits r/m-bits nil) nil mod-bits r/m-bits)))
       (1
    (with-mod-r/m-byte
      (,operation (register (bits->word-reg reg-bits)) (indirect-address mod-bits r/m-bits t) t mod-bits r/m-bits)))
       (2
    (with-mod-r/m-byte
      (,operation (indirect-address mod-bits r/m-bits nil) (byte-register (bits->byte-reg reg-bits)) nil)))
       (3
    (with-mod-r/m-byte
      (,operation (indirect-address mod-bits r/m-bits t) (register (bits->word-reg reg-bits)) t)))
       (4
    (,operation (next-instruction) (byte-register :al) nil))
       (5
    (,operation (next-word) (register :ax) t)))))

(defmacro add-without-carry (src dest is-word &optional mod-bits r/m-bits)
  `(disasm-instr (list "add" :src ,src :dest ,dest)
     (with-in-place-mod ,dest ,mod-bits ,r/m-bits
       (set-zf-on-op (set-sf-on-op (set-cf-on-add (incf ,dest ,src)) ,is-word)))))

(defmacro add-with-carry (src dest is-word &optional mod-bits r/m-bits)
  `(disasm-instr (list "adc" :src ,src :dest ,dest)
     (with-in-place-mod ,dest ,mod-bits ,r/m-bits
       (set-zf-on-op (set-sf-on-op (set-cf-on-add (incf ,dest (+ ,src (flag :cf)))) ,is-word)))))

(defmacro sub-without-borrow (src dest is-word &optional mod-bits r/m-bits)
  `(disasm-instr (list "sub" :src ,src :dest ,dest)
     (with-in-place-mod ,dest ,mod-bits ,r/m-bits
       (let ((src-value ,src))
     (set-zf-on-op (set-sf-on-op (set-cf-on-sub (+ (decf ,dest src-value) src-value) src-value) ,is-word))))))

(defmacro sub-with-borrow (src dest is-word &optional mod-bits r/m-bits)
  `(disasm-instr (list "sbb" :src ,src :dest ,dest)
     (with-in-place-mod ,dest ,mod-bits ,r/m-bits
       (let ((src-plus-cf (+ ,src (flag :cf))))
     (set-zf-on-op (set-sf-on-op (set-cf-on-sub (+ (decf ,dest src-plus-cf) src-plus-cf) src-plus-cf) ,is-word))))))

(defmacro cmp-operation (src dest is-word &optional mod-bits r/m-bits)
  `(disasm-instr (list "cmp" :src ,src :dest ,dest)
     (set-zf-on-op (set-sf-on-op (set-cf-on-sub ,dest ,src) ,is-word))))

(defmacro and-operation (src dest is-word &optional mod-bits r/m-bits)
  `(disasm-instr (list "and" :src ,src :dest ,dest)
     (with-in-place-mod ,dest ,mod-bits ,r/m-bits
       (set-zf-on-op (set-sf-on-op (setf ,dest (logand ,src ,dest)) ,is-word))
       (setf (flag-p :cf) nil))))

(defmacro or-operation (src dest is-word &optional mod-bits r/m-bits)
  `(disasm-instr (list "or" :src ,src :dest ,dest)
     (with-in-place-mod ,dest ,mod-bits ,r/m-bits
       (set-zf-on-op (set-sf-on-op (setf ,dest (logior ,src ,dest)) ,is-word))
       (setf (flag-p :cf) nil))))

(defmacro xor-operation (src dest is-word &optional mod-bits r/m-bits)
  `(disasm-instr (list "xor" :src ,src :dest ,dest)
     (with-in-place-mod ,dest ,mod-bits ,r/m-bits
       (set-zf-on-op (set-sf-on-op (setf ,dest (logxor ,src ,dest)) ,is-word))
       (setf (flag-p :cf) nil))))

(defmacro parse-group1-byte (opcode operation mod-bits r/m-bits)
  `(case (mod ,opcode 4)
    (0 (,operation (next-instruction-ahead-of-indirect-address ,mod-bits ,r/m-bits) (indirect-address ,mod-bits ,r/m-bits nil) nil mod-bits r/m-bits))
    (1 (,operation (next-word-ahead-of-indirect-address ,mod-bits ,r/m-bits) (indirect-address ,mod-bits ,r/m-bits t) t mod-bits r/m-bits))
    (3 (,operation (twos-complement (next-instruction-ahead-of-indirect-address ,mod-bits ,r/m-bits) nil) (indirect-address ,mod-bits ,r/m-bits t) t mod-bits r/m-bits))))

(defmacro parse-group1-opcode (opcode)
  `(parse-group-opcode
     (0 (parse-group1-byte ,opcode add-without-carry mod-bits r/m-bits))
     (1 (parse-group1-byte ,opcode or-operation mod-bits r/m-bits))
     (2 (parse-group1-byte ,opcode add-with-carry mod-bits r/m-bits))
     (3 (parse-group1-byte ,opcode sub-with-borrow mod-bits r/m-bits))
     (4 (parse-group1-byte ,opcode and-operation mod-bits r/m-bits))
     (5 (parse-group1-byte ,opcode sub-without-borrow mod-bits r/m-bits))
     (6 (parse-group1-byte ,opcode xor-operation mod-bits r/m-bits))
     (7 (parse-group1-byte ,opcode cmp-operation mod-bits r/m-bits))))

;; Memory and register mov/xchg

(defun xchg-memory-register (opcode)
  (let ((is-word (oddp opcode)))
    (with-mod-r/m-byte
      (if is-word
      (xchg (register (bits->word-reg reg-bits)) (indirect-address mod-bits r/m-bits is-word))
      (xchg (byte-register (bits->byte-reg reg-bits)) (indirect-address mod-bits r/m-bits is-word))))))

(defmacro mov-immediate-to-memory (mod-bits r/m-bits is-word)
  `(if ,is-word
       (mov (next-word-ahead-of-indirect-address ,mod-bits ,r/m-bits) (indirect-address ,mod-bits ,r/m-bits t))
       (mov (next-instruction-ahead-of-indirect-address ,mod-bits ,r/m-bits) (indirect-address ,mod-bits ,r/m-bits nil))))

(defmacro parse-group11-opcode (opcode)
  `(parse-group-opcode
     (0 (parse-group-byte-pair ,opcode mov-immediate-to-memory mod-bits r/m-bits))))

(defmacro parse-mov-opcode (opcode)
  `(let ((mod-4 (mod ,opcode 4)))
     (with-mod-r/m-byte
       (case mod-4
     (0
      (mov (byte-register (bits->byte-reg reg-bits)) (indirect-address mod-bits r/m-bits nil)))
     (1
      (mov (register (bits->word-reg reg-bits)) (indirect-address mod-bits r/m-bits t)))
     (2
      (mov (indirect-address mod-bits r/m-bits nil) (byte-register (bits->byte-reg reg-bits))))
     (3
      (mov (indirect-address mod-bits r/m-bits t) (register (bits->word-reg reg-bits))))))))

;; Group 4/5 (inc/dec on EAs)

(defmacro inc-indirect (mod-bits r/m-bits is-word)
  `(inc (indirect-address ,mod-bits ,r/m-bits ,is-word) ,is-word))

(defmacro dec-indirect (mod-bits r/m-bits is-word)
  `(dec (indirect-address ,mod-bits ,r/m-bits ,is-word) ,is-word))

(defmacro parse-group4/5-opcode (opcode)
  `(parse-group-opcode
     (0 (parse-group-byte-pair ,opcode inc-indirect mod-bits r/m-bits))
     (1 (parse-group-byte-pair ,opcode dec-indirect mod-bits r/m-bits))))

;;; Opcode parsing

(defun in-paired-byte-block-p (opcode block)
  (= (truncate (/ opcode 2)) (/ block 2)))

(defun in-4-byte-block-p (opcode block)
  (= (truncate (/ opcode 4)) (/ block 4)))

(defun in-8-byte-block-p (opcode block)
  (= (truncate (/ opcode 8)) (/ block 8)))

(defun in-6-byte-block-p (opcode block)
  (and (= (truncate (/ opcode 8)) (/ block 8)) (< (mod opcode 8) 6)))

(defun parse-opcode (opcode)
  "Parse an opcode."
  (cond
    ((not opcode) (return-from parse-opcode nil))
    ((= opcode #xf4) (return-from parse-opcode '("hlt")))
    ((in-8-byte-block-p opcode #x40) (with-one-byte-opcode-register opcode #'inc-register))
    ((in-8-byte-block-p opcode #x48) (with-one-byte-opcode-register opcode #'dec-register))
    ((in-8-byte-block-p opcode #x50) (with-one-byte-opcode-register opcode #'push-register))
    ((in-8-byte-block-p opcode #x58) (with-one-byte-opcode-register opcode #'pop-to-register))
    ((in-8-byte-block-p opcode #x90) (with-one-byte-opcode-register opcode #'xchg-register))
    ((in-8-byte-block-p opcode #xb0) (mov-byte-to-register opcode))
    ((in-8-byte-block-p opcode #xb8) (with-one-byte-opcode-register opcode #'mov-word-to-register))
    ((= opcode #xf8) (clear-carry-flag))
    ((= opcode #xf9) (set-carry-flag))
    ((= opcode #xeb) (jmp-short))
    ((in-paired-byte-block-p opcode #x72) (jmp-short-conditionally opcode (flag-p :cf) "b"))
    ((in-paired-byte-block-p opcode #x74) (jmp-short-conditionally opcode (flag-p :zf) "z"))
    ((in-paired-byte-block-p opcode #x76) (jmp-short-conditionally opcode (or (flag-p :cf) (flag-p :zf)) "be"))
    ((in-paired-byte-block-p opcode #x78) (jmp-short-conditionally opcode (flag-p :sf) "s"))
    ((= opcode #xe8) (call-near))
    ((= opcode #xc3) (ret-from-call))
    ((in-6-byte-block-p opcode #x00) (parse-alu-opcode opcode add-without-carry))
    ((in-6-byte-block-p opcode #x08) (parse-alu-opcode opcode or-operation))
    ((in-6-byte-block-p opcode #x10) (parse-alu-opcode opcode add-with-carry))
    ((in-6-byte-block-p opcode #x18) (parse-alu-opcode opcode sub-with-borrow))
    ((in-6-byte-block-p opcode #x20) (parse-alu-opcode opcode and-operation))
    ((in-6-byte-block-p opcode #x28) (parse-alu-opcode opcode sub-without-borrow))
    ((in-6-byte-block-p opcode #x30) (parse-alu-opcode opcode xor-operation))
    ((in-6-byte-block-p opcode #x38) (parse-alu-opcode opcode cmp-operation))
    ((in-4-byte-block-p opcode #x80) (parse-group1-opcode opcode))
    ((in-4-byte-block-p opcode #x88) (parse-mov-opcode opcode))
    ((in-paired-byte-block-p opcode #x86) (xchg-memory-register opcode))
    ((in-paired-byte-block-p opcode #xc6) (parse-group11-opcode opcode))
    ((in-paired-byte-block-p opcode #xfe) (parse-group4/5-opcode opcode))))

;;; Main functions

(defun execute-instructions ()
  "Loop through loaded instructions."
  (loop
     for ret = (parse-opcode (next-instruction))
     until (equal ret '("hlt"))
     do (advance-ip)
     finally (return t)))

(defun disasm-instructions (instr-length)
  "Disassemble code."
  (loop
     for ret = (parse-opcode (next-instruction))
     collecting ret into disasm
     until (= *ip* instr-length)
     do (advance-ip)
     finally (return disasm)))

(defun loop-instructions (instr-length)
  (if *disasm*
      (disasm-instructions instr-length)
      (execute-instructions)))

(defun load-instructions-from-file (file)
  (with-open-file (in file :element-type '(unsigned-byte 8))
    (let ((instrs (make-array (file-length in) :element-type '(unsigned-byte 8) :initial-element 0 :adjustable t)))
      (read-sequence instrs in)
      instrs)))

(defun load-instructions (&key (file nil))
  (if file
      (load-instructions-from-file file)
      #()))

(defun print-video-ram (&key (width 80) (height 25) (stream t) (newline nil))
  (dotimes (line height)
    (dotimes (column width)
      (let ((char-at-cell (byte-in-ram (+ #x8000 (* line 80) column) *ram*)))
    (if (zerop char-at-cell)
        (format stream "~a" #\Space)
        (format stream "~a" (code-char char-at-cell)))))
    (if newline (format stream "~%"))))

(defun disasm (&key (file nil))
  (setf *disasm* t)
  (loop-instructions (load-instructions-into-ram (load-instructions :file file))))

(defun main (&key (file nil) (display nil) (stream t) (newline nil))
  (setf *disasm* nil)
  (loop-instructions (load-instructions-into-ram (load-instructions :file file)))
  (when display
    (print-video-ram :stream stream :newline newline)))

Đây là đầu ra trong Emacs:

Cửa sổ Emacs với hai panes, một phần của nguồn Lisp ở bên trái và đầu ra REPL với nội dung được yêu cầu ở bên phải.

Tôi muốn làm nổi bật ba tính năng chính. Mã này làm cho việc sử dụng nặng của macro khi thực hiện hướng dẫn, chẳng hạn như mov, xchg, và các hoạt động artithmetic. Mỗi hướng dẫn bao gồm một disasm-instrcuộc gọi macro. Điều này thực hiện việc phân tách cùng với mã thực tế bằng cách sử dụng if trên một biến toàn cục được đặt trong thời gian chạy. Tôi đặc biệt tự hào về phương pháp tiếp cận bất khả tri được sử dụng để ghi giá trị vào các thanh ghi và địa chỉ gián tiếp. Các macro thực hiện các hướng dẫn không quan tâm đến đích, vì các biểu mẫu được ghép trong một trong hai khả năng sẽ hoạt động với setfmacro Common Lisp chung.

Mã có thể được tìm thấy tại repo GitHub của tôi . Hãy tìm nhánh "codegolf", vì tôi đã bắt đầu triển khai các tính năng khác của 8086 trong tổng thể. Tôi đã thực hiện các cờ tràn và chẵn lẻ, cùng với thanh ghi FLAGS.

Có ba thao tác này không phải trong 8086, 0x820x83các phiên bản của toán tử logic. Điều này đã bị bắt rất muộn, và sẽ khá lộn xộn khi loại bỏ các hoạt động đó.

Tôi muốn cảm ơn @ja vì phiên bản Python của anh ấy, thứ đã truyền cảm hứng cho tôi từ rất sớm trong liên doanh này.


3
Câu trả lời đầu tiên đáng kinh ngạc! Chào mừng đến với trang web :)
DJMcMayhem

1
Lựa chọn ngôn ngữ rất tuyệt!
sao chép

12

C - 319 348 dòng

Đây là bản dịch trực tiếp ít nhiều của chương trình Postcript của tôi sang C. Tất nhiên việc sử dụng ngăn xếp được thay thế bằng các biến rõ ràng. Các trường của một lệnh được chia thành các biến o- byte opcode lệnh, dtrường hướng, w- chiều rộng. Nếu đó là lệnh "mod-reg-r / m", byte mr-rm được đọc vào struct rm r. Việc giải mã các trường reg và r / m tiến hành theo hai bước: tính toán con trỏ tới dữ liệu và tải dữ liệu, sử dụng lại cùng một biến. Vì vậy, đối với một cái gì đó như ADD AX,BX, đầu tiên x là một con trỏ đến ax và y là một con trỏ đến bx, sau đó x là nội dung (ax) và y là nội dung (bx). Có rất nhiều đúc cần thiết để sử dụng lại biến cho các loại khác nhau như thế này.

Byte opcode được giải mã với một bảng các con trỏ hàm. Mỗi thân hàm được cấu tạo bằng cách sử dụng macro cho các phần có thể sử dụng lại. Các DWvĩ mô hiện diện trong tất cả các chức năng opcode và giải mã dwbiến từ obyte opcode. Các RMPvĩ mô thực hiện giai đoạn đầu của giải mã "mr-rm" byte, và LDXYthực hiện giai đoạn thứ hai. Các mã lưu trữ kết quả sử dụng pbiến để giữ con trỏ đến vị trí kết quả và zbiến để giữ giá trị kết quả. Cờ được tính sau khi zgiá trị đã được tính toán. Các hoạt động INCDEClưu cờ mang trước khi sử dụng MATHFLAGSchức năng chung (như là một phần của ADDhoặcSUB Subacro) và khôi phục lại lời bạt, để bảo vệ Carry.

Chỉnh sửa: sửa lỗi!
Chỉnh sửa: mở rộng và bình luận. Khi trace==0nó xuất ra lệnh ANSI move-to-0,0 khi đổ video. Vì vậy, nó tốt hơn mô phỏng một màn hình thực tế. Các BIGENDIANđiều (mà thậm chí không làm việc) đã được gỡ bỏ. Nó phụ thuộc vào một số nơi theo thứ tự byte cuối nhỏ, nhưng tôi dự định sửa lỗi này trong lần sửa đổi tiếp theo. Về cơ bản, tất cả các truy cập con trỏ cần phải đi qua get_và các put_hàm rõ ràng (de) soạn các byte theo thứ tự LE.

#include<ctype.h>
#include<stdint.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/stat.h>
#include<unistd.h>
#define P printf
#define R return
#define T typedef
T intptr_t I; T uintptr_t U;
T short S; T unsigned short US;
T signed char C; T unsigned char UC; T void V;  // to make everything shorter
U o,w,d,f; // opcode, width, direction, extra temp variable (was initially for a flag, hence 'f')
U x,y,z;   // left operand, right operand, result
void *p;   // location to receive result
UC halt,debug=0,trace=0,reg[28],null[2],mem[0xffff]={ // operating flags, register memory, RAM
    1, (3<<6),        // ADD ax,ax
    1, (3<<6)+(4<<3), // ADD ax,sp
    3, (3<<6)+(4<<3), // ADD sp,ax
    0xf4 //HLT
};

// register declaration and initialization
#define H(_)_(al)_(ah)_(cl)_(ch)_(dl)_(dh)_(bl)_(bh)
#define X(_)_(ax)     _(cx)     _(dx)     _(bx)     _(sp)_(bp)_(si)_(di)_(ip)_(fl)
#define SS(_)_(cs)_(ds)_(ss)_(es)
#define HD(_)UC*_;      // half-word regs declared as unsigned char *
#define XD(_)US*_;      // full-word regs declared as unsigned short *
#define HR(_)_=(UC*)(reg+i++);      // init and increment by one
#define XR(_)_=(US*)(reg+i);i+=2;   // init and increment by two
H(HD)X(XD)SS(XD)V init(){I i=0;H(HR)i=0;X(XR)SS(XR)}    // declare and initialize register pointers
enum { CF=1<<0, PF=1<<2, AF=1<<4, ZF=1<<6, SF=1<<7, OF=1<<11 };

#define HP(_)P(#_ ":%02x ",*_);     // dump a half-word reg as zero-padded hex
#define XP(_)P(#_ ":%04x ",*_);     // dump a full-word reg as zero-padded hex
V dump(){ //H(HP)P("\n");
    P("\n"); X(XP)
    if(trace)P("%s %s %s %s ",*fl&CF?"CA":"NC",*fl&OF?"OV":"NO",*fl&SF?"SN":"NS",*fl&ZF?"ZR":"NZ");
    P("\n");  // ^^^ crack flag bits into strings ^^^
}

// get and put into memory in a strictly little-endian format
I get_(void*p,U w){R w? *(UC*)p + (((UC*)p)[1]<<8) :*(UC*)p;}
V put_(void*p,U x,U w){ if(w){ *(UC*)p=x; ((UC*)p)[1]=x>>8; }else *(UC*)p=x; }
// get byte or word through ip, incrementing ip
UC fetchb(){ U x = get_(mem+(*ip)++,0); if(trace)P("%02x(%03o) ",x,x); R x; }
US fetchw(){I w=fetchb();R w|(fetchb()<<8);}

T struct rm{U mod,reg,r_m;}rm;      // the three fields of the mod-reg-r/m byte
rm mrm(U m){ R(rm){ (m>>6)&3, (m>>3)&7, m&7 }; }    // crack the mrm byte into fields
U decreg(U reg,U w){    // decode the reg field, yielding a uintptr_t to the register (byte or word)
    if (w)R (U)((US*[]){ax,cx,dx,bx,sp,bp,si,di}[reg]);
    else R (U)((UC*[]){al,cl,dl,bl,ah,ch,dh,bh}[reg]); }
U rs(US*x,US*y){ R get_(x,1)+get_(y,1); }  // fetch and sum two full-words
U decrm(rm r,U w){      // decode the r/m byte, yielding uintptr_t
    U x=(U[]){rs(bx,si),rs(bx,di),rs(bp,si),rs(bp,di),get_(si,1),get_(di,1),get_(bp,1),get_(bx,1)}[r.r_m];
    switch(r.mod){ case 0: if (r.r_m==6) R (U)(mem+fetchw()); break;
                   case 1: x+=fetchb(); break;
                   case 2: x+=fetchw(); break;
                   case 3: R decreg(r.r_m,w); }
    R (U)(mem+x); }

// opcode helpers
    // set d and w from o
#define DW  if(trace){ P("%s:\n",__func__); } \
            d=!!(o&2); \
            w=o&1;
    // fetch mrm byte and decode, setting x and y as pointers to args and p ptr to dest
#define RMP rm r=mrm(fetchb());\
            x=decreg(r.reg,w); \
            y=decrm(r,w); \
            if(trace>1){ P("x:%d\n",x); P("y:%d\n",y); } \
            p=d?(void*)x:(void*)y;

    // fetch x and y values from x and y pointers
#define LDXY \
            x=get_((void*)x,w); \
            y=get_((void*)y,w); \
            if(trace){ P("x:%d\n",x); P("y:%d\n",y); }

    // normal mrm decode and load
#define RM  RMP LDXY

    // immediate to accumulator
#define IA x=(U)(p=w?(UC*)ax:al); \
           x=get_((void*)x,w); \
           y=w?fetchw():fetchb();

    // flags set by logical operators
#define LOGFLAGS  *fl=0; \
                  *fl |= ( (z&(w?0x8000:0x80))           ?SF:0) \
                       | ( (z&(w?0xffff:0xff))==0        ?ZF:0) ;

    // additional flags set by math operators
#define MATHFLAGS *fl |= ( (z&(w?0xffff0000:0xff00))     ?CF:0) \
                       | ( ((z^x)&(z^y)&(w?0x8000:0x80)) ?OF:0) \
                       | ( ((x^y^z)&0x10)                ?AF:0) ;

    // store result to p ptr
#define RESULT \
        if(trace)P(w?"->%04x ":"->%02x ",z); \
        put_(p,z,w);

// operators, composed with helpers in the opcode table below
    // most of these macros will "enter" with x and y already loaded with operands
#define PUSH(x) put_(mem+(*sp-=2),*(x),1)
#define POP(x) *(x)=get_(mem+(*sp+=2)-2,1)
#define ADD z=x+y; LOGFLAGS MATHFLAGS RESULT
#define ADC x+=(*fl&CF); ADD
#define SUB z=d?x-y:y-x; LOGFLAGS MATHFLAGS RESULT
#define SBB d?y+=*fl&CF:(x+=*fl&CF); SUB
#define CMP p=null; SUB
#define AND z=x&y; LOGFLAGS RESULT
#define  OR z=x|y; LOGFLAGS RESULT
#define XOR z=x^y; LOGFLAGS RESULT
#define INC(r) w=1; d=1; p=(V*)r; x=(S)*r; y=1; f=*fl&CF; ADD *fl=(*fl&~CF)|f;
#define DEC(r) w=1; d=1; p=(V*)r; x=(S)*r; y=1; f=*fl&CF; SUB *fl=(*fl&~CF)|f;
#define F(f) !!(*fl&f)
#define J(c) U cf=F(CF),of=F(OF),sf=F(SF),zf=F(ZF); y=(S)(C)fetchb(); \
                  if(trace)P("<%d> ", c); \
                  if(c)*ip+=(S)y;
#define JN(c) J(!(c))
#define IMM(a,b) rm r=mrm(fetchb()); \
            p=(void*)(y=decrm(r,w)); \
            a \
            x=w?fetchw():fetchb(); \
            b \
            d=0; \
            y=get_((void*)y,w); \
            if(trace){ P("x:%d\n",x); P("y:%d\n",y); } \
            if(trace){ P("%s ", (C*[]){"ADD","OR","ADC","SBB","AND","SUB","XOR","CMP"}[r.reg]); } \
            switch(r.reg){case 0:ADD break; \
                          case 1:OR break; \
                          case 2:ADC break; \
                          case 3:SBB break; \
                          case 4:AND break; \
                          case 5:SUB break; \
                          case 6:XOR break; \
                          case 7:CMP break; }
#define IMMIS IMM(w=0;,w=1;x=(S)(C)x;)
#define TEST z=x&y; LOGFLAGS MATHFLAGS
#define XCHG f=x;z=y; LDXY if(w){*(US*)f=y;*(US*)z=x;}else{*(UC*)f=y;*(UC*)z=x;}
#define MOV z=d?y:x; RESULT
#define MOVSEG
#define LEA RMP z=((UC*)y)-mem; RESULT
#define NOP
#define AXCH(r) x=(U)ax; y=(U)(r); w=1; XCHG
#define CBW *ax=(S)(C)*al;
#define CWD z=(I)(S)*ax; *dx=z>>16;
#define CALL x=w?fetchw():(S)(C)fetchb(); PUSH(ip); (*ip)+=(S)x;
#define WAIT
#define PUSHF PUSH(fl)
#define POPF POP(fl)
#define SAHF x=*fl; y=*ah; x=(x&~0xff)|y; *fl=x;
#define LAHF *ah=(UC)*fl;
#define mMOV if(d){ x=get_(mem+fetchw(),w); if(w)*ax=x; else*al=x; } \
             else { put_(mem+fetchw(),w?*ax:*al,w); }
#define MOVS
#define CMPS
#define STOS
#define LODS
#define SCAS
#define iMOVb(r) (*r)=fetchb();
#define iMOVw(r) (*r)=fetchw();
#define RET(v) POP(ip); if(v)*sp+=v*2;
#define LES
#define LDS
#define iMOVm if(w){iMOVw((US*)y)}else{iMOVb((UC*)y)}
#define fRET(v) POP(cs); RET(v)
#define INT(v)
#define INT0
#define IRET
#define Shift rm r=mrm(fetchb());
#define AAM
#define AAD
#define XLAT
#define ESC(v)
#define LOOPNZ
#define LOOPZ
#define LOOP
#define JCXZ
#define IN
#define OUT
#define INv
#define OUTv
#define JMP x=fetchw(); *ip+=(S)x;
#define sJMP x=(S)(C)fetchb(); *ip+=(S)x;
#define FARJMP
#define LOCK
#define REP
#define REPZ
#define HLT halt=1
#define CMC *fl=(*fl&~CF)|((*fl&CF)^1);
#define NOT
#define NEG
#define MUL
#define IMUL
#define DIV
#define IDIV
#define Grp1 rm r=mrm(fetchb()); \
             y=decrm(r,w); \
             if(trace)P("%s ", (C*[]){}[r.reg]); \
             switch(r.reg){case 0: TEST; break; \
                           case 2: NOT; break; \
                           case 3: NEG; break; \
                           case 4: MUL; break; \
                           case 5: IMUL; break; \
                           case 6: DIV; break; \
                           case 7: IDIV; break; }
#define Grp2 rm r=mrm(fetchb()); \
             y=decrm(r,w); \
             if(trace)P("%s ", (C*[]){"INC","DEC","CALL","CALL","JMP","JMP","PUSH"}[r.reg]); \
             switch(r.reg){case 0: INC((S*)y); break; \
                           case 1: DEC((S*)y); break; \
                           case 2: CALL; break; \
                           case 3: CALL; break; \
                           case 4: *ip+=(S)y; break; \
                           case 5: JMP; break; \
                           case 6: PUSH((S*)y); break; }
#define CLC *fl=*fl&~CF;
#define STC *fl=*fl|CF;
#define CLI
#define STI
#define CLD
#define STD

// opcode table
// An x-macro table of pairs (a, b) where a becomes the name of a void function(void) which
// implements the opcode, and b comprises the body of the function (via further macro expansion)
#define OP(_)\
/*dw:bf                 wf                     bt                    wt   */ \
_(addbf, RM ADD)      _(addwf, RM ADD)       _(addbt,  RM ADD)     _(addwt, RM ADD)     /*00-03*/\
_(addbi, IA ADD)      _(addwi, IA ADD)       _(pushes, PUSH(es))   _(popes, POP(es))    /*04-07*/\
_(orbf,  RM OR)       _(orwf,  RM OR)        _(orbt,   RM OR)      _(orwt,  RM OR)      /*08-0b*/\
_(orbi,  IA OR)       _(orwi,  IA OR)        _(pushcs, PUSH(cs))   _(nop0,       )      /*0c-0f*/\
_(adcbf, RM ADC)      _(adcwf, RM ADC)       _(adcbt,  RM ADC)     _(adcwt, RM ADC)     /*10-13*/\
_(adcbi, IA ADC)      _(adcwi, IA ADC)       _(pushss, PUSH(ss))   _(popss, POP(ss))    /*14-17*/\
_(sbbbf, RM SBB)      _(sbbwf, RM SBB)       _(sbbbt,  RM SBB)     _(sbbwt, RM SBB)     /*18-1b*/\
_(sbbbi, IA SBB)      _(sbbwi, IA SBB)       _(pushds, PUSH(ds))   _(popds, POP(ds))    /*1c-1f*/\
_(andbf, RM AND)      _(andwf, RM AND)       _(andbt, RM AND)      _(andwt, RM AND)     /*20-23*/\
_(andbi, IA AND)      _(andwi, IA AND)       _(esseg, )            _(daa, )             /*24-27*/\
_(subbf, RM SUB)      _(subwf, RM SUB)       _(subbt, RM SUB)      _(subwt, RM SUB)     /*28-2b*/\
_(subbi, IA SUB)      _(subwi, IA SUB)       _(csseg, )            _(das, )             /*2c-2f*/\
_(xorbf, RM XOR)      _(xorwf, RM XOR)       _(xorbt, RM XOR)      _(xorwt, RM XOR)     /*30-33*/\
_(xorbi, IA XOR)      _(xorwi, IA XOR)       _(ssseg, )            _(aaa, )             /*34-37*/\
_(cmpbf, RM CMP)      _(cmpwf, RM CMP)       _(cmpbt, RM CMP)      _(cmpwt, RM CMP)     /*38-3b*/\
_(cmpbi, IA CMP)      _(cmpwi, IA CMP)       _(dsseg, )            _(aas, )             /*3c-3f*/\
_(incax, INC(ax))     _(inccx, INC(cx))      _(incdx, INC(dx))     _(incbx, INC(bx))    /*40-43*/\
_(incsp, INC(sp))     _(incbp, INC(bp))      _(incsi, INC(si))     _(incdi, INC(di))    /*44-47*/\
_(decax, DEC(ax))     _(deccx, DEC(cx))      _(decdx, DEC(dx))     _(decbx, DEC(bx))    /*48-4b*/\
_(decsp, DEC(sp))     _(decbp, DEC(bp))      _(decsi, DEC(si))     _(decdi, DEC(di))    /*4c-4f*/\
_(pushax, PUSH(ax))   _(pushcx, PUSH(cx))    _(pushdx, PUSH(dx))   _(pushbx, PUSH(bx))  /*50-53*/\
_(pushsp, PUSH(sp))   _(pushbp, PUSH(bp))    _(pushsi, PUSH(si))   _(pushdi, PUSH(di))  /*54-57*/\
_(popax, POP(ax))     _(popcx, POP(cx))      _(popdx, POP(dx))     _(popbx, POP(bx))    /*58-5b*/\
_(popsp, POP(sp))     _(popbp, POP(bp))      _(popsi, POP(si))     _(popdi, POP(di))    /*5c-5f*/\
_(nop1, ) _(nop2, )   _(nop3, ) _(nop4, )    _(nop5, ) _(nop6, )   _(nop7, ) _(nop8, )  /*60-67*/\
_(nop9, ) _(nopA, )   _(nopB, ) _(nopC, )    _(nopD, ) _(nopE, )   _(nopF, ) _(nopG, )  /*68-6f*/\
_(jo, J(of))          _(jno, JN(of))         _(jb, J(cf))          _(jnb, JN(cf))       /*70-73*/\
_(jz, J(zf))          _(jnz, JN(zf))         _(jbe, J(cf|zf))      _(jnbe, JN(cf|zf))   /*74-77*/\
_(js, J(sf))          _(jns, JN(sf))         _(jp, )               _(jnp, )             /*78-7b*/\
_(jl, J(sf^of))       _(jnl_, JN(sf^of))     _(jle, J((sf^of)|zf)) _(jnle,JN((sf^of)|zf))/*7c-7f*/\
_(immb, IMM(,))       _(immw, IMM(,))        _(immb1, IMM(,))      _(immis, IMMIS)      /*80-83*/\
_(testb, RM TEST)     _(testw, RM TEST)      _(xchgb, RMP XCHG)    _(xchgw, RMP XCHG)   /*84-87*/\
_(movbf, RM MOV)      _(movwf, RM MOV)       _(movbt, RM MOV)      _(movwt, RM MOV)     /*88-8b*/\
_(movsegf, RM MOVSEG) _(lea, LEA)            _(movsegt, RM MOVSEG) _(poprm,RM POP((US*)p))/*8c-8f*/\
_(nopH, )             _(xchgac, AXCH(cx))    _(xchgad, AXCH(dx))   _(xchgab, AXCH(bx))  /*90-93*/\
_(xchgasp, AXCH(sp))  _(xchabp, AXCH(bp))    _(xchgasi, AXCH(si))  _(xchadi, AXCH(di))  /*94-97*/\
_(cbw, CBW)           _(cwd, CWD)            _(farcall, )          _(wait, WAIT)        /*98-9b*/\
_(pushf, PUSHF)       _(popf, POPF)          _(sahf, SAHF)         _(lahf, LAHF)        /*9c-9f*/\
_(movalb, mMOV)       _(movaxw, mMOV)        _(movbal, mMOV)       _(movwax, mMOV)      /*a0-a3*/\
_(movsb, MOVS)        _(movsw, MOVS)         _(cmpsb, CMPS)        _(cmpsw, CMPS)       /*a4-a7*/\
_(testaib, IA TEST)   _(testaiw, IA TEST)    _(stosb, STOS)        _(stosw, STOS)       /*a8-ab*/\
_(lodsb, LODS)        _(lodsw, LODS)         _(scasb, SCAS)        _(scasw, SCAS)       /*ac-af*/\
_(movali, iMOVb(al))  _(movcli, iMOVb(cl))   _(movdli, iMOVb(dl))  _(movbli, iMOVb(bl)) /*b0-b3*/\
_(movahi, iMOVb(ah))  _(movchi, iMOVb(ch))   _(movdhi, iMOVb(dh))  _(movbhi, iMOVb(bh)) /*b4-b7*/\
_(movaxi, iMOVw(ax))  _(movcxi, iMOVw(cx))   _(movdxi, iMOVw(dx))  _(movbxi, iMOVw(bx)) /*b8-bb*/\
_(movspi, iMOVw(sp))  _(movbpi, iMOVw(bp))   _(movsii, iMOVw(si))  _(movdii, iMOVw(di)) /*bc-bf*/\
_(nopI, )             _(nopJ, )              _(reti, RET(fetchw())) _(retz, RET(0))     /*c0-c3*/\
_(les, LES)           _(lds, LDS)            _(movimb, RMP iMOVm)  _(movimw, RMP iMOVm) /*c4-c7*/\
_(nopK, )             _(nopL, )              _(freti, fRET(fetchw())) _(fretz, fRET(0)) /*c8-cb*/\
_(int3, INT(3))       _(inti, INT(fetchb())) _(int0, INT(0))       _(iret, IRET)        /*cc-cf*/\
_(shiftb, Shift)      _(shiftw, Shift)       _(shiftbv, Shift)     _(shiftwv, Shift)    /*d0-d3*/\
_(aam, AAM)           _(aad, AAD)            _(nopM, )             _(xlat, XLAT)        /*d4-d7*/\
_(esc0, ESC(0))       _(esc1, ESC(1))        _(esc2, ESC(2))       _(esc3, ESC(3))      /*d8-db*/\
_(esc4, ESC(4))       _(esc5, ESC(5))        _(esc6, ESC(6))       _(esc7, ESC(7))      /*dc-df*/\
_(loopnz, LOOPNZ)     _(loopz, LOOPZ)        _(loop, LOOP)         _(jcxz, JCXZ)        /*e0-e3*/\
_(inb, IN)            _(inw, IN)             _(outb, OUT)          _(outw, OUT)         /*e4-e7*/\
_(call, w=1; CALL)    _(jmp, JMP)            _(farjmp, FARJMP)     _(sjmp, sJMP)        /*e8-eb*/\
_(invb, INv)          _(invw, INv)           _(outvb, OUTv)        _(outvw, OUTv)       /*ec-ef*/\
_(lock, LOCK)         _(nopN, )              _(rep, REP)           _(repz, REPZ)        /*f0-f3*/\
_(hlt, HLT)           _(cmc, CMC)            _(grp1b, Grp1)        _(grp1w, Grp1)       /*f4-f7*/\
_(clc, CLC)           _(stc, STC)            _(cli, CLI)           _(sti, STI)          /*f8-fb*/\
_(cld, CLD)           _(std, STD)            _(grp2b, Grp2)        _(grp2w, Grp2)       /*fc-ff*/
#define OPF(a,b)void a(){DW b;}     // generate opcode function
#define OPN(a,b)a,                  // extract name
OP(OPF)void(*tab[])()={OP(OPN)};    // generate functions, declare and populate fp table with names

V clean(C*s){I i;       // replace unprintable characters in 80-byte buffer with spaces
    for(i=0;i<80;i++)
        if(!isprint(s[i]))
            s[i]=' ';
}
V video(){I i;          // dump the (cleaned) video memory to the console
    C buf[81]="";
    if(!trace)P("\e[0;0;f");
    for(i=0;i<28;i++)
        memcpy(buf, mem+0x8000+i*80, 80),
        clean(buf),
        P("\n%s",buf);
    P("\n");
}

static I ct;        // timer memory for period video dump
V run(){while(!halt){if(trace)dump();
    if(!ct--){ct=10; video();}
    tab[o=fetchb()]();}}
V dbg(){
    while(!halt){
        C c;
        if(!ct--){ct=10; video();}
        if(trace)dump();
        //scanf("%c", &c);
        fgetc(stdin);
        //switch(c){
        //case '\n':
        //case 's':
            tab[o=fetchb()]();
            //break;
        //}
    }
}

I load(C*f){struct stat s; FILE*fp;     // load a file into memory at address zero
    R (fp=fopen(f,"rb"))
        && fstat(fileno(fp),&s) || fread(mem,s.st_size,1,fp); }

I main(I c,C**v){
    init();
    if(c>1){            // if there's an argument
        load(v[1]);     //     load named file
    }
    *sp=0x100;          // initialize stack pointer
    if(debug) dbg();    // if debugging, debug
    else run();         // otherwise, just run
    video();            // dump final video
    R 0;}               // remember what R means? cf. line 9

Việc sử dụng các macro cho các giai đoạn của các hoạt động khác nhau tạo nên sự phù hợp về ngữ nghĩa rất gần với cách mã bưu điện hoạt động theo kiểu tuần tự thuần túy. Ví dụ, bốn opcodes đầu tiên, 0x00-0x03 đều là các lệnh ADD với hướng khác nhau (REG -> REG / MOD, REG <- REG / MOD) và kích thước byte / từ, do đó chúng được biểu diễn giống hệt nhau trong bảng chức năng .

_(addbf, RM ADD)      _(addwf, RM ADD)       _(addbt,  RM ADD)     _(addwt, RM ADD)

Bảng chức năng được khởi tạo với macro này:

OP(OPF)

áp dụng OPF()cho mỗi đại diện opcode. OPF()được định nghĩa là:

#define OPF(a,b)void a(){DW b;}     // generate opcode function

Vì vậy, bốn opcodes đầu tiên mở rộng (một lần) thành:

void addbf(){ DW RM ADD ; }
void addwf(){ DW RM ADD ; }
void addbt(){ DW RM ADD ; }
void addwt(){ DW RM ADD ; }

Các hàm này tự phân biệt bằng kết quả của DWmacro xác định các bit hướng và byte / từ trực tiếp từ byte opcode. Mở rộng phần thân của một trong các hàm này (một lần) sẽ tạo ra:

if(trace){ P("%s:\n",__func__); }  // DW: set d and w from o
d=!!(o&2);
w=o&1;
RMP LDXY  // RM: normal mrm decode and load
z=x+y; LOGFLAGS MATHFLAGS RESULT  // ADD
;

Trong đó vòng lặp chính đã đặt obiến:

while(!halt){tab[o=fetchb()]();}}

Mở rộng thêm một lần nữa sẽ cho tất cả "thịt" của opcode:

// DW: set d and w from o
if(trace){ P("%s:\n",__func__); }
d=!!(o&2);
w=o&1;

// RMP: fetch mrm byte and decode, setting x and y as pointers to args and p ptr to dest
rm r=mrm(fetchb());
x=decreg(r.reg,w);
y=decrm(r,w);
if(trace>1){ P("x:%d\n",x); P("y:%d\n",y); }
p=d?(void*)x:(void*)y;

// LDXY: fetch x and y values from x and y pointers
x=get_((void*)x,w);
y=get_((void*)y,w);
if(trace){ P("x:%d\n",x); P("y:%d\n",y); }

z=x+y;   // ADD
// LOGFLAGS: flags set by logical operators
*fl=0;
*fl |= ( (z&(w?0x8000:0x80))           ?SF:0)
     | ( (z&(w?0xffff:0xff))==0        ?ZF:0) ;

// MATHFLAGS: additional flags set by math operators
*fl |= ( (z&(w?0xffff0000:0xff00))     ?CF:0)
     | ( ((z^x)&(z^y)&(w?0x8000:0x80)) ?OF:0)
     | ( ((x^y^z)&0x10)                ?AF:0) ;

// RESULT: store result to p ptr
if(trace)P(w?"->%04x ":"->%02x ",z);
put_(p,z,w);
;

Và chức năng được xử lý trước, được chuyển qua indent:

void
addbf ()
{
  if (trace)
    {
      printf ("%s:\n", __func__);
    }
  d = ! !(o & 2);
  w = o & 1;
  rm r = mrm (fetchb ());
  x = decreg (r.reg, w);
  y = decrm (r, w);
  if (trace > 1)
    {
      printf ("x:%d\n", x);
      printf ("y:%d\n", y);
    }
  p = d ? (void *) x : (void *) y;
  x = get_ ((void *) x, w);
  y = get_ ((void *) y, w);
  if (trace)
    {
      printf ("x:%d\n", x);
      printf ("y:%d\n", y);
    }
  z = x + y;
  *fl = 0;
  *fl |=
    ((z & (w ? 0x8000 : 0x80)) ? SF : 0) | ((z & (w ? 0xffff : 0xff)) ==
                        0 ? ZF : 0);
  *fl |=
    ((z & (w ? 0xffff0000 : 0xff00)) ? CF : 0) |
    (((z ^ x) & (z ^ y) & (w ? 0x8000 : 0x80)) ? OF : 0) |
    (((x ^ y ^ z) & 0x10) ? AF : 0);
  if (trace)
    printf (w ? "->%04x " : "->%02x ", z);
  put_ (p, z, w);;
}

Không phải là phong cách C tuyệt vời nhất để sử dụng hàng ngày, nhưng sử dụng macro theo cách này có vẻ khá hoàn hảo để thực hiện ở đây rất ngắn và rất trực tiếp.

Kiểm tra đầu ra chương trình, với đuôi của đầu ra theo dõi:

43(103) incbx:
->0065 
ax:0020 cx:0015 dx:0190 bx:0065 sp:1000 bp:0000 si:0000 di:00c2 ip:013e fl:0000 NC NO NS NZ 
83(203) immis:
fb(373) 64(144) x:100
y:101
CMP ->0001 
ax:0020 cx:0015 dx:0190 bx:0065 sp:1000 bp:0000 si:0000 di:00c2 ip:0141 fl:0000 NC NO NS NZ 
76(166) jbe:
da(332) <0> 
ax:0020 cx:0015 dx:0190 bx:0065 sp:1000 bp:0000 si:0000 di:00c2 ip:0143 fl:0000 NC NO NS NZ 
f4(364) hlt:

.........                                                                       
Hello, world!                                                                   
0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ 


################################################################################
##                                                                            ##
##  0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987                          ##
##                                                                            ##
##  0 1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 289 324 361 400     ##
##                                                                            ##
##  2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97    ##
##                                                                            ##
##                                                                            ##
##                                                                            ##
##                                                                            ##
##                                                                            ##
##                                                                            ##
##                                                                            ##
##                                                                            ##
##                                                                            ##
##                                                                            ##
##                                                                            ##
##                                                                            ##
################################################################################

Tôi đã chia sẻ một số phiên bản trước đó trong comp.lang.c nhưng chúng không được quan tâm lắm.



Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.