인텔 8086 CPU 에뮬레이션


157

참고 : 몇 가지 답변이 도착했습니다. 새로운 답변을 찬성하는 것도 고려하십시오.


8086은 인텔 최초의 x86 마이크로 프로세서이다. 당신의 임무는 에뮬레이터를 작성하는 것입니다. 이것은 상대적으로 진보되어 있기 때문에 나는 그것을 조금만 제한하고 싶습니다.

  • 다음 opcode 만 구현하면됩니다.
    • mov, push, pop, xchg
    • 추가, adc, sub, sbb, cmp 및 x 또는 xor
    • inc, 12 월
    • 전화, ret, jmp
    • jb, jz, jbe, js, jnb, jnz, jnbe, jns
    • stc, clc
    • lt, nop
  • 결과적으로 캐리, 영점 및 부호 플래그 만 계산하면됩니다.
  • 세그먼트를 구현하지 마십시오. 가정하십시오 cs = ds = ss = 0.
  • 접두사 없음
  • 인터럽트 나 포트 IO가 없습니다
  • 문자열 함수가 없습니다
  • 2 바이트 opcode가 없습니다 (0F ..)
  • 부동 소수점 산술 없음
  • (분명히) 32 비트, sse, mmx, ... 1979 년에 아직 발명되지 않은 것은 없습니다.
  • 사이클을 세거나 타이밍을 세지 않아도됩니다

시작 ip = 0하고 sp = 100h.


입력 : 에뮬레이터는 입력 (파일, 미리 정의 된 배열 등에서 읽음)으로 원하는 모든 형식의 이진 프로그램을 가져 와서 주소 0의 메모리에로드해야합니다.

출력 : 비디오 RAM은 주소 8000h에서 시작하며 모든 바이트는 하나의 (ASCII-) 문자입니다. 콘솔에 80x25 화면을 에뮬레이트합니다. 0 바이트를 공백처럼 취급하십시오.

예:

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

참고 : 실제 비디오 모드와 매우 유사합니다.이 모드는 일반적으로 0xB8000이며 색상에 대해 문자 당 다른 바이트가 있습니다.

우승 기준 :

  • 언급 된 모든 지침을 구현해야합니다.
  • 제대로 실행되어야 하는 주석 처리되지 않은 테스트 프로그램 ( link , nasm source )을 만들었습니다 . 출력

    .........                                                                       
    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    ##
    ##                                                                            ##
    ##                                                                            ##
    ##                                                                            ##
    ##                                                                            ##
    ##                                                                            ##
    ##                                                                            ##
    ##                                                                            ##
    ##                                                                            ##
    ##                                                                            ##
    ##                                                                            ##
    ##                                                                            ##
    ##                                                                            ##
    ################################################################################
    
  • 이것이 코드 골프인지 확실하지 않습니다. 그것은 어려운 일이므로 제출하면 많은 찬사를 얻습니다. 의견을주세요.

이 작업에 도움이되는 몇 가지 링크는 다음과 같습니다.

이것이이 플랫폼의 첫 번째 항목입니다. 실수가 있으면 지적하십시오. 세부 사항을 놓쳤다면 간단히 물어보십시오.


5
나에게는 너무 발전했지만이 질문에 대한 답은 제가 가장 관심이있는 것들이기 때문에 매우 열심입니다! 내가 특히 매혹적이라고 느끼면 나중에 금이 갈지도 모른다 ...
Chris Browne

3
@ChrisBrowne 행운을 빕니다. 나는 현재 8086을 80386으로 바꾸고 있으며 지금 까지이 프로젝트에서 많은 것을 배웠습니다 .
복사

2
+1 + favorite ...이 질문을 보았을 때의 느낌을 표현할 수 없습니다.
ixtmixilix

2
@copy 모든 단일 언어 / 호스트 페어를위한 골프 경기를하기에는 결코 늦지 않습니다
Yauhen Yakimovich

2
@ MartinBüttner 물론, 그 질문은 그 태그보다 오래되었고 기본적으로 어쨌든 인기 콘테스트였습니다
복사

답변:


84

https://github.com/julienaubert/py8086 : 포크와 골프를 자유롭게 하십시오

결과 대화식 디버거도 포함했습니다.

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

emu8086.py (필수) console.py (디스플레이 출력의 경우 선택 사항), disasm.py (선택 사항, codegolf에서 asm 목록을 가져 오는 경우)의 세 가지 파일이 있습니다.

디스플레이와 함께 실행하려면 (참고는 curses를 사용합니다) :

python emu8086.py 

대화식 디버거로 실행하려면 다음을 수행하십시오.

python emu8086.py a b

