写一个模拟器 [英] Writing an Emulator

查看:97
本文介绍了写一个模拟器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

全部,


几个星期前我在这里发布了一个新闻组问题,询问了一些与我10年任务相关的问题(目前为止)理解

指针。


有人建议我写一个简单的模拟器。他的部分帖子是

以下。我会直接通过电子邮件发送给他,但有效的电子邮件不包含在内。$

我很确定这对他很简单,但我不知道不明白!我从概念上理解他的建议,但不知道从哪里开始。

将如何运行这些程序?如何执行指令?什么

我的指示呢?只需在屏幕上打印它们? Jeez,

也许如果我是一名硬件工程师,我可能会有一个线索。


Doug。


" ;如果你想了解指针,写一台电脑。或者更确切地说,

为您选择的不存在的计算机写一个

模拟器。


开始简单。具有2个1位字节的1位计算机。内存,一个

1位

指令寄存器(它保存要执行的下一个

指令的内存地址),以及一个1位寄存器。确定一个

指令集(它只有两个不同的指令),



然后写它。 /然后/为它编写程序。 (这些很简单:00,01,

10,

11.全部完成。)


这可以在大约300行C代码。如果你是一个简洁的话,那就少了。

编码器,但我的是300行(不包括一些printfs和评论),

和它

甚至包括一个基本的反汇编程序。


然后写一台2位计算机。那将有四个2位字节。

内存,一个

2位指令寄存器,也许你可以大胆并给它

两个

2位寄存器。指令集可以有大量的四个

指令。


我的版本需要大约350行。 3位版本约为400美元b $ b行。

这并不难,相信我。当你达到8位时,



/将/理解指针。

All,

I posted a newsgroup question here a few weeks back, asking some
questions that related to my 10 year quest (so far) to understand
pointers.

Someone suggested I write a simple emulator. Part of his post is
below. I would have emailed him directly but a valid email wasn''t
included.

I''m sure its quite simple for him, but I don''t get it! I understand
his suggestion conceptually, but don''t have a clue where to start. How
will these ''programs'' run? How will the instructions be executed? What
do I do with the instructions? Just print them on the screen? Jeez,
maybe if I was a hardware engineer I might have a clue.

Doug.

"If you want to understand pointers, write a computer. Or rather,
write an
emulator for the non-existent computer of your choice.

Start off simple. A 1-bit computer with 2 1-bit "bytes" of memory, a
1-bit
instruction register (it holds the address in memory of the next
instruction to be executed), and one 1-bit register. Decide on an
instruction set for it (it''ll have just two different instructions),
and
then write it. /Then/ write programs for it. (These are easy: 00, 01,
10,
11. All done.)

This can be done in around 300 lines of C code. Less if you''re a terse
coder, but mine is 300 lines (excluding a few printfs and comments),
and it
even includes a rudimentary disassembler.

Then write a 2-bit computer. That''ll have four 2-bit "bytes" of
memory, a
2-bit instruction register, and perhaps you can be daring and give it
two
2-bit registers. The instruction set can have a massive FOUR
instructions.

My version takes about 350 lines. And the 3-bit version is about 400
lines.
It''s not difficult, believe me. And by the time you get up to 8 bits,
you
/will/ understand pointers.

推荐答案



[第二次尝试;我不知道第一个发生了什么。

Cc''ed到OP,以防Usenet也吃这个。

F-up comp.programming。]


在Sun,2003年11月23日,Douglas Garstang写道:

[Second try; I don''t know what happened to the first one.
Cc''ed to the OP, just in case Usenet eats this one too.
F-up to comp.programming.]

On Sun, 23 Nov 2003, Douglas Garstang wrote:

几个星期后我在这里发布了一个新闻组问题,询问一些与我10年任务相关的问题(到目前为止)了解
指针。

有人


Richard Heathfield 。如果你花时间去查看你的报价,那将是很好的。 Google网上论坛可能有帮助。

建议我写一个简单的模拟器。他的部分帖子如下。我会直接通过电子邮件发送给他,但是没有包含有效的电子邮件。

我很确定这对他来说非常简单,但我不明白!我从概念上理解他的建议,但不知道从哪里开始。
这些程序将如何运行?如何执行指令?我对指示做了什么?只需在屏幕上打印它们? Jeez,
也许如果我是一名硬件工程师,我可能会有一些线索。


基本上,理查德建议您创建一个假想的

计算机。从头开始 - 你甚至可以在纸上做到这一点。一旦你得到了基本的想法,你就可以编写一个C程序,用软件模拟

想象中的机器。

就个人而言,我认为这是学习指针的一种不好的方法,虽然这是一个很好的练习,可以大致了解计算机如何工作。见下文。


