LMC 中的输出和重置列表
Output and Reset Lists in LMC
我正在应对这个编码挑战:
Write a program for the Little Man Computer that allows the user to manage a list of values. It should start with an empty list and then process input as follows:
If the input is:
- less than 100: add this value to a list, unless the list already has 10 values, in which case the value is ignored
- 995: make the list empty
- 996: output the number of values the list currently has
- 997: output each value the list currently has, in the order they were added to it
- 998: output each value the list currently has, in reversed order
- 999: end the program
- Any other value is ignored
The processing of input values continues as long as the input value is not 999.
输入 997 时,我在获取代码以正向顺序打印存储列表时遇到问题。我想我可能混淆了 ADD
和 SUB
指令。当输入 995 时,我也无法正确重置存储列表。
我能够正确编程的所有其他内容。
下面是我的代码:
START INP
STA TEMP
SUB NINES
BRZ end
LDA TEMP
SUB EIGHT
BRZ PRIT
lda temp
sub seven
brz printf
LDA TEMP
SUB SIX
BRZ DOOUT
LDA TEMP
SUB FIVE
BRZ RESET
LDA COUNT
SUB TEN
BRZ START
LDA TEMP
SUB HUND
BRP START
SIT LDA SINST
ADD COUNT
STA SLOC
LDA TEMP
SLOC DAT 0
LDA COUNT
ADD ONE
STA COUNT
BRA START
PRIT LDA COUNT
BRZ END
PRINTR LDA LINST
ADD COUNT
SUB ONE
STA LDIT
LDIT DAT 0
OUT
LDA COUNT
SUB ONE
STA COUNT
BRZ END
BRA PRINTR
---PRINTF LDA LINST
ADD COUNT
add ONE
STA LDIT
LDIT DAT 0
OUT
LDA COUNT
SUB ONE
STA COUNT
BRZ END
BRA PRINTF
doout lda count
out
bra start
reset lda zero
sta count
bra start
END HLT
TEMP DAT 0
COUNT DAT 0
ONE DAT 1
TWO DAT 2
TEN DAT 10
HUND DAT 100
SINST DAT 380
LINST DAT 580
five dat 995
six dat 996
seven dat 997
eight dat 998
NINES DAT 999
您程序中的问题可归类为:
- 一个好的模拟器在运行运行你的程序
之前应该检测到的与标签相关的问题
- 与程序逻辑相关的问题,使程序产生错误的输出
- 代码风格相关问题
1。标签
一个好的模拟器不应该接受以下错误:
PRINTF
标签未定义。
有一个 brz printf
指令,但该标签未定义,因为 ---
使其成为注释。显然,需要删除 ---
才能使标签引用有效。
LDIT
标签重复:
标签 LDIT 被定义了两次。这将有不可靠的行为。一个好的模拟器应该给出一个错误信息,但其他模拟器只会采用一个定义并忽略重复项。无论哪种方式,程序的意图是一个STA LDIT
使用第一个 LDIT位置,第二个STA LDIT
使用second LDIT 位置。如果有的话,这不会发生。因此重命名两个标签之一,并相应地调整 STA LDIT
指令之一。
zero
标签未定义:
重置计数器的代码引用了未定义的标签zero
。同样,好的模拟器在加载程序时会产生错误,但其他人可能会默默地使用邮箱 0,这会导致不良行为。所以定义zero
标签为
zero DAT 0
2。逻辑
打印列表的代码,无论是向前还是向后,都改变COUNT
的值,但是这样代码将丢失列表的 size 是什么!例如,如果在这样的遍历之后您选择操作 996——即查询列表的大小——它不会给出正确的结果。解决方案是使用 different 变量遍历列表,并保持 COUNT
不变。 COUNT
只应在您 添加 一个值到列表时,或者当您 重置 列表时更改。
打印代码以跳转到 END
结束,但似乎应该允许用户继续使用另一个“菜单”选项,而不是跳转到 [=29] =] 它应该跳转到 START
。跳转到 END
的唯一原因应该是因为用户输入了选项 999.
对于 forward 打印你不应该从添加 COUNT
到动态加载指令开始,因为你需要从 列表的第一个个元素,不是最后一个。所以在第一次打印之前不要执行 ADD COUNT
或 ADD ONE
。相反,增加动态加载指令直到它与原始加载指令的差异——即计算列表中的相对 offset——至少为 COUNT
.
LMC 规范有一个特别的奇怪之处:它没有定义当减法会导致负结果时累加器的值是多少。累加器不能存储负值,只能 flag 负结果。因此,当您刚刚执行了可能导致负值的 SUB
指令时,执行 BRZ
是不安全的(因为奇怪的是,模拟器 可能 当减法为负时,累加器中的值为零)。简而言之,如果可以,最好使用 BRP
而不是 BRZ
,或者在使用 BRZ
之前至少使用 BRP
。注意:即使是这方面的老师也不总是意识到这一点。
LMC 有一个重置“句柄”,它将程序计数器设置回程序的开始。当发生这种情况时,您将希望从头开始,并将 COUNT
重置为 0。因此,在程序的最顶部添加重置代码。
这将解决您程序中的语义问题。
3。代码风格
我建议您使用更有意义的名称。 doout
不是很有启发性,因为您的程序旨在输出不同的东西(按正向顺序列出,按倒序列出,列表的大小)。例如,使用 output_size
而不是 doout
。像 LDIT
、PRIT
、... 这样的名字非常神秘。当程序使用描述性名称而不是只有您才能理解的缩写时,它会更容易阅读和理解。
动态指令基于SINST
(存储指令)和LINST
(加载指令),硬编码为:
SINST DAT 380
LINST DAT 580
但遗憾的是你硬编码了它们。首先,他们假设邮箱 80 可用于存储列表,其次,它需要了解这些指令(3 和 5)的操作码。然而,如果有一个好的汇编程序,您就不必这样做。所以我建议改为这样做:
store_instruction STA list
load_instruction LDA list
...并在代码的最底部使用 DAT
指令定义 list
(因为那里有可用的邮箱)。我什至会在它后面添加虚拟 DAT
行,以便非常清楚该列表中的邮箱 - 它只是让人们更容易理解代码:
list DAT
DAT
DAT
DAT
DAT
DAT
DAT
DAT
DAT
DAT
这样,列表可能不会存储在80号邮箱,但我们不在乎。我们留给汇编程序为我们的列表分配下一个空闲邮箱。在你的“数据部分”中间有 STA
和 LDA
指令也可能看起来很奇怪,它们永远不会被执行,但 LMC 架构(冯诺依曼架构)的原理是代码和数据使用相同的内存,所以这很好。 LDA
和 STA
指令将从那里复制到实际程序中。
更正程序
考虑到以上所有因素,程序可能如下所示:
#input:1 2 3 997 998 999
clear_list LDA zero # Start by resetting the list
STA list_size
start INP
STA input
SUB menu_nine
BRP end
LDA input
SUB menu_eight
BRP output_reversed
LDA input
SUB menu_seven
BRP output_forward
LDA input
SUB menu_six
BRP output_size
LDA input
SUB menu_five
BRP clear_list
LDA list_size
SUB max_list_size
BRP start
LDA input
SUB input_limit
BRP start
LDA store_instruction
ADD list_size
STA store
LDA input
store DAT 0
LDA list_size
ADD one
STA list_size
BRA start
output_reversed LDA load_instruction
ADD list_size
loop_reversed SUB one
STA load_reversed
SUB load_instruction # are we still within the list?
BRP load_reversed # yes, continue printing
BRA start
load_reversed DAT 0
OUT
LDA load_reversed # decrement the dynamic LDA instruction
BRA loop_reversed
output_forward LDA load_instruction
loop_forward STA load_forward
SUB load_instruction # get relative offset in the list
SUB list_size # are we still within the list?
BRP start # no, stop printing
load_forward DAT 0
OUT
LDA load_forward # increment the dynamic LDA instruction
ADD one
BRA loop_forward
output_size LDA list_size
OUT
BRA start
end HLT
# constants
zero DAT 0
one DAT 1
two DAT 2
max_list_size DAT 10
input_limit DAT 100
load_instruction LDA list
store_instruction STA list
menu_five DAT 995
menu_six DAT 996
menu_seven DAT 997
menu_eight DAT 998
menu_nine DAT 999
# variables
input DAT 0
list_size DAT 0
list DAT
DAT
DAT
DAT
DAT
DAT
DAT
DAT
DAT
DAT
<script src="https://cdn.jsdelivr.net/gh/trincot/lmc@v0.816/lmc.js"></script>
您可以 运行 此处的代码,使用此代码段。点击运行 Code Snippet激活LMC模拟器,然后使用右侧的面板与之交互。
我正在应对这个编码挑战:
Write a program for the Little Man Computer that allows the user to manage a list of values. It should start with an empty list and then process input as follows:
If the input is:
- less than 100: add this value to a list, unless the list already has 10 values, in which case the value is ignored
- 995: make the list empty
- 996: output the number of values the list currently has
- 997: output each value the list currently has, in the order they were added to it
- 998: output each value the list currently has, in reversed order
- 999: end the program
- Any other value is ignored
The processing of input values continues as long as the input value is not 999.
输入 997 时,我在获取代码以正向顺序打印存储列表时遇到问题。我想我可能混淆了 ADD
和 SUB
指令。当输入 995 时,我也无法正确重置存储列表。
我能够正确编程的所有其他内容。
下面是我的代码:
START INP
STA TEMP
SUB NINES
BRZ end
LDA TEMP
SUB EIGHT
BRZ PRIT
lda temp
sub seven
brz printf
LDA TEMP
SUB SIX
BRZ DOOUT
LDA TEMP
SUB FIVE
BRZ RESET
LDA COUNT
SUB TEN
BRZ START
LDA TEMP
SUB HUND
BRP START
SIT LDA SINST
ADD COUNT
STA SLOC
LDA TEMP
SLOC DAT 0
LDA COUNT
ADD ONE
STA COUNT
BRA START
PRIT LDA COUNT
BRZ END
PRINTR LDA LINST
ADD COUNT
SUB ONE
STA LDIT
LDIT DAT 0
OUT
LDA COUNT
SUB ONE
STA COUNT
BRZ END
BRA PRINTR
---PRINTF LDA LINST
ADD COUNT
add ONE
STA LDIT
LDIT DAT 0
OUT
LDA COUNT
SUB ONE
STA COUNT
BRZ END
BRA PRINTF
doout lda count
out
bra start
reset lda zero
sta count
bra start
END HLT
TEMP DAT 0
COUNT DAT 0
ONE DAT 1
TWO DAT 2
TEN DAT 10
HUND DAT 100
SINST DAT 380
LINST DAT 580
five dat 995
six dat 996
seven dat 997
eight dat 998
NINES DAT 999
您程序中的问题可归类为:
- 一个好的模拟器在运行运行你的程序 之前应该检测到的与标签相关的问题
- 与程序逻辑相关的问题,使程序产生错误的输出
- 代码风格相关问题
1。标签
一个好的模拟器不应该接受以下错误:
PRINTF
标签未定义。有一个
brz printf
指令,但该标签未定义,因为---
使其成为注释。显然,需要删除---
才能使标签引用有效。LDIT
标签重复:标签 LDIT 被定义了两次。这将有不可靠的行为。一个好的模拟器应该给出一个错误信息,但其他模拟器只会采用一个定义并忽略重复项。无论哪种方式,程序的意图是一个
STA LDIT
使用第一个 LDIT位置,第二个STA LDIT
使用second LDIT 位置。如果有的话,这不会发生。因此重命名两个标签之一,并相应地调整STA LDIT
指令之一。zero
标签未定义:重置计数器的代码引用了未定义的标签
zero
。同样,好的模拟器在加载程序时会产生错误,但其他人可能会默默地使用邮箱 0,这会导致不良行为。所以定义zero
标签为zero DAT 0
2。逻辑
打印列表的代码,无论是向前还是向后,都改变
COUNT
的值,但是这样代码将丢失列表的 size 是什么!例如,如果在这样的遍历之后您选择操作 996——即查询列表的大小——它不会给出正确的结果。解决方案是使用 different 变量遍历列表,并保持COUNT
不变。COUNT
只应在您 添加 一个值到列表时,或者当您 重置 列表时更改。打印代码以跳转到
END
结束,但似乎应该允许用户继续使用另一个“菜单”选项,而不是跳转到 [=29] =] 它应该跳转到START
。跳转到END
的唯一原因应该是因为用户输入了选项 999.对于 forward 打印你不应该从添加
COUNT
到动态加载指令开始,因为你需要从 列表的第一个个元素,不是最后一个。所以在第一次打印之前不要执行ADD COUNT
或ADD ONE
。相反,增加动态加载指令直到它与原始加载指令的差异——即计算列表中的相对 offset——至少为COUNT
.LMC 规范有一个特别的奇怪之处:它没有定义当减法会导致负结果时累加器的值是多少。累加器不能存储负值,只能 flag 负结果。因此,当您刚刚执行了可能导致负值的
SUB
指令时,执行BRZ
是不安全的(因为奇怪的是,模拟器 可能 当减法为负时,累加器中的值为零)。简而言之,如果可以,最好使用BRP
而不是BRZ
,或者在使用BRZ
之前至少使用BRP
。注意:即使是这方面的老师也不总是意识到这一点。LMC 有一个重置“句柄”,它将程序计数器设置回程序的开始。当发生这种情况时,您将希望从头开始,并将
COUNT
重置为 0。因此,在程序的最顶部添加重置代码。
这将解决您程序中的语义问题。
3。代码风格
我建议您使用更有意义的名称。
doout
不是很有启发性,因为您的程序旨在输出不同的东西(按正向顺序列出,按倒序列出,列表的大小)。例如,使用output_size
而不是doout
。像LDIT
、PRIT
、... 这样的名字非常神秘。当程序使用描述性名称而不是只有您才能理解的缩写时,它会更容易阅读和理解。动态指令基于
SINST
(存储指令)和LINST
(加载指令),硬编码为:SINST DAT 380 LINST DAT 580
但遗憾的是你硬编码了它们。首先,他们假设邮箱 80 可用于存储列表,其次,它需要了解这些指令(3 和 5)的操作码。然而,如果有一个好的汇编程序,您就不必这样做。所以我建议改为这样做:
store_instruction STA list load_instruction LDA list
...并在代码的最底部使用
DAT
指令定义list
(因为那里有可用的邮箱)。我什至会在它后面添加虚拟DAT
行,以便非常清楚该列表中的邮箱 - 它只是让人们更容易理解代码:list DAT DAT DAT DAT DAT DAT DAT DAT DAT DAT
这样,列表可能不会存储在80号邮箱,但我们不在乎。我们留给汇编程序为我们的列表分配下一个空闲邮箱。在你的“数据部分”中间有
STA
和LDA
指令也可能看起来很奇怪,它们永远不会被执行,但 LMC 架构(冯诺依曼架构)的原理是代码和数据使用相同的内存,所以这很好。LDA
和STA
指令将从那里复制到实际程序中。
更正程序
考虑到以上所有因素,程序可能如下所示:
#input:1 2 3 997 998 999
clear_list LDA zero # Start by resetting the list
STA list_size
start INP
STA input
SUB menu_nine
BRP end
LDA input
SUB menu_eight
BRP output_reversed
LDA input
SUB menu_seven
BRP output_forward
LDA input
SUB menu_six
BRP output_size
LDA input
SUB menu_five
BRP clear_list
LDA list_size
SUB max_list_size
BRP start
LDA input
SUB input_limit
BRP start
LDA store_instruction
ADD list_size
STA store
LDA input
store DAT 0
LDA list_size
ADD one
STA list_size
BRA start
output_reversed LDA load_instruction
ADD list_size
loop_reversed SUB one
STA load_reversed
SUB load_instruction # are we still within the list?
BRP load_reversed # yes, continue printing
BRA start
load_reversed DAT 0
OUT
LDA load_reversed # decrement the dynamic LDA instruction
BRA loop_reversed
output_forward LDA load_instruction
loop_forward STA load_forward
SUB load_instruction # get relative offset in the list
SUB list_size # are we still within the list?
BRP start # no, stop printing
load_forward DAT 0
OUT
LDA load_forward # increment the dynamic LDA instruction
ADD one
BRA loop_forward
output_size LDA list_size
OUT
BRA start
end HLT
# constants
zero DAT 0
one DAT 1
two DAT 2
max_list_size DAT 10
input_limit DAT 100
load_instruction LDA list
store_instruction STA list
menu_five DAT 995
menu_six DAT 996
menu_seven DAT 997
menu_eight DAT 998
menu_nine DAT 999
# variables
input DAT 0
list_size DAT 0
list DAT
DAT
DAT
DAT
DAT
DAT
DAT
DAT
DAT
DAT
<script src="https://cdn.jsdelivr.net/gh/trincot/lmc@v0.816/lmc.js"></script>
您可以 运行 此处的代码,使用此代码段。点击运行 Code Snippet激活LMC模拟器,然后使用右侧的面板与之交互。