비 대화식 "디버거"로 실행하려면 :

python emu8086.py a

" codegolf " 프로그램 은 동일한 디렉토리에 있어야합니다.

emu8086.py

console.py

disasm.py

github에서


9
그것은 첫 번째 코드 골프 포스트의 지옥 중 하나입니다. +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 ...
Dillon Cower

@DC 덕분에 :) 재미있는 도전이었습니다!
ja

1
아직도 누군가가 실제로 이것을했다고 믿을 수 없습니다 :-) 훌륭한 일!
복사

1
놀랄 만한! 축하합니다! 결국 몇 줄이 있었습니까?
Will Lp

59

하스켈, 256 (234) 196 선

나는이 진행중인 작업을 한동안 가지고 있었지만 출판하기 전에 조금 더 연마하려고했지만 이제는 재미가 공식적으로 시작되었으므로 더 이상 숨길 필요가 없습니다. 나는 그것을 추출하는 동안 정확히 256 줄 길이라는 것을 알았으므로 그것이 존재하는 "놀라운"지점에 있다고 가정합니다.

내용 : 예제 바이너리를 완벽하게 실행하기에 8086 명령어 세트 중 겨우 충분합니다. 자체 수정 코드가 지원됩니다. (프리 페치 : 0 바이트)가
역설적 코드의 첫 번째 반복은 충분한 이상이었고 오피 스팬 이하 지원. 리팩토링은 결국 코드 길이와 opcode 커버리지 모두에 유리합니다.

결과 : 분명히 세그먼트, 접두사 및 멀티 바이트 opcode, 인터럽트, I / O 포트, 문자열 작업 및 FP. 처음에는 원래 PUSH SP동작을 따랐 지만 몇 번 반복 한 후에 삭제해야했습니다.

캐리 플래그 결과는 아마도 ADC/의 경우에 매우 엉망 일 것입니다 SBB.

어쨌든, 여기 코드가 있습니다 :

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

제공된 샘플 바이너리의 출력은 사양과 완벽하게 일치합니다. 다음과 같은 호출을 사용하여 시도하십시오.

runhaskell 8086.hs <8086.bin

구현되지 않은 대부분의 작업은 단순히 패턴 일치 실패를 초래합니다.

나는 여전히 꽤 많은 요소를 고려하고 저주로 실제 라이브 출력을 구현하려고합니다.

업데이트 1 : 234 줄로 떨어졌습니다. 기능별로 코드를보다 잘 구성하고, 가능한 것을 재정렬하고, 80 개의 열을 고수하려고했습니다. 그리고 ALU를 여러 번 리팩토링했습니다.

업데이트 2 : 5 년이 지났으므로 최신 GHC에서 완벽하게 컴파일 할 수있는 업데이트가 필요하다고 생각했습니다. 길을 따라 :

  • liftM, liftM2 등을 제거했습니다. 나는 좋아하는데 <$><*>전주곡이다.
  • Data.Bool 및 Data.ByteString은 비트를 저장하고 정리합니다.
  • 예전에는 IP 레지스터가 특별 했으므로 이제는 레지스터 파일에 있습니다. 그것은 8086만큼 의미가 없지만, 저는 골퍼입니다.
  • 이제는 순수한 ST 기반 코드입니다. 골프의 관점에서 볼 때 이것은 많은 유형 서명이 필요하기 때문에 짜증납니다. 다른 한편으로, 나는 양심을 가지고 행을 잃어 버렸으므로 이제 깨끗하고 긴 코드를 얻게됩니다.
  • 이제 이것은 git-tracked입니다.
  • 더 심각한 의견을 추가했습니다. 결과적으로 줄 수를 세는 방식이 변경되었습니다. 빈 줄과 순수한 주석 줄을 삭제합니다. 모든 줄을 보증하지만 수입은 80 자 미만입니다. 내가 남긴 서명은 실제로 올바르게 컴파일하기 위해 필요하기 때문에 유형 서명을 삭제하지 않습니다 (ST 청결도를 높이 평가합니다).

코드 주석에서 알 수 있듯이 5 줄 (Data.Char 가져 오기, 8 비트 레지스터 매핑 및 화면 덤프)이 사양에 맞지 않으므로 너무 기울어지면 할인 할 것을 환영합니다 :-)


3
좋은데 특히 내 솔루션과 다른 솔루션에 비해 짧습니다. 하스켈을 먼저 배워야하지만 코드가 매우 좋아 보입니다.
복사

3
잘 했어! 매우 짧은. 하스켈을 배워야합니다.
ja

무엇입니까 .|.? / 10char
Soham Chowdhury

@octatoan x86 opcode에서 알려진 연산은 OR입니다.
JB

