sim: sim_core: add instruction fetch, effective address calculation, and code to start a freshly loaded program

This commit is contained in:
Mikael Pettersson 2020-07-05 16:01:42 +02:00
parent 5e138be489
commit 8631bc5c2a

View File

@ -1,7 +1,7 @@
%%% -*- erlang-indent-level: 2 -*-
%%%
%%% simulator for pdp10-elf
%%% Copyright (C) 2020 Mikael Pettersson
%%% Copyright (C) 2018-2020 Mikael Pettersson
%%%
%%% This file is part of pdp10-tools.
%%%
@ -38,12 +38,241 @@
Argc :: word(),
Argv :: word(),
Envp :: word()) -> ok | {error, {module(), term()}}.
run(_Mem, _PC, _SP, _Argc, _Argv, _Envp) -> ok. % FIXME
run(Mem, PC, SP, Argc, Argv, Envp) ->
PCSection = (PC bsr 18) band ((1 bsl 12) - 1),
PCOffset = PC band ((1 bsl 18) - 1),
ACS0 = list_to_tuple(lists:duplicate(16, 0)),
ACS1 = do_set_ac(ACS0, 1, Argc),
ACS2 = do_set_ac(ACS1, 2, Argv),
ACS3 = do_set_ac(ACS2, 3, Envp),
ACS = do_set_ac(ACS3, ?AC_SP, SP),
Flags = (1 bsl ?PDP10_PF_USER),
Core = #core{ pc_section = PCSection
, pc_offset = PCOffset
, acs = ACS
, flags = Flags
},
{_Core, _Mem, Result} = run(Core, Mem),
Result.
%% The instruction fetch, effective address calculation, and dispatch process,
%% is a tail-recursive state machine, in order to support correct handling of
%% faults (especially page faults) that may occur at various points, without
%% creating recursion in the simulator.
-spec run(#core{}, sim_mem:mem()) -> {#core{}, sim_mem:mem(), ok | {error, {module(), term()}}}.
run(Core, Mem) ->
insn_fetch(Core, Mem).
%% Instruction Fetch and Effective Address Calculation =========================
%% c.f. Toad-1 Architecture Manual, page 41, Figure 1.11
%% Instruction Fetch. This always uses local addressing.
insn_fetch(Core, Mem) ->
PCOffset = Core#core.pc_offset,
case PCOffset =< 8#17 of
true ->
MB = get_ac(Core, PCOffset),
insn_fetch2(Core, Mem, MB);
false ->
Address = (Core#core.pc_section bsl 18) bor PCOffset,
case sim_mem:read_word(Mem, Address) of
{ok, MB} -> insn_fetch2(Core, Mem, MB);
{error, Reason} -> page_fault(Core, Mem, Address, read, Reason, fun insn_fetch/2)
end
end.
insn_fetch2(Core, Mem, MB) ->
IR = bits36(MB, 0, 12), % IR := MB_<0:12>
ESection = Core#core.pc_section, % E_<6:17> := PC_<6:17>
calculate_ea(Core, Mem, IR, MB, ESection).
calculate_ea(Core, Mem, IR, MB, ESection) ->
local_format_address_word(Core, Mem, IR, MB, ESection).
local_format_address_word(Core, Mem, IR, MB, ESection) ->
Y = bits36(MB, 18, 35), % Y_<18:35> := MB_<18:35>
X = bits36(MB, 14, 17), % X := MB_<14:17>
I = bit36(MB, 13), % I := MB_<13>
%% Indexed Address? Test X field.
case X of
0 ->
%% X field =:= 0. No Indexing.
EOffset = Y, % E_<18:35> := Y_<18:35>
indirect_addressing(Core, Mem, IR, I, ESection, EOffset, _IsLocal = true);
_ ->
%% X field =/= 0. Indexing.
CX = get_ac(Core, X), % C(X) hoisted from following blocks
%% Test Section Number in E_<6:17>.
case ESection of
0 ->
local_index(Core, Mem, IR, I, ESection, CX, Y);
_ ->
%% Section =/= 0.
%% Test C(X). Global Index when (C(X)_<0> =:= 0) and (C(X)_<6:17> =/= 0).
case bit36(CX, 0) =:= 0 andalso bits36(CX, 6, 17) =/= 0 of
true ->
global_index(Core, Mem, IR, I, CX, Y);
false ->
local_index(Core, Mem, IR, I, ESection, CX, Y)
end
end
end.
local_index(Core, Mem, IR, I, ESection, CX, Y) ->
EOffset = bits36low18(CX + Y), % E_<18:35> := C(X)_<18:35> + Y_<18:35>
indirect_addressing(Core, Mem, IR, I, ESection, EOffset, _IsLocal = true).
global_index(Core, Mem, IR, I, CX, Y) ->
Y1 = sext18(Y), % Y_<6:17> =:= 7777 * Y_<18>
E = CX + Y1, % E_<6:35> := C(X)_<6:35> + Y_<6:35>
ESection = bits36(E, 6, 17),
EOffset = bits36(E, 18, 35),
indirect_addressing(Core, Mem, IR, I, ESection, EOffset, _IsLocal = false).
indirect_addressing(Core, Mem, IR, I, ESection, EOffset, IsLocal) ->
%% Test I bit.
case I of
0 ->
%% I =:= 0. Done!
%% E is the Effective Address.
%% IsLocal signals if E is a global address or if it is a local address
%% in the last section from which an address word was fetched. This
%% matters for instructions that use E and E+1, and also to determine
%% if an address denotes an accumulator or a memory location.
dispatch(Core, Mem, IR, #ea{section = ESection, offset = EOffset, islocal = IsLocal});
_ ->
%% I =:= 1.
fetch_indirect_word(Core, Mem, IR, ESection, EOffset, IsLocal)
end.
fetch_indirect_word(Core, Mem, IR, ESection, EOffset, IsLocal) ->
%% Fetch the Indirect Word.
case c(Core, Mem, ESection, EOffset, IsLocal) of
{ok, MB} ->
%% Non-zero section? Test E_<6:17>.
case ESection of
0 ->
local_format_address_word(Core, Mem, IR, MB, ESection);
_ ->
%% Section =/= 0.
%% Decode Indirect Word MB_<0:1>.
case bits36(MB, 0, 1) of
0 ->
global_indirect_word(Core, Mem, IR, MB, _I = 0);
1 ->
global_indirect_word(Core, Mem, IR, MB, _I = 1);
2 ->
%% Local Indirect
local_format_address_word(Core, Mem, IR, MB, ESection);
3 ->
E = (ESection bsl 18) bor EOffset,
page_fault(Core, Mem, E, read, indirect_word,
fun(Core1, Mem1) ->
fetch_indirect_word(Core1, Mem1, IR, ESection, EOffset, IsLocal)
end)
end
end;
{error, Reason} ->
E = (ESection bsl 18) bor EOffset,
page_fault(Core, Mem, E, read, Reason,
fun(Core1, Mem1) ->
fetch_indirect_word(Core1, Mem1, IR, ESection, EOffset, IsLocal)
end)
end.
global_indirect_word(Core, Mem, IR, MB, I) ->
Y = bits36(MB, 6, 35), % Y:= MB_<6,35>
X = bits36(MB, 2, 5), % X := MB_<2:5>
%% Indexed Address? Test X field.
case X of
0 ->
E = Y, % E_<6:35> := Y_<6:35>
ESection1 = bits36(E, 6, 17),
EOffset1 = bits36(E, 18, 35),
indirect_addressing(Core, Mem, IR, I, ESection1, EOffset1, _IsLocal = false);
_ ->
%% X =/= 0.
CX = get_ac(Core, X),
E = CX + Y, % E_<6:35> := C(X)_<6:35> + Y_<6:35>
ESection1 = bits36(E, 6, 17),
EOffset1 = bits36(E, 18, 35),
indirect_addressing(Core, Mem, IR, I, ESection1, EOffset1, _IsLocal = false)
end.
%% Instruction Dispatch ========================================================
-spec dispatch(#core{}, sim_mem:mem(), IR :: word(), #ea{})
-> {#core{}, sim_mem:mem(), ok | {error, {module(), term()}}}.
dispatch(Core, Mem, IR, EA) ->
PC = (Core#core.pc_section bsl 18) bor Core#core.pc_offset,
{Core, Mem, {error, {?MODULE, {dispatch, PC, IR, EA}}}}. % FIXME
%% Page Fault Handling =========================================================
-spec page_fault(#core{}, sim_mem:mem(), word(), atom(), term(), fun())
-> {#core{}, sim_mem:mem(), ok | {error, {module(), term()}}}.
page_fault(Core, Mem, Address, Op, Reason, Cont) ->
%% This should trap to supervisor mode, but for now we treat all faults as fatal.
PC = (Core#core.pc_section bsl 18) bor Core#core.pc_offset,
{Core, Mem, {error, {?MODULE, {page_fault, Address, PC, Op, Reason, Cont}}}}.
%% Miscellaneous ===============================================================
%% bits36(X, LEFT, RIGHT)
%%
%% Extracts bits LEFT through RIGHT (inclusive) from a 36-bit number X.
%% Bit 0 is most significant, and 35 least significant.
%% Requires 0 =< LEFT =< RIGHT =< 35.
-spec bits36(word(), 0..35, 0..35) -> word().
bits36(X, LEFT, RIGHT) ->
true = 0 =< LEFT andalso LEFT =< RIGHT andalso RIGHT =< 35,
(X bsr (35 - RIGHT)) band ((1 bsl (1 + RIGHT - LEFT)) - 1).
%% bits36low18(X)
%%
%% Extract the right 18 bits from a 36-bit number X.
-spec bits36low18(word()) -> word().
bits36low18(X) -> bits36(X, 18, 35).
%% bit36(X, BIT)
%%
%% Extract bit BIT from a 36-bit number X.
%% Bit 0 is most significant, and 35 least significant.
%% Requires 0 =< BIT =< 35.
-spec bit36(word(), 0..35) -> word().
bit36(X, BIT) -> bits36(X, BIT, BIT).
%% Sign-extend a uint18_t() to the full width of its representation type.
-spec sext18(uint18_t()) -> integer().
sext18(X) ->
UInt18Sbit = 1 bsl (18 - 1),
UInt18Max = (1 bsl 18) - 1,
((X band UInt18Max) bxor UInt18Sbit) - UInt18Sbit.
-spec c(#core{}, sim_mem:mem(), Section :: uint12_t(), Offset :: uint18_t(), IsLocal :: boolean())
-> {ok, word()} | {error, {sim_mem:prot(), sim_mem:what()} | false}.
c(Core, Mem, Section, Offset, IsLocal) ->
case Offset =< 8#17 andalso (IsLocal orelse Section =< 1) of
true -> {ok, get_ac(Core, Offset)};
false ->
Address = (Section bsl 18) bor Offset,
sim_mem:read_word(Mem, Address)
end.
-spec get_ac(#core{}, 0..8#17) -> word().
get_ac(Core, Nr) -> element(Nr + 1, Core#core.acs).
do_set_ac(ACS, Nr, Val) -> setelement(Nr + 1, ACS, Val).
%% Error Formatting ============================================================
-spec format_error(term()) -> io_lib:chars().
format_error(Reason) ->
case Reason of
_ -> [] % FIXME
{dispatch, PC, IR, EA} ->
io_lib:format("DISPATCH: PC=~.8b IR=~.8b EA=~p NYI", [PC, IR, EA]);
{page_fault, Address, PC, Op, Reason, Cont} ->
io_lib:format("PAGE FAULT: ADDR=~.8b PC=~.8b OP=~p RSN=~p CONT=~p",
[Address, PC, Op, Reason, Cont])
end.