[理查德希思菲尔德写道:]如果你想了解指针,写一台电脑。或者更确切地说,为您选择的不存在的计算机编写模拟器。

从简单开始。具有2个1位字节的1位计算机。存储器,1位指令寄存器(它保存要执行的下一条指令的存储器中的地址)和一个1位寄存器。确定
一个指令集(它只有两个不同的指令),
然后写它。


好​​的。让我们做一台电脑吧!但首先我们需要直接获得一些

条款。大多数现代计算机具有寄存器。和内存。

内存是存储数据的大空间;它有很多

,它可以按顺序处理。内存是计算机

存储程序和其他数据的地方。相比之下,登记册的存储空间非常小。只有少数几个,但大多数CPU操作,

或操作码,直接对它们进行操作,因此它们非常快速且有用。

计算机A是我们第一次尝试制造计算机。这是理查德在上面描述的1位计算机。

计算机A有一个1位指令寄存器,IP(指令

指针)。 IP保存下一条指令的存储器地址,以便由CPU执行
。执行完每条指令后,CPU

自动将IP递增1.


练习0:计算机A有多少不同的内存地址
$ b总共有$ b?

答:请注意IP只有一位宽。这意味着

计算机A只能处理所有地址中的2个内存地址:地址0和

地址1(M0和M1,在我们的表示法中) 。


计算机A还有一个通用寄存器R0。这也是一点

长。

注意,寄存器IP和R0没有内存地址;

M0和M1不同于寄存器组。


另请注意,计算机A的状态可以完全描述

只有四个数字 - 四位,真。例如,一个状态

计算机A是

IP:0 R0:1 M0:1 M0:0

这四个数字绝对决定关于状态的一切

机器。

现在让我们添加一些CPU操作码,这样计算机实际上可以开始处理
程式。首先,我们总是需要一个

停止。操作码终止程序。让操作码''0'为HALT。

每当IP最终指向包含HALT

操作码的存储器地址时,计算机将停止执行程序(程序)

将成功终止。

让操作码1成为打印Hello world的操作。到

屏幕。 (这些想象中的操作码可以做任何你喜欢的事情,真的是

- 但这是一个很好的用户友好操作码,可以编程

计算机A更有趣。)


练习1:假设计算机A的状态为

IP:0 R0:1 M0:1 M0:0

如果我们让计算机从这一点开始运行会怎样?

程序是否成功终止?有什么东西可以打印吗?

答案:IP是0.内存地址0的指令是''1',

所以我们打印Hello world。 IP增加1。现在IP

是1. M1的指令是'0',HALT,所以程序

终止。所以整个程序的效果是打印

Hello world。然后停止 - 经典的初学者计划!


练习2:假设机器的状态是

IP:1 R0:0 M0: 1 M0:1

如果我们让计算机从这一点开始运行会发生什么?

假设IP上的所有算术都以模2运行,那么

IP的值环绕溢出。

答案:M1的操作码为'1',因此我们打印Hello world。

IP递增;我们执行M0,即'1',并打印

Hello world再次。 IP增加到1,我们打印

Hello world再次。然后再次。等等。这个程序,简称
,是一个永无止境的无限循环。


练习3:在一张纸上画出16种可能的状态/>
计算机A.(16是两到四次幂。)再绘制一个状态,

并将其标记为程序终止。

从每个州画一个箭头到它的后继者。州,州执行当前指令达到
。沿着每个箭头,

在该阶段写出程序的输出(即,Hello

world或者什么都没有)。你看到了什么?

答:这个状态图非常无聊,不是吗?你应该已经注意到了b0 b已经注意到R0寄存器没有被用于

任何东西;您也可能已经注意到所有程序都是无限循环,或者在一个或两个指令周期之后停止。我们很快就会给b $ b补救一下。

/然后/为它编写程序。 (这很容易:00,01,10,1。全部完成。)


我走在你前面,理查德。 ;-)


练习4:让程序AB(对于任何值A,B)成为状态

IP:0 R0:0 M0:A M0:B

描述四个程序00,01,10和11的效果。


这可以在大约300行C代码中完成。如果你是一个简洁的编码器,那就少了,但我的是300行(不包括一些printfs和评论),
它甚至包括一个基本的反汇编程序。


那么我必须是一个非常简洁的程序员。 :-)

将这个C程序剪切并粘贴到另一个文件中,编译并运行它。

尝试练习4中的所有四个程序。他们做了什么<你认为他们会吗?
? (注意程序11,无限循环,

将要求你知道如何在[real]

操作系统中打破程序 - 尝试Ctrl- C适用于Windows或Linux。)