46

C-7143 라인 (CPU 자체 3162 라인)

편집 : Windows 빌드에는 이제 가상 디스크를 변경하는 드롭 다운 메뉴가 있습니다.

완전한 80186 / V20 PC 에뮬레이터 (CGA / MCGA / VGA, 사운드 블래스터, adlib, 마우스 등)를 작성했지만 8086을 에뮬레이션하는 것은 결코 쉬운 일이 아닙니다. 완전히 정확 해지는 데 몇 달이 걸렸습니다. 내 에뮬레이터에서만 CPU 모듈이 있습니다.

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

이 에뮬레이터에서 너무 많은 전역 변수를 사용하는 것을 인정합니다. 나는 아직도 C를 처음 접했을 때 이것을 쓰기 시작했습니다. 요즈음 중 하나를 정리해야합니다. 그 안에있는 다른 소스 파일들 대부분은보기 흉하지 않습니다.

http://sourceforge.net/p/fake86을 통해 모든 코드 (및 일부 스크린 샷, 아래 스크린 샷)를 볼 수 있습니다.

많은 사람들이 자신의 글을 쓰고 싶어하는 사람을 도와주게되어 매우 기쁠 것입니다. 많은 재미가 있기 때문에 CPU에 대해 많은 것을 배울 수 있습니다! 면책 조항 : V20의 8080 에뮬레이션은 PC 프로그램에서 거의 사용되지 않았기 때문에 추가하지 않았습니다. 이익이없는 많은 일처럼 보입니다.

스트리트 파이터 2!


3
잘 했어! 게임이 실제로 최고 속도로 실행됩니까?
복사

1
감사. 예, 그것은 8088보다 몇 배나 더 빠릅니다. 현대 시스템에서는 486 같은 속도를 낼 수 있습니다. 정말 좋은 프로세서에서는 저가형 펜티엄과 같습니다. 불행히도 CPU를 에뮬레이트하는 것은 실제로 멀티 스레드 될 수 없습니다. 그래도 모든 비디오 렌더링을 자체 스레드로 수행합니다. 필자는 이전 400MHz PowePC G3에서도 실행했으며 실제 8088 속도로 떨어졌습니다.
Mike C

1
대박! 또한 더 많은 op 코드와 세분화를 구현하고 싶었습니다. 그러나 실행할 많은 테스트 프로그램을 찾지 못했습니다. 오래된 롬을 다운로드 했습니까?
Dave C

1
데이브, 실제로 알다시피 놀랍게도 8086 테스트 롬이 심각하게 부족한 것은 없습니다. 내가 한 방법은 일반적인 XT BIOS ROM을 올바르게 실행하여 시작하는 것입니다. 그 정도면 효과가 있다면 세분화는 괜찮을 것입니다. 그 후 DOS가 작동하기 시작할 때까지 디버깅 한 다음 응용 프로그램과 게임으로 전환했습니다! :)
Mike C

1
@ MikeC 초보자 도움이나 포인터를 원합니다! (Pun 예정 : P). 저는 수년 동안 데스크탑 및 웹 앱 개발자였으며 ​​천천히 리눅스 소스 코드를 가지고있는 시점에 도달했습니다. 나는 일반적으로 다양한 OS 기능이 어떻게 작동하는지 이해하고 작은 장난감 OS 프로젝트로 놀 수있었습니다. 그러나 직접 하드웨어와 상호 작용하는 것은 나를 피할 수 없습니다!
기드온

41

포스트 스크립트 (130 200 367 517 531 222 246 줄)

여전히 진행중인 작업이지만나는 다른 사람들이 어떤 코드보여줄 수 있도록 일부 코드를 보여주고 싶었다 .

레지스터 세트는 하나의 문자열로 표시되므로 다양한 바이트 및 워드 크기 레지스터는 하위 문자열을 참조하여 자연스럽게 겹칠 수 있습니다. 하위 문자열은 전체 포인터로 사용되므로 레지스터와 메모리 위치 (메모리 문자열의 하위 문자열)를 연산자 함수에서 균일하게 처리 할 수 ​​있습니다.

그런 다음 "포인터", 메모리, mem [(IP)] (IP 증가)에서 데이터 (바이트 또는 단어)를 가져오고 저장하는 몇 가지 단어가 있습니다. 그런 다음 MOD-REG-R / M 바이트를 페치하고 REG 및 R / M 및 MOD 변수를 설정하고 테이블을 사용하여 디코딩하는 함수가 몇 가지 있습니다. 그런 다음 연산자 코드는 opcode 바이트를 기준으로합니다. 따라서 실행 루프는 간단 fetchb load exec합니다.

나는 소수의 opcode 만 구현했지만 g피연산자 디코딩을 얻는 것은 내가 공유하고 싶은 이정표와 같은 느낌이 들었습니다.

편집 : 음수를 부호 확장하는 단어가 추가되었습니다. 더 많은 opcode. 레지스터 할당의 버그 수정. 코멘트. 여전히 플래그 작업을하고 연산자를 작성합니다. 출력은 종료시 stdout으로 텍스트 출력, vt100 코드를 사용하여 연속 출력, CP437 글꼴을 사용하여 이미지 창으로 출력을 선택합니다.

편집 : 쓰기 완료, 디버깅 시작. 출력의 처음 네 점을 얻습니다! 그런 다음 캐리가 잘못됩니다. 졸리는.

편집 : 나는 캐리 플래그를 정렬했다고 생각합니다. 이야기 중 일부는 comp.lang.postscript에서 발생했습니다 . 디버깅 장치를 추가했으며 출력은 이전에 작성된 Code-Page 437 Type-3 글꼴을 사용하여 그래픽 창으로 이동 하므로 텍스트 출력에 추적 및 덤프가 가득 찰 수 있습니다. "Hello World!"라고 쓰여 있습니다. 그리고 의심스러운 캐럿이 있습니다. 그런 다음 전체 로타가 없습니다. :( 우리는 거기에 갈 것이다. 모든 격려에 감사드립니다!

편집 : 테스트를 완료합니다. 마지막 몇 가지 버그는 다음과 같습니다. XCHG는 물론 2 {read store} 반복을 수행하고 교환하지 않고 복사하고 플래그를 설정하지 않고 (FE) INC는 바이트 포인터에서 단어를 가져 오려고합니다.

편집 : 매뉴얼의 간결한 표를 사용하여 처음부터 완전히 다시 쓰십시오 ( 새 페이지가되었습니다! ). opcode 에서 매장 을 제외 하는 것은 나쁜 생각 이라고 생각하기 시작 했지만 optab을 예쁘게 유지하는 데 도움이되었습니다. 이번에는 스크린 샷이 없습니다. 비디오 메모리를 덤프하기 위해 명령 카운터와 모드 트리거를 추가하여 디버그 정보와 쉽게 인터리브합니다.

편집하다: 테스트 프로그램을 다시 실행하십시오! 짧은 재 작성을위한 마지막 몇 가지 버그는 opcodes 83 ( "즉시"그룹)과 EB (짧은 JMP)에서 즉시 바이트를 부호 확장하는 것을 무시하고있었습니다. 24 줄 증가는 이러한 최종 버그를 추적하는 데 필요한 추가 디버깅 루틴을 포함합니다.

%!
%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

그리고 출력 (약식 디버깅 출력의 꼬리 끝).

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
궁금합니다 ... F4h가 8086 HLT opcode 이기 때문에 응용 프로그램 alt-F4를 종료하는 단축키가 있습니까?
luser droog

5
Postscript에서 이것을 구현하는 데 절대적으로 훌륭하다는 것을 말하고 싶습니다.
cemper93

1
그 코드는 짧다 . 더 많은 투표권이 있습니다. 시작 해봐
JB

2
잠깐만 ... 포스트 스크립트는 프로그래밍 언어입니까?! ;)
n611x007

32

자바 스크립트

jslinux에서 영감을 얻은 자바 스크립트로 486 에뮬레이터를 작성 중입니다. 그것이 얼마나 많은 일인지 알았다면 아마도 시작한 적이 없지만 이제는 끝내고 싶습니다.

그리고 난 당신의 도전에 직면하고 테스트 할 8086 프로그램을 가지고 매우 기쁘게 생각합니다.

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

당신은 "실제로 여기에서 실행"을 볼 수 있습니다 : http://codinguncut.com/jsmachine/

그래픽 버퍼를 인쇄 할 때 한 가지 문제가있었습니다. 공백이 있어야하는 경우 메모리에 "00"요소가 포함됩니다. "0x00"을 공백으로 해석하거나 에뮬레이터에 버그가 있습니까?

건배,

요하네스


흥미롭게도 저는 실제로이 스크린 캐스트에서 귀하의 이름을 알고 있습니다.이 도전에서 Haskell의 응답을 지켜 봤습니다 (또한 Javascript에서 x86 에뮬레이터를 시작했습니다). 예, 0 바이트는 공백으로 나타나야합니다. 또한 게시물에 스크린 샷을 추가했습니다. +1 어쨌든 :-)
복사