==在此处切换==


#include< stdio.h>


struct MemoryCell_1bit {

无符号值:1;

};


struct ComputerA {

无符号R0:1; / * 1位寄存器* /

无符号IP:1; / * 1位IP * /

struct MemoryCell_1bit M [2]; / *(2比1)内存单元* /

};


#define OPCODE_HALT 0 / *停止程序* /

#define OPCODE_HELW 1 / * printHello world * /


int main()

{

struct ComputerA a;

int t1, t2,t3,t4;

int running;


puts(请输入R0,IP,M0和M1的初始值,) ;

puts(以下列格式:(1 1)(0 1)。");

printf("立即输入。>" );

fflush(stdout);

if(4!= scanf("(%d%d)(%d%d)"& t1 ,& t2,& t3,& t4)){

puts(Something fouled up - 请再次运行程序。)

返回0;

}


a.R0 = t1;

a.IP = t2;

aM [0] .value = t3;

aM [1] .value = t4;


for(running = 1; running; a。 IP ++){

开关(aM [a.IP] .value)

{

案例OPCODE_HALT:

running = 0; / *停止计划* /

休息;

案例OPCODE_HELW:

put(Hello world);

休息;

}

}


put(程序终止。);

返回0;

}


==在这里切= =


然后写一个2-位电脑。那将有四个2位字节。的内存,一个2位指令寄存器,也许你可以大胆地给它两个2位寄存器。指令集可以有大量的四个指令。


好​​吧,这里有点变得有趣了。让我们设计计算机B,我们是计算机A的2位继任者。这次,我只是用b来描述它,所以拿花点时间研究计算机A的实现。

struct MemoryCell_2bit {

无符号值:2;

};


struct ComputerB {

unsigned R0:2; / * 2位寄存器* /

无符号IP:2; / * 2位IP * /

struct MemoryCell_2bit M [4]; / *(2到2)内存单元格* /

};


#define OPCODE_HALT 0

#define OPCODE_LOAD 1 / *占用内存地址* /

#define OPCODE_INCR 2 / *增量R0 * /

#define OPCODE_STOR 3 / *占用内存地址* /

您可以看到计算机B仍然只有一个寄存器,R0,

但是R0和IP现在是2位寄存器,内存也是如此。

单元格(现在有四个单元格)。 OPCODE_HALT仍然存在,

但是我已经拿走了HELW操作码并将其替换为三个更加神秘的指令:LOAD,INCR和STOR。

LOAD取字节紧跟内存中的操作码

并使用这两位作为地址。它将存储在该内存地址的值加载到R0中。 (例如,双字节指令

sequence''1 3''将M3的内容加载到R0中。)

STOR执行相反的操作,存储当前的值R0返回

到指定的内存位置。

INCR只是递增R0。


这是我的其余代码计算机B的实施。

请注意,我已经添加了print_machine_state。函数,并且

机器在执行每条指令之前打印它的状态。

通过这种方式,我们可以跟踪我们的代码所做的事情。

您还应该注意,我没有在计算机B中放置任何屏幕输出操作码

,因此print_machine_state也是如此。输出是我们将获得的唯一

输出。虽然计算机B可以通过在执行期间修改自己的程序来做一些漂亮的东西,但是它不能打印Hello world。了。 (但等一下......)

#include< stdio.h>

#define NELEM(x)((int)(sizeof(x)/ sizeof *(x)))

void print_machine_state(struct ComputerB * p);


int main()

{

struct ComputerB b;

int t1,t2;

int i;

int running;


puts(请输入R0,IP和M0..M3的初始值,);

puts("采用以下格式:(3 0)(1 2 3 0)。);

printf(现在输入。>");

fflush(stdout);

if(2!= scanf("(%d%d)(",& t1,& t2)){

puts(Something fouled up - - 请再次运行程序。);

返回0;

}

b.R0 = t1;

b.IP = t2;


for(i = 0; i< NELEM(bM); ++ i){

if( 1!= scanf("%d"& t1)){

puts(Something fouled up - 请再次运行程序。);

返回0;

}

bM [i] .value = t1;

}


for(running = 1 ;运行; b.IP ++){

print_machine_state(& b);

switch(bM [b.IP] .value)

{

case OPCODE_HALT:

running = 0; / *停止计划* /

休息;

案例OPCODE_LOAD:

b.IP ++;

b.R0 = bM [bM [b.IP] .value] .value;

break;

case OPCODE_INCR:

b.R0 ++;

休息;

案例OPCODE_STOR:

b.IP ++;

bM [bM [b.IP] .value] .value = b.R0;

休息;

}

}


puts(" ;程序终止。);


puts(机器的最终状态为:);

print_machine_state(& b) ;


返回0;

}


void print_machine_state(struct ComputerB * p)

{

int i;

printf(" IP:%d R0:%d \ n",(int)p-> IP,( int)p-> R0);

for(i = 0; i< NELEM(p-> M); ++ i)

printf(" ; M%d:%d",i,(int)p-> M [i] .value);

printf(" \ n");

}

练习5:用20或30个州的子集重复练习3

来自计算机B.(计算机B总共有几个州?)

像以前一样绘制箭头。您注意到了什么?

答案:计算机B总共有4096(= 4 ** 6)个状态。这太多了,不能立刻画出来。但即使只有一小部分状态,你应该看到图表比计算机A的图表更有趣和复杂。

我的版本约需350行。而3位版本大约有400行。相信我,这并不困难。当你起床到8位时,你/将会/理解指针。

I posted a newsgroup question here a few weeks back, asking some
questions that related to my 10 year quest (so far) to understand
pointers.

Someone
Richard Heathfield. It would be nice if you''d take the time to
look up your quotes. Google Groups might help.
suggested I write a simple emulator. Part of his post is
below. I would have emailed him directly but a valid email wasn''t
included.

I''m sure its quite simple for him, but I don''t get it! I understand
his suggestion conceptually, but don''t have a clue where to start. How
will these ''programs'' run? How will the instructions be executed? What
do I do with the instructions? Just print them on the screen? Jeez,
maybe if I was a hardware engineer I might have a clue.
Basically, Richard is suggesting that you create an "imaginary
computer" from scratch -- you can even do this on paper. Once you
have got the basic idea, you can write a C program to emulate the
imaginary machine in software.
Personally, I think this is a bad way to learn pointers, although
it''s a nice exercise for general understanding of how computers
work. See below.

[Richard Heathfield wrote:] If you want to understand pointers, write a computer. Or rather,
write an emulator for the non-existent computer of your choice.

Start off simple. A 1-bit computer with 2 1-bit "bytes" of memory, a
1-bit instruction register (it holds the address in memory of the
next instruction to be executed), and one 1-bit register. Decide on
an instruction set for it (it''ll have just two different instructions),
and then write it.
Okay. Let''s make a computer! But first we need to get a few
terms straight. Most modern computers have "registers" and "memory."
Memory is the big open space where data is stored; there''s a lot of
it, and it can be addressed sequentially. Memory is where the computer
stores programs and other data. Registers are by comparison very small
areas of storage; there are only a few of them, but most CPU operations,
or "opcodes," operate directly on them, so they''re fast and useful.
Computer A is our first attempt at making a computer. It''s the
1-bit computer Richard describes above.
Computer A has one 1-bit instruction register, IP ("Instruction
Pointer"). IP holds the memory address of the next instruction to
be executed by the CPU. After executing each instruction, the CPU
automatically increments IP by 1.

Exercise 0: How many different memory addresses does Computer A
have, in all?
Answer: Notice that IP is only one bit wide. That means that
Computer A can address only 2 memory addresses in all: address 0 and
address 1 ("M0" and "M1", in our notation).

Computer A also has a general-purpose register, R0. It''s one bit
long, too.
Note that the registers IP and R0 don''t have memory addresses;
M0 and M1 are distinct from the register set.

Notice also that the state of Computer A can be completely described
by only four numbers -- four bits, really. For example, one state of
Computer A is
IP: 0 R0: 1 M0: 1 M0: 0
These four numbers determine absolutely everything about the state
of the machine.
Now let''s add some CPU opcodes, so that the computer can actually
start processing programs. First, we are always going to need a
"halt" opcode to terminate the program. Let opcode ''0'' be HALT.
Whenever IP ends up pointing to a memory address containing a HALT
opcode, the computer will stop executing the program (the program
will "terminate" successfully).
Let opcode ''1'' be the operation of printing "Hello world" to the
screen. (These imaginary opcodes can do whatever you like, really
-- but this is a nice user-friendly opcode that will make programming
Computer A a little more fun.)

Exercise 1: Suppose the state of the Computer A is
IP: 0 R0: 1 M0: 1 M0: 0
What happens if we let the computer run from this point? Does
the program terminate succesfully? Does anything get printed?
Answer: IP is 0. The instruction at memory address 0 is ''1'',
so we print "Hello world." IP is incremented by one. Now IP
is 1. The instruction at M1 is ''0'', HALT, so the program
terminates. So the effect of the total program is to print
"Hello world" and then halt -- the classic beginners'' program!

Exercise 2: Suppose the state of the machine is
IP: 1 R0: 0 M0: 1 M0: 1
What happens if we let the computer run from this point?
Assume that all arithmetic on IP is done modulo 2, so that
the value of IP "wraps around" on overflow.
Answer: The opcode at M1 is ''1'', so we print "Hello world."
IP is incremented; we execute M0, which is ''1'', and print
"Hello world" again. IP is incremented to 1, and we print
"Hello world" again. And again. And so on. This program,
in short, is an infinite loop that never terminates.

Exercise 3: Draw on a piece of paper the 16 possible states of
Computer A. (16 is two to the fourth power.) Draw one more state,
and label it "Program Terminated."
Draw an arrow from each state to its "successor" state, the state
reached by executing the current instruction. Along each arrow,
write the output of the program at that stage (i.e., either "Hello
world" or nothing). What do you see?
Answer: This state diagram is pretty boring, isn''t it? You should
have noticed by now that the R0 register isn''t being used for
anything; you also might have noticed that all programs are either
infinite loops, or stop after one or two "instruction cycles." We''ll
remedy this shortly.
/Then/ write programs for it. (These are easy: 00, 01, 10, 11. All done.)
I''m way ahead of you, Richard. ;-)

Exercise 4: Let the program AB (for any values A, B) be the state
IP: 0 R0: 0 M0: A M0: B
Describe the effects of the four programs 00, 01, 10, and 11.

This can be done in around 300 lines of C code. Less if you''re a terse
coder, but mine is 300 lines (excluding a few printfs and comments),
and it even includes a rudimentary disassembler.
I must be a very terse coder, then. :-)
Cut and paste this C program into another file, compile and run it.
Try out all four of the programs from Exercise 4. Do they do what
you thought they would? (Note that program 11, the infinite loop,
will require you to know how to break out of a program in your [real]
operating system -- try Ctrl-C for Windows or Linux.)

==cut here==

#include <stdio.h>

struct MemoryCell_1bit {
unsigned value: 1;
};

struct ComputerA {
unsigned R0: 1; /* 1 bit register */
unsigned IP: 1; /* 1 bit IP */
struct MemoryCell_1bit M[2]; /* (2 to the 1) memory cells */
};

#define OPCODE_HALT 0 /* stop the program */
#define OPCODE_HELW 1 /* print "Hello world" */

int main()
{
struct ComputerA a;
int t1, t2, t3, t4;
int running;

puts("Please input the initial values of R0, IP, M0 and M1,");
puts("in the following format: (1 1) (0 1) .");
printf("Input now. >");
fflush(stdout);
if (4 != scanf(" (%d%d ) (%d%d )", &t1, &t2, &t3, &t4)) {
puts("Something fouled up -- please run the program again.");
return 0;
}

a.R0 = t1;
a.IP = t2;
a.M[0].value = t3;
a.M[1].value = t4;

for (running = 1; running; a.IP++) {
switch (a.M[a.IP].value)
{
case OPCODE_HALT:
running = 0; /* halt program */
break;
case OPCODE_HELW:
puts("Hello world");
break;
}
}

puts("Program terminated.");
return 0;
}

==cut here==

Then write a 2-bit computer. That''ll have four 2-bit "bytes" of
memory, a 2-bit instruction register, and perhaps you can be daring
and give it two 2-bit registers. The instruction set can have a
massive FOUR instructions.
All right, here''s where it gets slightly more interesting. Let''s
design Computer B, our 2-bit successor to Computer A. This time, I''m
going to describe it only using C, so take a moment to study the
implementation of Computer A before plunging ahead.
struct MemoryCell_2bit {
unsigned value: 2;
};

struct ComputerB {
unsigned R0: 2; /* 2 bit register */
unsigned IP: 2; /* 2 bit IP */
struct MemoryCell_2bit M[4]; /* (2 to the 2) memory cells */
};

#define OPCODE_HALT 0
#define OPCODE_LOAD 1 /* takes a memory address */
#define OPCODE_INCR 2 /* increment R0 */
#define OPCODE_STOR 3 /* takes a memory address */
You can see that Computer B has still only the one register, R0,
but that R0 and IP are now 2-bit registers, and so are the memory
cells (and now there are four of them). OPCODE_HALT still exists,
but I''ve taken away the HELW opcode and replaced it with three
more cryptic instructions: LOAD, INCR, and STOR.
LOAD takes the "byte" immediately following the opcode in memory
and uses those two bits as an address. It loads the value stored
at that address in memory into R0. (E.g., the two-byte instruction
sequence ''1 3'' loads the contents of M3 into R0.)
STOR does the reverse, storing the value currently in R0 back
into the specified memory location.
INCR simply increments R0.

Here is the rest of the code for my implementation of Computer B.
Notice that I''ve added the "print_machine_state" function, and that
the machine prints its state before executing each instruction.
This way we can keep track of what it''s doing with our code.
You should also note that I didn''t put any screen-output opcodes
in Computer B, so the "print_machine_state" output is the only
output we''re going to get. While Computer B can do some pretty
neat things by modifying its own programs during execution, it
can''t print "Hello world" anymore. (But wait a bit...)
#include <stdio.h>
#define NELEM(x) ((int)(sizeof (x) / sizeof *(x)))
void print_machine_state(struct ComputerB *p);

int main()
{
struct ComputerB b;
int t1, t2;
int i;
int running;

puts("Please input the initial values of R0, IP, and M0..M3,");
puts("in the following format: (3 0) (1 2 3 0) .");
printf("Input now. >");
fflush(stdout);
if (2 != scanf(" (%d%d ) (", &t1, &t2)) {
puts("Something fouled up -- please run the program again.");
return 0;
}
b.R0 = t1;
b.IP = t2;

for (i=0; i < NELEM(b.M); ++i) {
if (1 != scanf("%d", &t1)) {
puts("Something fouled up -- please run the program again.");
return 0;
}
b.M[i].value = t1;
}

for (running = 1; running; b.IP++) {
print_machine_state(&b);
switch (b.M[b.IP].value)
{
case OPCODE_HALT:
running = 0; /* halt program */
break;
case OPCODE_LOAD:
b.IP++;
b.R0 = b.M[b.M[b.IP].value].value;
break;
case OPCODE_INCR:
b.R0++;
break;
case OPCODE_STOR:
b.IP++;
b.M[b.M[b.IP].value].value = b.R0;
break;
}
}

puts("Program terminated.");

puts("The final state of the machine was:");
print_machine_state(&b);

return 0;
}

void print_machine_state(struct ComputerB *p)
{
int i;
printf(" IP: %d R0: %d\n", (int)p->IP, (int)p->R0);
for (i=0; i < NELEM(p->M); ++i)
printf(" M%d: %d", i, (int)p->M[i].value);
printf("\n");
}
Exercise 5: Repeat Exercise 3 with a subset of 20 or 30 states
from Computer B. (How many states does Computer B have in total?)
Draw arrows as before. What do you notice?
Answer: Computer B has 4096 (=4**6) states in total. That''s much
too many to draw at once. But even with a small subset of the states,
you should see that the diagram is more interesting and complex than
that of Computer A.

My version takes about 350 lines. And the 3-bit version is about 400
lines. It''s not difficult, believe me. And by the time you get up
to 8 bits, you /will/ understand pointers.




嗯。在我看来,理查德说实话,但只是因为你不会在没有理解指示的情况下将它变为8位 - 我不会看到这些计算机的C实现

真的使用了太多的指针(当它们这样做时,如在LOAD

和STOR指令中,我们有双重间接)。 br />
但是无论如何都要尝试。


练习6:设计并实现计算机C,一个3位版本的

计算机B.它将具有8个三位字节。内存,以及8个
操作码。添加一些操作码至少执行以下操作:打印

一条消息到屏幕。打印R0的值。 跳跃操作码

直接改变IP的价值。


HTH,如有问题请跟进comp.programming

与C实现没有直接关系,


-Arthur



Hmm. In my opinion, Richard speaks the truth, but only because
you won''t ever make it to eight bits without an understanding of
pointers -- I don''t see the C implementation of these computers
really using too many pointers (and when they do, as in the LOAD
and STOR instructions, we have double indirection).
But try anyway.

Exercise 6: Design and implement Computer C, a 3-bit version of
Computer B. It will have 8 three-bit "bytes" of memory, and eight
opcodes. Add some opcodes at least that do the following: Print
a message to the screen. Print the value of R0. A "jump" opcode
that directly changes the value of IP.

HTH, and please follow up to comp.programming with any questions
not directly related to the C implementations,

-Arthur


[Google的新闻编辑器并不是非常好。如果文本

流量被宣传,请道歉。


" Arthur J. O''Dwyer" < aj*@nospam.andrew.cmu.edu>在消息新闻中写道:< Pi *********************************** @ unix46 .andrew.cmu。 edu> ...
[Google''s news "editor" isn''t thrillingly good. Apologies if the text
flow is munged.]

"Arthur J. O''Dwyer" <aj*@nospam.andrew.cmu.edu> wrote in message news:<Pi***********************************@unix46 .andrew.cmu.edu>...
2003年11月23日,在太阳报上,Douglas Garstang写道:
On Sun, 23 Nov 2003, Douglas Garstang wrote:

几个星期后我在这里发布了一个新闻组问题,询问一些与我10年任务相关的问题(到目前为止),以了解
指针。

< snip>

I posted a newsgroup question here a few weeks back, asking some
questions that related to my 10 year quest (so far) to understand
pointers.
<snip>

基本上,理查德建议您创建一个假想的计算机。从头开始 - 你甚至可以在纸上做到这一点。


对。


< snip>

就个人而言,我认为这是学习指针的一种不好的方法虽然这对于了解计算机如何工作是一个很好的练习。见下文。


我必须在这里不同意你,Arthur。正是因为它是一个很好的练习来理解计算机是如何工作的,所以/

是一个学习指针的好方法。即使OP选择以一种不受欢迎的语言实现

解决方案(Visual Basic会让人想起一个可能的候选人),实现计算机的行为将是,

有效,引导他/发明/指针。我当然可以证明这样一个事实,即发明一些东西肯定会给你一个更多,更多的b $ b更清楚地理解它,而不仅仅是阅读它可以做到的事情。


< snip>

这可以在大约300行C代码中完成。如果你是一个简洁的编码器,那就少了,但我的是300行(不包括一些printfs和注释),
它甚至包括一个基本的反汇编程序。

Basically, Richard is suggesting that you create an "imaginary
computer" from scratch -- you can even do this on paper.
Right.

<snip>
Personally, I think this is a bad way to learn pointers, although
it''s a nice exercise for general understanding of how computers
work. See below.
I have to disagree with you here, Arthur. It is precisely because it''s
a nice exercise for understanding how computers work that it is /also/
a good way to learn pointers. Even if the OP chooses to implement the
solution in an unpointery language (Visual Basic springs to mind as a
possible candidate), the very act of implementing a computer will,
effectively, lead him to /invent/ pointers. And I can certainly attest
to the fact that inventing something definitely gives you a much, much
clearer understanding of it than merely reading about it can ever do.

<snip>
This can be done in around 300 lines of C code. Less if you''re a terse
coder, but mine is 300 lines (excluding a few printfs and comments),
and it even includes a rudimentary disassembler.




I must be a very terse coder, then. :-)




呃,是的。好吧,当写作C时,tersinosity从未成为我的目标之一。我倾向于将我的摊位放得相当冗长。


< snip>

嗯。在我看来,理查德讲的是真相,但这只是因为你不会在没有理解指针的情况下将它变为八位。


我冒险建议编写机器的过程非常可能产生这种理解。

- 我没看到这些计算机的C实现
真的使用太多指针


不,不是特别,但这不是重点。重点是

机器代码解释器。必须了解如何从

中读取并写入模拟的主存储器,并且正在设计

并编写该解释器以了解学生的理解dawns。


< snip>

HTH,请跟进comp.programming任何问题
与C实现没有直接关系,



Er, yes. Well, tersinosity has never been one of my objectives when
writing C. I tend to set my stall out rather verbosely.

<snip>
Hmm. In my opinion, Richard speaks the truth, but only because
you won''t ever make it to eight bits without an understanding of
pointers
I venture to suggest that the process of writing the machine is very
likely to produce that understanding.
-- I don''t see the C implementation of these computers
really using too many pointers
No, not particularly, but that''s not the point. The point is that the
"machine code interpreter" has to understand about how to read from
and write to the main memory of the simulation, and it is in designing
and writing that interpreter that the student''s understanding dawns.

<snip>
HTH, and please follow up to comp.programming with any questions
not directly related to the C implementations,




模数这些尼特,我把你的帽子给你一个极好的答案。我只希望OP不会/太/紧密地阅读它,因为整个想法

是他自己完成设计和开发过程。 br />
正是在这条道路上他会遇到谅解。


-

Richard Heathfield

(奇怪地放置)



Modulo those nits, I take my hat off to you for a superb answer. I
only hope the OP doesn''t read it /too/ closely, since the whole idea
is for him to go through the design and development process himself.
It is on that road that he will encounter understanding.

--
Richard Heathfield
(Strangely Placed)




2003年11月24日星期一,Strangely Placed [Richard Heathfield]写道:

On Mon, 24 Nov 2003, Strangely Placed [Richard Heathfield] wrote:

Arthur J. O''Dwyer写道......

Arthur J. O''Dwyer wrote...

基本上,理查德建议您创建一个虚构的计算机。从头开始 - 你甚至可以在纸上做到这一点。

Basically, Richard is suggesting that you create an "imaginary
computer" from scratch -- you can even do this on paper.
就我个人而言,我认为这是学习指针的一种不好的方法,虽然这对于如何理解计算机是一个很好的练习
工作。见下文。
我不得不在这里不同意你,Arthur。正是因为它是一个很好的练习,可以理解计算机是如何工作的,也是/
学习指针的好方法。即使OP选择以一种不受欢迎的语言实现
解决方案(Visual Basic可能会成为可能的候选者),实现计算机的行为将有效地引导他到/发明/指针。而且我当然可以证明这样一个事实,即发明一些东西肯定会让你对它有更多,更清楚的理解,而不仅仅是阅读它可以做到的。
Personally, I think this is a bad way to learn pointers, although
it''s a nice exercise for general understanding of how computers
work. See below.
I have to disagree with you here, Arthur. It is precisely because it''s
a nice exercise for understanding how computers work that it is /also/
a good way to learn pointers. Even if the OP chooses to implement the
solution in an unpointery language (Visual Basic springs to mind as a
possible candidate), the very act of implementing a computer will,
effectively, lead him to /invent/ pointers. And I can certainly attest
to the fact that inventing something definitely gives you a much, much
clearer understanding of it than merely reading about it can ever do.



True。但是,我会说当我编写计算机代码
B时,包含LOAD的代码。操作码,通过将地址操作数解析为LOAD,我暂时感到困惑

;在原来的

代码中看到它是如何双重间接的


b.R0 = bM [bM [b.IP] .value] .value;


其中机器代码建议单个间接


1 3负载[M3]


恕我直言,对于任何不是这样的人来说,这将会非常混乱已经

对他的指针知识充满信心,像我一样继续前进,

相信我*知道*是正确的C表达,即使它是

*看起来有点搞笑。

我绝对同意你的说法,铅笔和纸上练习

设计你自己的电脑是一个很好的方式学习指针

通过做。但我恭敬地提出,如果你[意思是新手]

尝试用C语言实现*这些计算机,那么你最终会比你开始时更加可怕...... 。无论多么有趣,

能够看到你想象中的程序在真机上执行。



True. However, I will say that when I wrote the code for Computer
B, the one that includes the "LOAD" opcode, I was momentarily confused
by the parsing of the address operand to "LOAD"; see in the original
code how it has double indirection

b.R0 = b.M[b.M[b.IP].value].value;

where the "machine code" suggests single indirection

1 3 LOAD [M3]

IMHO that''s going to be very confusing for anyone who''s not already
confident enough in his pointer knowledge to plough ahead as I did,
trusting what I *knew* to be the right C expression even though it
*looked* kind of funny.
I do absolutely agree with you that the pencil-and-paper exercise
of designing your own computers is a great way to learn pointers
"by doing." But I respectfully submit that if you [meaning newbies]
try to *implement* these computers in C, you''ll end up more horribly
confused than you started out. No matter how much more fun it is to
be able to see your imaginary programs executing on real machines.

这可以在大约300行C代码。如果你是一个简洁的编码器,那就少了,但我的是300行(不包括一些printfs和注释),
它甚至包括一个基本的反汇编程序。
This can be done in around 300 lines of C code. Less if you''re a terse
coder, but mine is 300 lines (excluding a few printfs and comments),
and it even includes a rudimentary disassembler.




I must be a very terse coder, then. :-)



呃,是的。好吧,当写C时,tersinosity从来就不是我的目标之一。我倾向于把我的摊位放得相当冗长。



Er, yes. Well, tersinosity has never been one of my objectives when
writing C. I tend to set my stall out rather verbosely.




*你的代码做了什么*看起来像?你还有它在任何地方吗?

也许你可以张贴一个URL。 (当你写的时候:

重点是机器代码解释器必须理解如何读取和写入模拟的主存储器,
正是在设计和编写解释器时,学生的理解才会明白。


它让我觉得你的代码可能会长得太长,因为你/>
没有为你的存储单元使用数组或位字段;但是如果那个'b
的情况下,我想看看你是如何*做的。病态迷恋,

你知道。;-)


模数那些尼特,我把你的帽子给你一个极好的答案。 [...]



What *did* your code look like? You still have it around anywhere?
Maybe you could post a URL. (And when you write:
The point is that the "machine code interpreter" has to understand
about how to read from and write to the main memory of the simulation,
and it is in designing and writing that interpreter that the student''s
understanding dawns.
it makes me think that your code might be horribly long because you
didn''t use arrays or bit-fields for your memory cells; but if that''s
the case, I want to see how you *did* do it. Morbid fascination,
y''know. ;-)

Modulo those nits, I take my hat off to you for a superb answer. [...]




非常感谢!


-Arthur



Thank you much!

-Arthur

这篇关于写一个模拟器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