@Johannes 나는 mycpu-min.js 코드를 훑어 보았다. 내가 알 수 있듯이 cpux86.js (FB의 jslinux)에서 몇 가지 아이디어 만 사용했습니다. 축하합니다! 잘 했어 컴파일되지 않은 mycpu.js를 어딘가에 볼 수 있습니까? github.com/codinguncut
Yauhen Yakimovich

@YauhenYakimovich 아니요, jslinux 코드를 재사용하지 않았습니다. 지금까지 모든 286 명령어에서 페이징 및 세그먼트 (mmu)를 뺀 것을 구현했습니다. 내 계획은 GPL 하에서 코드를 공개하는 것이지만 Freedos 또는 Reactos 실행 아이디어를 상용화하고 싶기 때문에 라이센스에 대해서는 여전히 확신 할 수 없습니다. 사실, 전체 메모리 관리를 구현하는 데 오랜 시간이 걸릴 것입니다. 그런 다음 속도가 빨라지도록 오랜 시간이 걸립니다. github.com/codinguncut에서 확실히 공유 할 것입니다. 의견을 보내 주셔서 감사합니다. Johannes
fluquid

1
링크가 끊어졌습니다. (Windows 8의 경우 IE)
Conor O'Brien

이것은 매우 늦습니다. 문자 0은 비디오 RAM의 또 다른 공간입니다.
Joshua

30

C ++

이 코드 챌린지에 대한 참가 신청서를 제출하고 싶습니다. C ++로 작성되었으며 테스트 프로그램을 완벽하게 실행합니다. 우리는 1 바이트 연산 코드와 기본 세그먼테이션의 90 %를 구현했습니다 (일부는 테스트 프로그램에서 작동하지 않기 때문에 비활성화되었습니다).

프로그램 작성 : http://davecarruth.com/index.php/2012/04/15/creating-an-8086-emulator

블로그 게시물 끝에있는 zip 파일에서 코드를 찾을 수 있습니다.

테스트 프로그램을 실행하는 스크린 샷 : 여기에 이미지 설명을 입력하십시오

질문이나 의견이 있으시면 언제든지 저에게 메시지를 보내주십시오. 파트너 프로그래밍에서 확실히 큰 연습이었습니다.


3
사람들 이이 도전에 재미있을 때 항상 좋습니다 :) 몇 가지 참고 사항 : 내 테스트 프로그램은 모든 세그먼트가 0으로 작동하고 작동해야합니다. 귀하의 코드 중 일부를 살펴보면 ret imm지침이 잘못되어 ( 여기 참조 ) 0xff그룹 이 누락 된 것으로 나타났습니다 . 오류 메시지가 마음에 듭니다 : throw "즉시 값은 값을 저장할 수없고 지연됩니다.";
복사

테스트 프로그램에는 두 가지 주요 문제가있었습니다. 1) 세분화-호출이있을 때 스택에 CS를 푸시했습니다. 테스트 프로그램의 기능 중 하나가이 방식을 좋아하지 않았습니다. 2) 테스트 프로그램은 메모리가 0으로 초기화 될 것으로 예상했다. 여하튼, 우리는 많은 재미를 가지고 있었다, 게시 해 주셔서 감사합니다!
Dave C

당신은 거기서 실수했을 것입니다 : Near jumps ( 0xE8 )는 cs레지스터를 밀지 않습니다
복사

문제가 될 것입니다. 당신은 8086에 매우 경험이있는 것 같습니다.
Dave C

1
실제로 x86 에뮬레이터 프로젝트를 직접 수행하고 있습니다. 프리도를 잘 실행하고 있으며 현재 32 비트를 완벽하게 지원하고 있습니다. 다른 포스터에 공평하지 않을 수 있기 때문에 여기에 게시하지 않았습니다 (소스 코드가 약간 엉망입니다).
복사

28

큰 도전과 첫 번째 도전. 도전이 너무 흥미로워 계정을 만들었습니다. 단점은 실제로 지불하고 프로그래밍해야 할 과제가 있었을 때 도전에 대한 생각을 멈출 수 없다는 것입니다.

완성 된 8086 에뮬레이션을 실행해야한다는 느낌이 들지만 이는 또 다른 도전입니다. ;-)

코드는 ANSI-C로 작성되었으므로 .c 파일을 함께 컴파일 / 링크하고 codegolf 바이너리를 전달한 다음 진행하십시오.

압축 된 소스

여기에 이미지 설명을 입력하십시오


좋은 직업 RichTX!
Dave C

고마워 데이브. 너도. 처음 시작할 때 코드를 최대한 작게 만들 겠다는 기대를 이해하지 못했지만 여전히 도전 과제였습니다.
RichTX

+1 캐리 플래그의 작동 방식을 파악하기 위해 코드를 살펴 보았습니다.
luser droog

링크가 다운되었습니다.
Tyilo

25

C ++ 1064 라인

환상적인 프로젝트. 나는 한 인텔리 에뮬레이터를 몇 년 전, 그래서 다시는 내 비트 뱅잉 근육을 플렉스 좋았어요.

일주일 정도 일한 후에, 나는 이것이 일어날 때 더 흥분 할 수 없었습니다.

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


############################################### ##############################
############################################### ######################
    0000 0000 0000 0000

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

    2 34 5678 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`

나중에 조금 디버깅하고 ... SHAZAM! 여기에 이미지 설명을 입력하십시오

또한 80386 확장을 사용하지 않고 원래의 테스트 프로그램을 다시 작성했습니다. 8086에 충실한 에뮬레이터를 만들고 추가 지침을 따르지 않기 때문입니다. 여기에 코드로 직접 연결되는 링크 : Zip 파일 .

좋아, 나는 이것으로 너무 재미있다. 메모리와 화면 관리를 중단했으며 이제 화면 버퍼를 쓸 때 화면이 업데이트됩니다. 나는 비디오를 만들었다 :)

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

업데이트 : 세그먼트 화의 첫 번째 단계가 진행 중입니다. 실제로 구현되는 명령은 거의 없지만 CS / DS 및 SS를 이동하여 테스트했으며 모든 것이 여전히 잘 작동합니다.

기본적인 인터럽트 처리 기능도 추가되었습니다. 아주 초보적인. 그러나 문자열을 인쇄하기 위해 int 21h를 구현했습니다. 테스트 소스에 몇 줄을 추가하고 업로드했습니다.

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

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

여기에 이미지 설명을 입력하십시오

누구든지 세그먼트를 테스트 할 꽤 간단한 어셈블리 코드가 있다면, 그 코드를 가지고 놀고 싶습니다.

나는 이것을 얼마나 멀리 가지고 싶어하는지 알아 내려고 노력 중이다. 전체 CPU 에뮬레이션? VGA 모드? 이제 DOSBox를 작성하고 있습니다.

12/6 : VGA 모드를 확인하십시오!

여기에 이미지 설명을 입력하십시오


등록이 필요없는 무료 사이트에 코드를 게시 할 수 있습니까? 감사합니다
Dave C

나는 등록이 필요하다는 것을 몰랐다. 미안합니다! 오늘 밤 집에 갈 때 해보도록하겠습니다.
JoeFish

@DaveC, 최신 편집을 확인하십시오.
JoeFish

낙타 포트가 있는지 궁금합니다. 세그먼트를 테스트합니다.
luser droog

대단해! 다시 +1 BTW있을 것이다 낙타있어서의 8086 포트 bradrodriguez.com/papers/index.html이 .
luser droog

25

C ++-4455 줄

그리고 아니오, 나는 단지 질문의 요구 사항을 수행하지 않았습니다. 나는 이전에 알려지지 않은 16 개의 알려진 opcode를 포함하여 ENTIRE 8086을 수행했습니다. reenigne은 그 opcode를 알아내는 데 도움을주었습니다.

https://github.com/Alegend45/IBM5150


4455 줄 파일은 어디에 있습니까? 아, 찾았 어 보기 #include "cpu.h"어렵다.
luser droog

2
(w) 거룩한 성명서!
luser droog

네, NEC V20 지원도 포함 시키려고해서 더 나빠질 것입니다.
다리우스 고드

reenigne의 블로그를 살펴 보았습니다 . 이 추가 opcode에 대해 아무것도 찾을 수 없습니다. 온라인 어딘가에 있습니까?
luser droog

1
그는 한동안 블로그를 업데이트하지 않았습니다. 그는 EFNET에서 # ibm5150을 사용하고 있으므로 여기에서 요청할 수 있습니다.
다리우스 고드

20

자바 스크립트-4,404 줄

내 에뮬레이터에 대한 정보를 조사 할 때이 게시물을 우연히 발견했습니다. 이 Codegolf 게시물은 나에게 매우 귀중한 것입니다. 예제 프로그램과 관련 어셈블리를 사용하면 쉽게 디버깅하고 무슨 일이 있었는지 확인할 수있었습니다.

감사합니다!!!

그리고 여기 내 Javascript 8086 에뮬레이터의 첫 번째 버전이 있습니다.

실행 완료

풍모:

  • 이 챌린지에 필요한 모든 opcode와 쉽게 코딩 할 수있는 유사한 추가 코드
  • 부분적으로 작동하는 텍스트 모드 (80x25) 비디오 (아직 인터럽트 없음)
  • 기능성 스택
  • 기본 (비분 할) 메모리
  • 꽤 괜찮은 디버깅 (이것이 있어야 함)
  • 코드 페이지 437 글꼴 세트는 비트 맵 표현에서 동적으로로드됩니다.

데모

온라인 데모가 있습니다. 버그를 발견하면 알려주십시오. :)

http://js86emu.chadrempp.com/

codegolf 프로그램을 실행하려면

1) 설정 버튼을 클릭하십시오

여기에 이미지 설명을 입력하십시오

2) 그런 다음로드를 클릭하십시오 (프로그램을 통한 스테핑과 같은 디버그 옵션으로 재생할 수 있음). codegolf 프로그램은 현재 사용할 수있는 유일한 프로그램이며 온라인에서 더 많은 작업을 진행하고 있습니다.

여기에 이미지 설명을 입력하십시오

출처

여기에 전체 소스. https://github.com/crempp/js86emu

8086 에뮬레이션의 내장을 여기에 붙여 넣으려고했지만 (도어 노브에서 제안한대로) 문자 제한을 초과했습니다 ( "본문은 30000 자로 제한되어 있습니다. 158,272 입력했습니다").

여기에 붙여 넣을 코드에 대한 빠른 링크가 있습니다-https: //github.com/crempp/js86emu/blob/39dbcb7106a0aaf59e003cd7f722acb4b6923d87/src/js/emu/cpus/8086.js

*Edit - updated for new demo and repo location


와우! 그러나 게시물이 자체 포함되는 것을 선호하므로 코드가 게시물 자체에 있으면 이상적입니다.
Doorknob

@Doorknob, 잘 모르겠습니다. 게시물에 4,400 줄의 코드를 인라인으로 게시하고 싶습니까?
crempp

음 ... 나는 그것이 그렇게 길다 는 것을 몰랐다 . 최대 글자 수 제한에 맞습니까? 그렇다면 게시물이 자체적으로 포함되어 있다면 좋을 것입니다. 감사! :-)
Doorknob

13

자바

나는이 도전을 오랫동안하고 싶었고, 마침내 시간을 들였다. 지금까지 멋진 경험을 해왔고 마지막으로 완성한 것을 자랑스럽게 생각합니다.

테스트 프로그램 출력

출처

소스 코드는 GitHub의 NeatMonster / Intel8086 에서 사용할 수 있습니다 . holly 8086 Family User 's Manual 의 도움으로 거의 모든 것을 문서화하려고했습니다. .

누락 된 opcode 및 기능을 모두 구현하려고하므로이 문제에 필요한 버전 만있는 버전 의 릴리스 1.0 을 확인하십시오 .

@copy에 감사드립니다!


13

공통 리스프-580 loc (442 빈 줄 및 주석 없음)

나는이 도전을 커먼 리스프를 배우기위한 변명으로 사용했습니다. 결과는 다음과 같습니다.

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

Emacs의 출력 결과는 다음과 같습니다.

왼쪽에있는 Lisp 소스 섹션과 오른쪽에 필요한 컨텐츠가있는 REPL 출력의 두 개의 분할 창이있는 Emacs 창.

세 가지 주요 기능을 강조하고 싶습니다. 같은 지침을 구현할 때이 코드는 매크로를 많이 사용하게 mov, xchg그리고 산술 (artithmetic) 작업을. 각 명령에는disasm-instr 매크로 호출이 . 런타임에 전역 변수 세트를 통해 if를 사용하여 실제 코드와 함께 디스 어셈블리를 구현합니다. 특히 레지스터 및 간접 주소에 값을 쓰는 데 사용되는 대상에 독립적 인 접근 방식을 자랑스럽게 생각합니다. 지침을 구현하는 매크로는 대상에 대해 신경 쓰지 않습니다. 두 가지 방법으로 결합 된 양식은 일반적인 setfCommon Lisp 매크로 와 함께 작동하기 때문입니다 .

코드는 다음에서 찾을 수 있습니다 내 GitHub 저장소 . 마스터에서 8086의 다른 기능을 구현하기 시작한 "codegolf"분기를 찾으십시오. FLAGS 레지스터와 함께 오버플로 및 패리티 플래그를 이미 구현했습니다.

이이하지의 세 가지 작업은 8086에있는의 0x820x83 논리 연산자의 버전 버전의 있습니다. 이것은 매우 늦게 걸렸으며 이러한 작업을 제거하는 것은 상당히 어려울 것입니다.

이 벤처에서 초기에 영감을 얻은 Python 버전에 대해 @ja에게 감사하고 싶습니다.


3
놀라운 첫 번째 답변! 사이트에 오신 것을 환영합니다 :)
DJMcMayhem

1
매우 멋진 언어 선택!
복사

12

씨 - 319 348 줄

이것은 Postscript 프로그램을 C로 직접 변환하는 것입니다. 물론 스택 사용법은 명시 적 변수로 대체됩니다. 명령어의 필드는 변수 o-명령어 opcode 바이트, d-방향 필드, w-너비 필드 로 나뉩니다 . "mod-reg-r / m"명령어 인 경우 mr-rm 바이트는로 읽습니다 struct rm r. reg 및 r / m 필드 디코딩은 두 가지 단계로 진행됩니다. 데이터에 대한 포인터를 계산하고 동일한 변수를 재사용하여 데이터를로드합니다. 따라서 ADD AX,BX, 먼저 x는 ax에 대한 포인터이고 y는 bx에 대한 포인터이고 x는 내용 (ax)이고 y는 내용 (bx)입니다. 이와 같은 다양한 유형의 변수를 재사용하려면 많은 캐스팅이 필요합니다.

opcode 바이트는 함수 포인터 테이블로 디코딩됩니다. 각 기능 몸체는 재사용 가능한 조각에 매크로를 사용하여 구성됩니다. DW매크로 오피 모든 기능에 존재하고, 디코딩 d하고 w로부터 변수 o오피 코드 바이트. RMP매크로 "MR-RM '바이트를 디코딩하는 첫 번째 단계를 수행하고,LDXY 두 번째 단계를 수행한다. 결과를 저장하는 opcode는 p변수를 사용하여 결과 위치에 대한 포인터 z를 보유하고 결과 값을 보유하는 변수를 사용합니다. z값 이 계산 된 후 플래그가 계산 됩니다. INCDEC일반 사용하기 전에 작업 캐리 플래그를 저장 MATHFLAGS(의 일부로서 기능을 submacro)과 캐리를 유지하기 위해, afterwords를 복원합니다.ADD 또는SUB

편집 : 버그 수정!
편집 : 확장 및 의견. 경우 trace==0비디오 덤핑 때 이제 ANSI 이동 대 0,0 명령을 출력한다. 따라서 실제 디스플레이를 더 잘 시뮬레이션합니다. 그만큼BIGENDIAN것은 (그래도 문제가 해결되지 않으면) 제거되었습니다. 리틀 엔디안 바이트 순서로 일부 위치에 의존하지만 다음 개정 에서이 문제를 해결할 계획입니다. 기본적으로 모든 포인터 액세스 는 바이트를 LE 순서로 명시 적으로 (de) 구성하는 get_put_함수 를 거쳐야합니다 .

#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

에 매크로 사용 다양한 작업 단계 에 하면 포스트 스크립트 코드가 순차 순차 방식으로 작동하는 방식과 매우 밀접한 의미가 일치합니다. 예를 들어, 처음 네 개의 오피 코드 0x00-0x03은 ​​방향 (REG-> REG / MOD, REG <-REG / MOD) 및 바이트 / 워드 크기가 다양한 모든 ADD 명령어이므로 기능 테이블에서 정확히 동일하게 표시됩니다. .

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

함수 테이블은 다음 매크로로 인스턴스화됩니다.

OP(OPF)

이는 OPF()각 opcode 표현에 적용됩니다 .OPF()다음과 같이 정의됩니다.

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

따라서 처음 네 개의 opcode는 다음과 같이 확장됩니다 (한 번).

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

이 함수들은 DW매크로 의 결과에 의해 opcode 바이트로부터 방향과 바이트 / 워드 비트를 결정합니다. 이 함수 중 하나의 본문을 확장하면 (한 번) 다음이 생성됩니다.

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
;

메인 루프가 이미 설정되어있는 곳 o 변수를 경우 :

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

한 번 더 확장하면 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);
;

그리고 전처리 된 기능은 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);;
}

일상적인 사용에 가장 적합한 C 스타일은 아니지만 매크로를 이런 식으로 사용하면 구현을 매우 짧고 직접적으로 구현할 수 있습니다.

추적 출력의 테일과 함께 테스트 프로그램 출력 :

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    ##
##                                                                            ##
##                                                                            ##
##                                                                            ##
##                                                                            ##
##                                                                            ##
##                                                                            ##
##                                                                            ##
##                                                                            ##
##                                                                            ##
##                                                                            ##
##                                                                            ##
##                                                                            ##
################################################################################

comp.lang.c 에서 일부 이전 버전을 공유 했지만 그다지 관심이 없었습니다.


매크로 확장 버전 Wrapped . (그것은 도움이되지 않습니다 :)
luser droog

indented 5810 라인.
luser droog
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.