mirror of
https://github.com/mikpe/pdp10-tools.git
synced 2026-01-24 19:32:52 +00:00
as: rewrite in Erlang, initial version
This commit is contained in:
parent
c184c9b4a8
commit
ce0595ef44
@ -21,7 +21,7 @@ REBAR3=$(shell type -p rebar3 || echo ./rebar3)
|
||||
REBAR3_GIT=https://github.com/erlang/rebar3.git
|
||||
REBAR3_VSN=3.7.5
|
||||
|
||||
PROGRAMS=8to9 ar nm od
|
||||
PROGRAMS=8to9 ar as nm od
|
||||
|
||||
default: compile link
|
||||
|
||||
|
||||
31
erlang/apps/as/priv/test1.s
Normal file
31
erlang/apps/as/priv/test1.s
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* test1.s
|
||||
* Copyright (C) 2013-2019 Mikael Pettersson
|
||||
*
|
||||
* This file is part of pdp10-tools.
|
||||
*
|
||||
* pdp10-tools is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* pdp10-tools is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with pdp10-tools. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* int foo(void) { return 27; }
|
||||
*/
|
||||
.file "test1.c"
|
||||
.text
|
||||
.globl foo
|
||||
.type foo,@function
|
||||
foo:
|
||||
movei 1,033
|
||||
popj 017,
|
||||
.size foo,.-foo
|
||||
.ident "GCC: (GNU) 4.3.0.- for XKL-2 (XKL LLC, Kirkland, WA, USA) Built 2013-08-15 23:03 +0200 on porter by mikpe"
|
||||
25
erlang/apps/as/src/as.app.src
Normal file
25
erlang/apps/as/src/as.app.src
Normal file
@ -0,0 +1,25 @@
|
||||
%%% Copyright (C) 2019 Mikael Pettersson
|
||||
%%%
|
||||
%%% This file is part of pdp10-tools.
|
||||
%%%
|
||||
%%% pdp10-tools is free software: you can redistribute it and/or modify
|
||||
%%% it under the terms of the GNU General Public License as published by
|
||||
%%% the Free Software Foundation, either version 3 of the License, or
|
||||
%%% (at your option) any later version.
|
||||
%%%
|
||||
%%% pdp10-tools is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
%%% GNU General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License
|
||||
%%% along with pdp10-tools. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
{application, as,
|
||||
[{description, "'as' clone for pdp10-elf"},
|
||||
{vsn, "0.1.0"},
|
||||
{registered, []},
|
||||
{applications, [kernel, stdlib, lib]},
|
||||
{env, []},
|
||||
{modules, []}
|
||||
]}.
|
||||
75
erlang/apps/as/src/as.erl
Normal file
75
erlang/apps/as/src/as.erl
Normal file
@ -0,0 +1,75 @@
|
||||
%%% -*- erlang-indent-level: 2 -*-
|
||||
%%%
|
||||
%%% 'as' clone for pdp10-elf
|
||||
%%% Copyright (C) 2013-2019 Mikael Pettersson
|
||||
%%%
|
||||
%%% This file is part of pdp10-tools.
|
||||
%%%
|
||||
%%% pdp10-tools is free software: you can redistribute it and/or modify
|
||||
%%% it under the terms of the GNU General Public License as published by
|
||||
%%% the Free Software Foundation, either version 3 of the License, or
|
||||
%%% (at your option) any later version.
|
||||
%%%
|
||||
%%% pdp10-tools is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
%%% GNU General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License
|
||||
%%% along with pdp10-tools. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
-module(as).
|
||||
-export([main/1]).
|
||||
|
||||
%% Command-line interface ======================================================
|
||||
|
||||
main(Argv) ->
|
||||
escript_runtime:start(fun main_/1, Argv).
|
||||
|
||||
main_(Argv) ->
|
||||
case getopt:parse(Argv, "vo:",
|
||||
[ {"version", no, version}
|
||||
]) of
|
||||
{ok, {Options, Files}} ->
|
||||
OutFile = scan_options(Options),
|
||||
case as(Files, OutFile) of
|
||||
ok -> halt(0);
|
||||
{error, _Reason} = Error -> escript_runtime:fatal("~p\n", [Error])
|
||||
end;
|
||||
{error, ErrMsg} ->
|
||||
escript_runtime:errmsg("~s\n", [ErrMsg]),
|
||||
usage()
|
||||
end.
|
||||
|
||||
scan_options(Options) ->
|
||||
lists:foldl(fun scan_option/2, "a.out", Options).
|
||||
|
||||
scan_option($v, OutFile) ->
|
||||
version(),
|
||||
OutFile;
|
||||
scan_option(version, _OutFile) ->
|
||||
version(),
|
||||
halt(0);
|
||||
scan_option({$o, OutFile}, _OutFile) ->
|
||||
OutFile.
|
||||
|
||||
usage() ->
|
||||
escript_runtime:fmterr(
|
||||
"Usage: ~s [-v] [-o objfile] [files..]\n",
|
||||
[escript_runtime:progname()]),
|
||||
halt(1).
|
||||
|
||||
version() ->
|
||||
io:format(standard_io, "pdp10-tools as version 0.2\n", []).
|
||||
|
||||
%% As ==========================================================================
|
||||
|
||||
as(Files, OutFile) ->
|
||||
case input:files(Files) of
|
||||
{ok, Tunit0} ->
|
||||
case assemble:tunit(Tunit0) of
|
||||
{ok, Tunit} -> output:tunit(Tunit, OutFile);
|
||||
{error, _Reason} = Error -> Error
|
||||
end;
|
||||
{error, _Reason} = Error -> Error
|
||||
end.
|
||||
102
erlang/apps/as/src/assemble.erl
Normal file
102
erlang/apps/as/src/assemble.erl
Normal file
@ -0,0 +1,102 @@
|
||||
%%% -*- erlang-indent-level: 2 -*-
|
||||
%%%
|
||||
%%% sections assembler for pdp10-elf as
|
||||
%%% Copyright (C) 2013-2019 Mikael Pettersson
|
||||
%%%
|
||||
%%% This file is part of pdp10-tools.
|
||||
%%%
|
||||
%%% pdp10-tools is free software: you can redistribute it and/or modify
|
||||
%%% it under the terms of the GNU General Public License as published by
|
||||
%%% the Free Software Foundation, either version 3 of the License, or
|
||||
%%% (at your option) any later version.
|
||||
%%%
|
||||
%%% pdp10-tools is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
%%% GNU General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License
|
||||
%%% along with pdp10-tools. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
-module(assemble).
|
||||
|
||||
-export([ tunit/1
|
||||
]).
|
||||
|
||||
-include("tunit.hrl").
|
||||
-include_lib("lib/include/pdp10_elf36.hrl").
|
||||
|
||||
tunit(Tunit) ->
|
||||
sections(maps:values(Tunit#tunit.sections), Tunit).
|
||||
|
||||
sections([], Tunit) -> {ok, Tunit};
|
||||
sections([Section | Sections], Tunit) ->
|
||||
case section(Section, Tunit) of
|
||||
{ok, NewTunit} -> sections(Sections, NewTunit);
|
||||
{error, _Reason} = Error -> Error
|
||||
end.
|
||||
|
||||
section(Section, Tunit) ->
|
||||
case Section of
|
||||
#section{ name = ".text" ++ _
|
||||
, sh_type = ?SHT_PROGBITS
|
||||
, sh_flags = ?SHF_ALLOC bor ?SHF_EXECINSTR
|
||||
} -> text(Section, Tunit);
|
||||
#section{ name = ".comment"
|
||||
, sh_type = ?SHT_PROGBITS
|
||||
, sh_flags = ?SHF_MERGE bor ?SHF_STRINGS
|
||||
} -> comment(Section, Tunit);
|
||||
#section{ name = Name } ->
|
||||
{error, io_lib:format("don't know how to assemble section ~s", [Name])}
|
||||
end.
|
||||
|
||||
%% Assemble .comment -----------------------------------------------------------
|
||||
%%
|
||||
%% The image starts with a NUL, followed by the strings, all NUL-terminated.
|
||||
|
||||
comment(Section = #section{data = {stmts, Stmts}}, Tunit) ->
|
||||
Image = comment_image(Stmts),
|
||||
NewSection = Section#section{data = {image, Image}, dot = image_size(Image)},
|
||||
{ok, tunit:put_section(Tunit, NewSection)}.
|
||||
|
||||
comment_image(Stmts) -> comment_image(Stmts, []).
|
||||
|
||||
comment_image([], []) -> [];
|
||||
comment_image([], Acc) -> lists:reverse([0 | Acc]);
|
||||
comment_image([#s_dot_ident{string = String} | Stmts], Acc) ->
|
||||
comment_image(Stmts, [[0 | String] | Acc]).
|
||||
|
||||
%% FIXME: duplicated
|
||||
image_size(Image) -> image_size(Image, 0).
|
||||
|
||||
image_size([H | T], Acc) -> image_size(T, image_size(H, Acc));
|
||||
image_size([], Acc) -> Acc;
|
||||
image_size(TByte, Acc) when is_integer(TByte), 0 =< TByte, TByte =< 511 -> Acc + 1.
|
||||
|
||||
%% Assemble .text --------------------------------------------------------------
|
||||
|
||||
text(Section = #section{data = {stmts, Stmts}}, Tunit) ->
|
||||
Image = text_image(Stmts),
|
||||
{ok, tunit:put_section(Tunit, Section#section{data = {image, Image}})}.
|
||||
|
||||
text_image(Stmts) -> text_image(Stmts, []).
|
||||
|
||||
text_image([], Acc) -> Acc; % the input Stmts were in reverse order
|
||||
text_image([Stmt | Stmts], Acc) ->
|
||||
text_image(Stmts, [insn_image(Stmt) | Acc]).
|
||||
|
||||
insn_image(Insn) ->
|
||||
#s_insn{ high13 = High13
|
||||
, at = At
|
||||
, address = Address
|
||||
, index = Index
|
||||
} = Insn,
|
||||
Word = (((High13 band ((1 bsl 13) - 1)) bsl (36 - 13)) bor
|
||||
((case At of true -> 1; false -> 0 end) bsl (36 - 14)) bor
|
||||
((Index band ((1 bsl 4) - 1)) bsl (36 - 18)) bor
|
||||
(Address band ((1 bsl 18) - 1))),
|
||||
%% big-endian conversion
|
||||
[(Word bsr 27) band 511,
|
||||
(Word bsr 18) band 511,
|
||||
(Word bsr 9) band 511,
|
||||
Word band 511].
|
||||
243
erlang/apps/as/src/input.erl
Normal file
243
erlang/apps/as/src/input.erl
Normal file
@ -0,0 +1,243 @@
|
||||
%%% -*- erlang-indent-level: 2 -*-
|
||||
%%%
|
||||
%%% input processing phase for pdp10-elf as
|
||||
%%% Copyright (C) 2013-2019 Mikael Pettersson
|
||||
%%%
|
||||
%%% This file is part of pdp10-tools.
|
||||
%%%
|
||||
%%% pdp10-tools is free software: you can redistribute it and/or modify
|
||||
%%% it under the terms of the GNU General Public License as published by
|
||||
%%% the Free Software Foundation, either version 3 of the License, or
|
||||
%%% (at your option) any later version.
|
||||
%%%
|
||||
%%% pdp10-tools is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
%%% GNU General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License
|
||||
%%% along with pdp10-tools. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
-module(input).
|
||||
|
||||
-export([ files/1
|
||||
]).
|
||||
|
||||
-include("tunit.hrl").
|
||||
-include_lib("lib/include/pdp10_elf36.hrl").
|
||||
|
||||
files(Files) ->
|
||||
NewFiles =
|
||||
case Files of
|
||||
[] -> ["--"];
|
||||
_ -> Files
|
||||
end,
|
||||
files(NewFiles, tunit_init()).
|
||||
|
||||
files([], Tunit) -> {ok, Tunit};
|
||||
files([File | Files], Tunit) ->
|
||||
case file(File, Tunit) of
|
||||
{ok, NewTunit} -> files(Files, NewTunit);
|
||||
{error, _Reason} = Error -> Error
|
||||
end.
|
||||
|
||||
file(File, Tunit) ->
|
||||
case scan_state_open(File) of
|
||||
{ok, ScanState} ->
|
||||
try process(ScanState, Tunit)
|
||||
after scan_state:fclose(ScanState)
|
||||
end;
|
||||
{error, _Reason} = Error -> Error
|
||||
end.
|
||||
|
||||
%% Open next input file, support "--" and "-" as aliases for stdin.
|
||||
scan_state_open(File) ->
|
||||
case File of
|
||||
"--" -> scan_state:stdin();
|
||||
"-" -> scan_state:stdin();
|
||||
_ -> scan_state:fopen(File)
|
||||
end.
|
||||
|
||||
process(ScanState, Tunit) ->
|
||||
case parse:stmt(ScanState) of
|
||||
eof -> {ok, Tunit};
|
||||
{ok, Stmt} ->
|
||||
case interpret(ScanState, Tunit, Stmt) of
|
||||
{ok, NewTunit} -> process(ScanState, NewTunit);
|
||||
{error, _Reason} = Error -> Error
|
||||
end;
|
||||
{error, _Reason} = Error -> Error
|
||||
end.
|
||||
|
||||
interpret(ScanState, Tunit, Stmt) ->
|
||||
case Stmt of
|
||||
#s_dot_file{} -> dot_file(ScanState, Tunit, Stmt);
|
||||
#s_dot_globl{} -> dot_globl(ScanState, Tunit, Stmt);
|
||||
#s_dot_ident{} -> dot_ident(ScanState, Tunit, Stmt);
|
||||
#s_dot_size{} -> dot_size(ScanState, Tunit, Stmt);
|
||||
#s_dot_text{} -> dot_text(ScanState, Tunit, Stmt);
|
||||
#s_dot_type{} -> dot_type(ScanState, Tunit, Stmt);
|
||||
#s_label{} -> label(ScanState, Tunit, Stmt);
|
||||
#s_insn{} -> insn(ScanState, Tunit, Stmt)
|
||||
end.
|
||||
|
||||
dot_file(_ScanState, Tunit, #s_dot_file{string = String}) ->
|
||||
Symbol = #symbol{ name = String
|
||||
, section = false
|
||||
, st_value = 0
|
||||
, st_size = 0
|
||||
, st_info = ?ELF_ST_INFO(?STB_LOCAL, ?STT_FILE)
|
||||
, st_name = 0
|
||||
, st_shndx = 0
|
||||
},
|
||||
{ok, tunit:put_symbol(Tunit, Symbol)}.
|
||||
|
||||
dot_globl(ScanState, Tunit, #s_dot_globl{name = Name}) ->
|
||||
case tunit:get_symbol(Tunit, Name) of
|
||||
false ->
|
||||
Symbol =
|
||||
#symbol{ name = Name
|
||||
, section = false
|
||||
, st_value = false
|
||||
, st_size = false
|
||||
, st_info = ?ELF_ST_INFO(?STB_GLOBAL, ?STT_NOTYPE)
|
||||
, st_name = 0
|
||||
, st_shndx = 0
|
||||
},
|
||||
{ok, tunit:put_symbol(Tunit, Symbol)};
|
||||
#symbol{st_info = StInfo} = OldSymbol ->
|
||||
case ?ELF_ST_BIND(StInfo) of
|
||||
?STB_GLOBAL -> {ok, Tunit};
|
||||
?STB_LOCAL -> % FIXME: assumed local-by-default, are there hard-local symbols?
|
||||
Symbol = OldSymbol#symbol{st_info = ?ELF_ST_INFO(?STB_GLOBAL, ?ELF_ST_TYPE(StInfo))},
|
||||
{ok, tunit:put_symbol(Tunit, Symbol)};
|
||||
Bind ->
|
||||
fmterr(ScanState, "symbol ~s has previous incompatible binding type ~p", [Name, Bind])
|
||||
end
|
||||
end.
|
||||
|
||||
dot_ident(_ScanState, Tunit, #s_dot_ident{} = Stmt) ->
|
||||
#section{data = {stmts, Stmts}} = OldSection =
|
||||
case tunit:get_section(Tunit, ".comment") of
|
||||
false -> section_dot_comment();
|
||||
Section -> Section
|
||||
end,
|
||||
NewSection = OldSection#section{data = {stmts, [Stmt | Stmts]}},
|
||||
{ok, tunit:put_section(Tunit, NewSection)}.
|
||||
|
||||
dot_size(ScanState, Tunit, #s_dot_size{name = Name}) ->
|
||||
#tunit{cursect = Cursect} = Tunit,
|
||||
#section{dot = Dot} = tunit:get_section(Tunit, Cursect),
|
||||
case tunit:get_symbol(Tunit, Name) of
|
||||
#symbol{st_size = StSize} when StSize =/= false ->
|
||||
fmterr(ScanState, "size of symbol ~s already defined", [Name]);
|
||||
#symbol{section = Section} when Section =/= Cursect ->
|
||||
fmterr(ScanState, "symbol ~s not defined in same section as dot", [Name]);
|
||||
#symbol{st_value = StValue} = OldSymbol when StValue =< Dot -> % note: false > integer()
|
||||
Symbol = OldSymbol#symbol{st_size = Dot - StValue},
|
||||
{ok, tunit:put_symbol(Tunit, Symbol)};
|
||||
#symbol{st_value = StValue} when StValue =/= false, StValue > Dot ->
|
||||
fmterr(ScanState, "cannot make symbol ~s negative size", [Name]);
|
||||
_ ->
|
||||
fmterr(ScanState, "symbol ~s not defined", [Name])
|
||||
end.
|
||||
|
||||
dot_text(_ScanState, Tunit, #s_dot_text{}) ->
|
||||
%% just check that .text has been pre-created
|
||||
#section{} = tunit:get_section(Tunit, ".text"),
|
||||
{ok, Tunit#tunit{cursect = ".text"}}.
|
||||
|
||||
dot_type(ScanState, Tunit, #s_dot_type{name = Name}) ->
|
||||
case tunit:get_symbol(Tunit, Name) of
|
||||
false ->
|
||||
Symbol =
|
||||
#symbol{ name = Name
|
||||
, section = false
|
||||
, st_value = false
|
||||
, st_size = false
|
||||
, st_info = ?ELF_ST_INFO(?STB_LOCAL, ?STT_FUNC)
|
||||
, st_name = 0
|
||||
, st_shndx = 0
|
||||
},
|
||||
{ok, tunit:put_symbol(Tunit, Symbol)};
|
||||
#symbol{st_info = StInfo} = OldSymbol ->
|
||||
case ?ELF_ST_TYPE(StInfo) of
|
||||
?STT_FUNC -> {ok, Tunit};
|
||||
?STT_NOTYPE ->
|
||||
Symbol = OldSymbol#symbol{st_info = ?ELF_ST_INFO(?ELF_ST_BIND(StInfo), ?STT_FUNC)},
|
||||
{ok, tunit:put_symbol(Tunit, Symbol)};
|
||||
Type ->
|
||||
fmterr(ScanState, "symbol ~s has previous incompatible type ~p", [Name, Type])
|
||||
end
|
||||
end.
|
||||
|
||||
label(ScanState, Tunit, #s_label{name = Name}) ->
|
||||
case tunit:get_symbol(Tunit, Name) of
|
||||
#symbol{section = false, st_value = false} = Symbol -> label2(Tunit, Symbol);
|
||||
#symbol{} -> fmterr(ScanState, "label ~s already defined", [Name]);
|
||||
false -> label2(Tunit, #symbol{name = Name, st_size = false, st_info = 0})
|
||||
end.
|
||||
|
||||
label2(Tunit, Symbol) ->
|
||||
#tunit{cursect = Cursect} = Tunit,
|
||||
#section{dot = Dot} = tunit:get_section(Tunit, Cursect),
|
||||
{ok, tunit:put_symbol(Tunit, Symbol#symbol{section = Cursect, st_value = Dot})}.
|
||||
|
||||
insn(ScanState, Tunit, #s_insn{} = Stmt) ->
|
||||
#tunit{cursect = Cursect} = Tunit,
|
||||
#section{data = {stmts, Stmts}, dot = Dot} = Section = tunit:get_section(Tunit, Cursect),
|
||||
case Dot rem 4 of % FIXME: target-specific
|
||||
0 ->
|
||||
NewSection =
|
||||
Section#section{ data = {stmts, [Stmt | Stmts]}
|
||||
, dot = Dot + 4 % FIXME: target-specific
|
||||
},
|
||||
{ok, tunit:put_section(Tunit, NewSection)};
|
||||
_ -> fmterr(ScanState, "misaligned address for instruction", [])
|
||||
end.
|
||||
|
||||
%% Initialization --------------------------------------------------------------
|
||||
|
||||
tunit_init() ->
|
||||
SectionText = section_dot_text(),
|
||||
Tunit = tunit:put_section(tunit:new(), SectionText),
|
||||
Tunit#tunit{cursect = SectionText#section.name}.
|
||||
|
||||
%% Predefined Sections ---------------------------------------------------------
|
||||
|
||||
section_dot_comment() -> % ".comment"
|
||||
#section{ name = ".comment"
|
||||
, data = {stmts, []}
|
||||
, dot = false % do not allow dot or labels here
|
||||
, shndx = 0
|
||||
, sh_name = 0
|
||||
, sh_type = ?SHT_PROGBITS
|
||||
, sh_offset = 0
|
||||
, sh_flags = ?SHF_MERGE bor ?SHF_STRINGS
|
||||
, sh_link = 0
|
||||
, sh_addralign = 1
|
||||
, sh_entsize = 1
|
||||
}.
|
||||
|
||||
section_dot_text() -> % ".text"
|
||||
#section{ name = ".text"
|
||||
, data = {stmts, []}
|
||||
, dot = 0
|
||||
, shndx = 0
|
||||
, sh_name = 0
|
||||
, sh_type = ?SHT_PROGBITS
|
||||
, sh_offset = 0
|
||||
, sh_flags = ?SHF_ALLOC bor ?SHF_EXECINSTR
|
||||
, sh_link = 0
|
||||
, sh_addralign = 4 % FIXME: target-specific
|
||||
, sh_entsize = 0
|
||||
}.
|
||||
|
||||
%% Error reporting -------------------------------------------------------------
|
||||
|
||||
%% FIXME: this is duplicated a few times, move it to scan_state.erl
|
||||
fmterr(ScanState, Fmt, Args) ->
|
||||
{ok, FileName} = scan_state:filename(ScanState),
|
||||
{ok, LineNr} = scan_state:linenr(ScanState),
|
||||
{error, lists:flatten(io_lib:format("file ~s line ~p: " ++ Fmt,
|
||||
[FileName, LineNr | Args]))}.
|
||||
560
erlang/apps/as/src/output.erl
Normal file
560
erlang/apps/as/src/output.erl
Normal file
@ -0,0 +1,560 @@
|
||||
%%% -*- erlang-indent-level: 2 -*-
|
||||
%%%
|
||||
%%% ELF output for pdp10-elf as
|
||||
%%% Copyright (C) 2013-2019 Mikael Pettersson
|
||||
%%%
|
||||
%%% This file is part of pdp10-tools.
|
||||
%%%
|
||||
%%% pdp10-tools is free software: you can redistribute it and/or modify
|
||||
%%% it under the terms of the GNU General Public License as published by
|
||||
%%% the Free Software Foundation, either version 3 of the License, or
|
||||
%%% (at your option) any later version.
|
||||
%%%
|
||||
%%% pdp10-tools is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
%%% GNU General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License
|
||||
%%% along with pdp10-tools. If not, see <http://www.gnu.org/licenses/>.
|
||||
%%%
|
||||
%%%-----------------------------------------------------------------------------
|
||||
%%%
|
||||
%%% Output file layout:
|
||||
%%%
|
||||
%%% ELF header
|
||||
%%% <sections directly defined by the input>
|
||||
%%% <.strtab, if any symbols>
|
||||
%%% <.symtab, if any symbols>
|
||||
%%% <.shstrtab, if any sections>
|
||||
%%% <section header table, if any sections>
|
||||
%%%
|
||||
%%% Processing steps:
|
||||
%%%
|
||||
%%% initialize context
|
||||
%%% for each section:
|
||||
%%% - add name to .shstrtab, assign sh_name
|
||||
%%% - assign sh_offset and st_shndx
|
||||
%%% - update context
|
||||
%%% for each symbol:
|
||||
%%% - add name to .strtab, assign st_name
|
||||
%%% - assign st_shndx
|
||||
%%% append .strtab to list of sections
|
||||
%%% append .symtab to list of sections
|
||||
%%% append .shstrtab to list of sections
|
||||
|
||||
-module(output).
|
||||
|
||||
-export([ tunit/2
|
||||
]).
|
||||
|
||||
-include("tunit.hrl").
|
||||
-include_lib("lib/include/pdp10_elf36.hrl").
|
||||
|
||||
-record(strtab,
|
||||
{ map :: #{string() => non_neg_integer()}
|
||||
, dot :: pos_integer()
|
||||
}).
|
||||
|
||||
-record(context,
|
||||
{ tunit :: #tunit{}
|
||||
, shnum :: pos_integer()
|
||||
, offset :: pos_integer()
|
||||
, shstrtab :: #strtab{}
|
||||
, strtab :: #strtab{}
|
||||
}).
|
||||
|
||||
tunit(Tunit, File) ->
|
||||
emit(layout(Tunit), File).
|
||||
|
||||
%% LAYOUT ======================================================================
|
||||
|
||||
layout(Tunit) ->
|
||||
lists:foldl(
|
||||
fun(Fun, Context) -> Fun(Context) end,
|
||||
context_new(Tunit),
|
||||
[ fun process_sections/1
|
||||
, fun process_symbols/1
|
||||
, fun create_strtab/1
|
||||
, fun create_symtab/1
|
||||
, fun create_shstrtab/1
|
||||
, fun align_shtab/1
|
||||
]).
|
||||
|
||||
context_new(Tunit) ->
|
||||
#context{ tunit = Tunit
|
||||
, shnum = 1
|
||||
, offset = ?ELF36_EHDR_SIZEOF
|
||||
, shstrtab = strtab_new()
|
||||
, strtab = strtab_new()
|
||||
}.
|
||||
|
||||
%% Sections --------------------------------------------------------------------
|
||||
|
||||
process_sections(Context) ->
|
||||
#context{tunit = #tunit{sections = Sections}} = Context,
|
||||
lists:foldl(fun process_section/2, Context, maps:values(Sections)).
|
||||
|
||||
process_section(Section, Context) ->
|
||||
append_section(Context, Section).
|
||||
|
||||
append_section(Context, Section) ->
|
||||
#section{ dot = Dot
|
||||
, name = Name
|
||||
, sh_addralign = ShAddrAlign
|
||||
} = Section,
|
||||
case Dot of
|
||||
0 -> Context;
|
||||
_ ->
|
||||
#context{ tunit = Tunit
|
||||
, shnum = ShNum
|
||||
, offset = Offset
|
||||
, shstrtab = ShStrTab
|
||||
} = Context,
|
||||
{ShName, NewShStrTab} = strtab_enter(ShStrTab, Name),
|
||||
ShOffset = (Offset + ShAddrAlign - 1) band bnot (ShAddrAlign - 1),
|
||||
NewSection =
|
||||
Section#section{sh_name = ShName, sh_offset = ShOffset, shndx = ShNum},
|
||||
NewTunit = tunit:put_section(Tunit, NewSection),
|
||||
Context#context{ tunit = NewTunit
|
||||
, shnum = ShNum + 1
|
||||
, offset = ShOffset + Dot
|
||||
, shstrtab = NewShStrTab
|
||||
}
|
||||
end.
|
||||
|
||||
%% Symbols ---------------------------------------------------------------------
|
||||
|
||||
process_symbols(Context) ->
|
||||
#context{tunit = #tunit{symbols = Symbols}} = Context,
|
||||
lists:foldl(fun process_symbol/2, Context, maps:values(Symbols)).
|
||||
|
||||
process_symbol(Symbol, Context) ->
|
||||
#symbol{name = Name, section = Section} = Symbol,
|
||||
#context{tunit = Tunit, strtab = StrTab} = Context,
|
||||
{StName, NewStrTab} = strtab_enter(StrTab, Name),
|
||||
StShndx =
|
||||
case tunit:get_section(Tunit, Section) of
|
||||
false -> ?SHN_ABS;
|
||||
#section{shndx = Shndx} -> Shndx % assigned in append_section/2 above
|
||||
end,
|
||||
NewSymbol = Symbol#symbol{st_name = StName, st_shndx = StShndx},
|
||||
NewTunit = tunit:put_symbol(Tunit, NewSymbol),
|
||||
Context#context{tunit = NewTunit, strtab = NewStrTab}.
|
||||
|
||||
%% Symbol string table (.strtab) -----------------------------------------------
|
||||
|
||||
create_strtab(Context) ->
|
||||
case maps:size(Context#context.tunit#tunit.symbols) of
|
||||
0 -> Context;
|
||||
_ ->
|
||||
StrTab = Context#context.strtab,
|
||||
Image = strtab_image(StrTab),
|
||||
Section =
|
||||
#section{ name = ".strtab"
|
||||
, data = {image, Image}
|
||||
, dot = image_size(Image)
|
||||
, sh_type = ?SHT_STRTAB
|
||||
, sh_flags = ?SHF_MERGE bor ?SHF_STRINGS % FIXME: check
|
||||
, sh_link = 0
|
||||
, sh_addralign = 1 % FIXME: check
|
||||
, sh_entsize = 1 % FIXME: check
|
||||
},
|
||||
append_section(Context, Section)
|
||||
end.
|
||||
|
||||
%% Symbol table (.symtab) ------------------------------------------------------
|
||||
|
||||
create_symtab(Context) ->
|
||||
#context{tunit = Tunit} = Context,
|
||||
#tunit{symbols = Symbols} = Tunit,
|
||||
case maps:size(Symbols) of
|
||||
0 -> Context;
|
||||
NrSyms ->
|
||||
#section{shndx = StrTabShndx} = tunit:get_section(Tunit, ".strtab"),
|
||||
Image = symbols_image(Symbols),
|
||||
Size = (NrSyms + 1) * ?ELF36_SYM_SIZEOF,
|
||||
Size = image_size(Image), % consistency check
|
||||
Section =
|
||||
#section{ name = ".symtab"
|
||||
, data = {image, Image}
|
||||
, dot = Size
|
||||
, sh_type = ?SHT_SYMTAB
|
||||
, sh_flags = 0
|
||||
, sh_link = StrTabShndx
|
||||
, sh_addralign = 4 % FIXME: check
|
||||
, sh_entsize = ?ELF36_SYM_SIZEOF
|
||||
},
|
||||
append_section(Context, Section)
|
||||
end.
|
||||
|
||||
symbols_image(Symbols) ->
|
||||
ElfSym0 =
|
||||
#elf36_Sym{ st_name = 0
|
||||
, st_value = 0
|
||||
, st_size = 0
|
||||
, st_info = ?ELF36_ST_INFO(?STB_LOCAL, ?STT_NOTYPE)
|
||||
, st_other = 0
|
||||
, st_shndx = ?SHN_UNDEF
|
||||
},
|
||||
[elf36_Sym_image(ElfSym0) |
|
||||
lists:map(fun symbol_image/1, maps:values(Symbols))].
|
||||
|
||||
symbol_image(Symbol) ->
|
||||
#symbol{ st_value = StValue
|
||||
, st_size = StSize
|
||||
, st_info = StInfo
|
||||
, st_name = StName
|
||||
, st_shndx = StShndx
|
||||
} = Symbol,
|
||||
ElfSym =
|
||||
#elf36_Sym{ st_name = StName
|
||||
, st_value = StValue
|
||||
, st_size = StSize
|
||||
, st_info = StInfo
|
||||
, st_other = ?STV_DEFAULT % FIXME: should be set earlier
|
||||
, st_shndx = StShndx
|
||||
},
|
||||
elf36_Sym_image(ElfSym).
|
||||
|
||||
%% FIXME: the code below belongs in a library
|
||||
|
||||
elf36_Sym_image(ElfSym) ->
|
||||
#elf36_Sym{ st_name = StName
|
||||
, st_value = StValue
|
||||
, st_size = StSize
|
||||
, st_info = StInfo
|
||||
, st_other = StOther
|
||||
, st_shndx = StShndx
|
||||
} = ElfSym,
|
||||
[ elf36_Word_image(StName)
|
||||
, elf36_Addr_image(StValue)
|
||||
, elf36_Word_image(if StSize =:= false -> 0; true -> StSize end)
|
||||
, elf36_Uchar_image(StInfo)
|
||||
, elf36_Uchar_image(StOther)
|
||||
, elf36_Half_image(StShndx)
|
||||
].
|
||||
|
||||
elf36_Addr_image(Addr) -> uint36_image(Addr).
|
||||
elf36_Half_image(Half) -> uint18_image(Half).
|
||||
elf36_Off_image(Off) -> uint36_image(Off).
|
||||
elf36_Word_image(Word) -> uint36_image(Word).
|
||||
elf36_Uchar_image(Uchar) -> uint9_image(Uchar).
|
||||
|
||||
uint9_image(Uint9) ->
|
||||
Uint9 band 511.
|
||||
|
||||
uint18_image(Uint18) ->
|
||||
[(Uint18 bsr 9) band 511, Uint18 band 511].
|
||||
|
||||
uint36_image(Uint36) ->
|
||||
[(Uint36 bsr 27) band 511,
|
||||
(Uint36 bsr 18) band 511,
|
||||
(Uint36 bsr 9) band 511,
|
||||
Uint36 band 511].
|
||||
|
||||
image_size(Image) -> image_size(Image, 0).
|
||||
|
||||
image_size([H | T], Acc) -> image_size(T, image_size(H, Acc));
|
||||
image_size([], Acc) -> Acc;
|
||||
image_size(TByte, Acc) when is_integer(TByte), 0 =< TByte, TByte =< 511 -> Acc + 1.
|
||||
|
||||
%% Section Header String Table (.shstrtab) -------------------------------------
|
||||
|
||||
create_shstrtab(Context) ->
|
||||
case Context#context.shnum of
|
||||
1 -> Context;
|
||||
_ ->
|
||||
%% Note that append_section/1 enters the section's name to shstrtab,
|
||||
%% updating its contents if the name wasn't already there, which would
|
||||
%% invalidate the image recorded in the section. To avoid that, enter
|
||||
%% the name first.
|
||||
OldShStrTab = Context#context.shstrtab,
|
||||
{_ShName, NewShStrTab} = strtab_enter(OldShStrTab, ".shstrtab"),
|
||||
Image = strtab_image(NewShStrTab),
|
||||
Section =
|
||||
#section{ name = ".shstrtab"
|
||||
, data = {image, Image}
|
||||
, dot = image_size(Image)
|
||||
, sh_type = ?SHT_STRTAB
|
||||
, sh_flags = ?SHF_MERGE bor ?SHF_STRINGS % FIXME: check
|
||||
, sh_link = 0
|
||||
, sh_addralign = 1 % FIXME: check
|
||||
, sh_entsize = 1 % FIXME: check
|
||||
},
|
||||
append_section(Context#context{shstrtab = NewShStrTab}, Section)
|
||||
end.
|
||||
|
||||
%% Align Section Header Table --------------------------------------------------
|
||||
|
||||
align_shtab(Context) ->
|
||||
case Context#context.shnum of
|
||||
1 ->
|
||||
Context#context{shnum = 0, offset = 0};
|
||||
_ ->
|
||||
Offset = Context#context.offset,
|
||||
ShTabOffset = (Offset + (4 - 1)) band bnot (4 - 1),
|
||||
Context#context{offset = ShTabOffset}
|
||||
end.
|
||||
|
||||
%% String Tables ---------------------------------------------------------------
|
||||
%% FIXME: duplicates code for .ident directive / .comment section
|
||||
|
||||
strtab_new() ->
|
||||
#strtab{map = maps:new(), dot = 1}. % 1 due to NUL before 1st string
|
||||
|
||||
strtab_enter(StrTab = #strtab{map = Map, dot = Dot}, String) ->
|
||||
case maps:get(String, Map, false) of
|
||||
false -> {Dot, StrTab#strtab{map = maps:put(String, Dot, Map),
|
||||
dot = Dot + length(String) + 1}}; % +1 for terminating NUL
|
||||
Offset -> {Offset, StrTab}
|
||||
end.
|
||||
|
||||
strtab_image(#strtab{map = Map}) ->
|
||||
KVs = maps:to_list(Map),
|
||||
VKs = lists:map(fun({K, V}) -> {V, K} end, KVs),
|
||||
SortedVKs = lists:sort(VKs),
|
||||
[0 | lists:map(fun({_V, K}) -> K ++ [0] end, SortedVKs)].
|
||||
|
||||
%% EMIT ========================================================================
|
||||
|
||||
emit(Context, File) ->
|
||||
case pdp10_stdio:fopen(File, [raw, write, delayed_write]) of
|
||||
{ok, FP} ->
|
||||
try
|
||||
Funs =
|
||||
[ fun emit_elf_header/3
|
||||
, fun emit_sections/3
|
||||
, fun emit_shtab/3
|
||||
],
|
||||
emit(Funs, Context, FP, 0)
|
||||
after pdp10_stdio:fclose(FP)
|
||||
end;
|
||||
{error, Reason} -> {error, io_lib:format("opening ~s: ~p", [File, Reason])}
|
||||
end.
|
||||
|
||||
emit([], _Context, _FP, _Offset) -> ok;
|
||||
emit([Fun | Funs], Context, FP, Offset) ->
|
||||
case Fun(Context, FP, Offset) of
|
||||
{ok, NewOffset} -> emit(Funs, Context, FP, NewOffset);
|
||||
{error, _Reason} = Error -> Error
|
||||
end.
|
||||
|
||||
emit_elf_header(Context, FP, Offset = 0) ->
|
||||
ShStrTabShndx =
|
||||
case tunit:get_section(Context#context.tunit, ".shstrtab") of
|
||||
#section{shndx = Shndx} -> Shndx;
|
||||
false -> 0
|
||||
end,
|
||||
ElfHdr =
|
||||
#elf36_Ehdr{ e_ident = e_ident()
|
||||
, e_type = ?ET_REL
|
||||
, e_machine = ?EM_PDP10 % FIXME: target-specific
|
||||
, e_version = ?EV_CURRENT
|
||||
, e_entry = 0
|
||||
, e_phoff = 0
|
||||
, e_shoff = Context#context.offset
|
||||
, e_flags = 0
|
||||
, e_ehsize = ?ELF36_EHDR_SIZEOF
|
||||
, e_phentsize = 0
|
||||
, e_phnum = 0
|
||||
, e_shentsize = ?ELF36_SHDR_SIZEOF
|
||||
, e_shnum = Context#context.shnum
|
||||
, e_shstrndx = ShStrTabShndx
|
||||
},
|
||||
emit_image(elf36_Ehdr_image(ElfHdr), ?ELF36_EHDR_SIZEOF, FP, Offset).
|
||||
|
||||
emit_sections(Context, FP, Offset = ?ELF36_EHDR_SIZEOF) ->
|
||||
#context{tunit = #tunit{sections = SectionsMap}} = Context,
|
||||
Sections = lists:sort(fun order_by_sh_offset/2, maps:values(SectionsMap)),
|
||||
emit_sections2(Sections, FP, Offset).
|
||||
|
||||
emit_sections2([], _FP, Offset) -> {ok, Offset};
|
||||
emit_sections2([Section | Sections], FP, Offset) ->
|
||||
case emit_section(Section, FP, Offset) of
|
||||
{ok, NewOffset} -> emit_sections2(Sections, FP, NewOffset);
|
||||
{error, _Reason} = Error -> Error
|
||||
end.
|
||||
|
||||
emit_section(Section, FP, Offset) ->
|
||||
case Section#section.dot of
|
||||
0 -> {ok, Offset};
|
||||
Dot ->
|
||||
ShOffset = Section#section.sh_offset,
|
||||
NrPadBytes = ShOffset - Offset,
|
||||
case emit_padding(NrPadBytes, FP) of
|
||||
ok ->
|
||||
{image, Image} = Section#section.data,
|
||||
emit_image(Image, Dot, FP, ShOffset);
|
||||
{error, _Reason} = Error -> Error
|
||||
end
|
||||
end.
|
||||
|
||||
emit_shtab(Context, FP, Offset) ->
|
||||
case Context#context.offset of
|
||||
0 -> {ok, Offset};
|
||||
ShTabOffset ->
|
||||
case emit_padding(ShTabOffset - Offset, FP) of
|
||||
ok ->
|
||||
case emit_shdr0(FP, ShTabOffset) of
|
||||
{ok, NewOffset} ->
|
||||
#context{tunit = #tunit{sections = SectionsMap}} = Context,
|
||||
Sections = lists:sort(fun order_by_shndx/2, maps:values(SectionsMap)),
|
||||
emit_shdrs(Sections, FP, NewOffset);
|
||||
{error, _Reason} = Error -> Error
|
||||
end;
|
||||
{error, _Reason} = Error -> Error
|
||||
end
|
||||
end.
|
||||
|
||||
emit_shdrs([], _FP, Offset) -> {ok, Offset};
|
||||
emit_shdrs([Section | Sections], FP, Offset) ->
|
||||
case emit_shdr(Section, FP, Offset) of
|
||||
{ok, NewOffset} -> emit_shdrs(Sections, FP, NewOffset);
|
||||
{error, _Reason} = Error -> Error
|
||||
end.
|
||||
|
||||
emit_shdr(Section, FP, Offset) ->
|
||||
case Section#section.dot of
|
||||
0 -> {ok, Offset};
|
||||
Dot ->
|
||||
#section{ sh_name = ShName
|
||||
, sh_type = ShType
|
||||
, sh_offset = ShOffset
|
||||
, sh_flags = ShFlags
|
||||
, sh_link = ShLink
|
||||
, sh_addralign = ShAddrAlign
|
||||
, sh_entsize = ShEntSize
|
||||
} = Section,
|
||||
ElfShdr =
|
||||
#elf36_Shdr{ sh_name = ShName
|
||||
, sh_type = ShType
|
||||
, sh_flags = ShFlags
|
||||
, sh_addr = 0
|
||||
, sh_offset = ShOffset
|
||||
, sh_size = Dot
|
||||
, sh_link = ShLink
|
||||
, sh_info = 0 % FIXME: for symtab, LAST_LOCAL + 1
|
||||
, sh_addralign = ShAddrAlign
|
||||
, sh_entsize = ShEntSize
|
||||
},
|
||||
emit_elf36_Shdr(ElfShdr, FP, Offset)
|
||||
end.
|
||||
|
||||
emit_shdr0(FP, Offset) ->
|
||||
ElfShdr0 =
|
||||
#elf36_Shdr{ sh_name = 0
|
||||
, sh_type = ?SHT_NULL
|
||||
, sh_flags = 0
|
||||
, sh_addr = 0
|
||||
, sh_offset = 0
|
||||
, sh_size = 0
|
||||
, sh_link = 0
|
||||
, sh_info = 0
|
||||
, sh_addralign = 0
|
||||
, sh_entsize = 0
|
||||
},
|
||||
emit_elf36_Shdr(ElfShdr0, FP, Offset).
|
||||
|
||||
emit_elf36_Shdr(Shdr, FP, Offset) ->
|
||||
emit_image(elf36_Shdr_image(Shdr), ?ELF36_SHDR_SIZEOF, FP, Offset).
|
||||
|
||||
elf36_Shdr_image(ElfShdr) ->
|
||||
#elf36_Shdr{ sh_name = ShName
|
||||
, sh_type = ShType
|
||||
, sh_flags = ShFlags
|
||||
, sh_addr = ShAddr
|
||||
, sh_offset = ShOffset
|
||||
, sh_size = ShSize
|
||||
, sh_link = ShLink
|
||||
, sh_info = ShInfo
|
||||
, sh_addralign = ShAddrAlign
|
||||
, sh_entsize = ShEntSize
|
||||
} = ElfShdr,
|
||||
[ elf36_Word_image(ShName)
|
||||
, elf36_Word_image(ShType)
|
||||
, elf36_Word_image(ShFlags)
|
||||
, elf36_Addr_image(ShAddr)
|
||||
, elf36_Off_image(ShOffset)
|
||||
, elf36_Word_image(ShSize)
|
||||
, elf36_Word_image(ShLink)
|
||||
, elf36_Word_image(ShInfo)
|
||||
, elf36_Word_image(ShAddrAlign)
|
||||
, elf36_Word_image(ShEntSize)
|
||||
].
|
||||
|
||||
emit_padding(0, _FP) -> ok;
|
||||
emit_padding(N, FP) when N > 0 ->
|
||||
case pdp10_stdio:fputc(0, FP) of
|
||||
ok -> emit_padding(N - 1, FP);
|
||||
{error, _Reason} = Error -> Error
|
||||
end.
|
||||
|
||||
order_by_sh_offset(Section1, Section2) ->
|
||||
Section1#section.sh_offset =< Section2#section.sh_offset.
|
||||
|
||||
order_by_shndx(Section1, Section2) ->
|
||||
Section1#section.shndx =< Section2#section.shndx.
|
||||
|
||||
elf36_Ehdr_image(ElfHdr) ->
|
||||
#elf36_Ehdr{ e_ident = EIdent
|
||||
, e_type = EType
|
||||
, e_machine = EMachine
|
||||
, e_version = EVersion
|
||||
, e_entry = EEntry
|
||||
, e_phoff = EPhOff
|
||||
, e_shoff = EShOff
|
||||
, e_flags = EFlags
|
||||
, e_ehsize = EEhSize
|
||||
, e_phentsize = EPhEntSize
|
||||
, e_phnum = EPhNum
|
||||
, e_shentsize = EShEntSize
|
||||
, e_shnum = EShNum
|
||||
, e_shstrndx = EShStrNdx
|
||||
} = ElfHdr,
|
||||
[ EIdent % already a list of bytes
|
||||
, elf36_Half_image(EType)
|
||||
, elf36_Half_image(EMachine)
|
||||
, elf36_Word_image(EVersion)
|
||||
, elf36_Addr_image(EEntry)
|
||||
, elf36_Off_image(EPhOff)
|
||||
, elf36_Off_image(EShOff)
|
||||
, elf36_Word_image(EFlags)
|
||||
, elf36_Half_image(EEhSize)
|
||||
, elf36_Half_image(EPhEntSize)
|
||||
, elf36_Half_image(EPhNum)
|
||||
, elf36_Half_image(EShEntSize)
|
||||
, elf36_Half_image(EShNum)
|
||||
, elf36_Half_image(EShStrNdx)
|
||||
].
|
||||
|
||||
e_ident() ->
|
||||
tuple_to_list(
|
||||
erlang:make_tuple(
|
||||
?EI_NIDENT, 0,
|
||||
[ {1 + ?EI_MAG0, ?ELFMAG0}
|
||||
, {1 + ?EI_MAG1, ?ELFMAG1}
|
||||
, {1 + ?EI_MAG2, ?ELFMAG2}
|
||||
, {1 + ?EI_MAG3, ?ELFMAG3}
|
||||
, {1 + ?EI_CLASS, ?ELFCLASS36}
|
||||
, {1 + ?EI_DATA, ?ELFDATA2MSB}
|
||||
, {1 + ?EI_VERSION, ?EV_CURRENT}
|
||||
, {1 + ?EI_OSABI, ?ELFOSABI_NONE} % TODO: ELFOSABI_LINUX instead?
|
||||
, {1 + ?EI_ABIVERSION, 0}
|
||||
])).
|
||||
|
||||
emit_image(Image, NrBytes, FP, Offset) ->
|
||||
NrBytes = image_size(Image), % assert
|
||||
case image_write(Image, FP) of
|
||||
ok -> {ok, Offset + NrBytes};
|
||||
{error, _Reason} = Error -> Error
|
||||
end.
|
||||
|
||||
image_write([H | T], FP) ->
|
||||
case image_write(H, FP) of
|
||||
ok -> image_write(T, FP);
|
||||
{error, _Reason} = Error -> Error
|
||||
end;
|
||||
image_write([], _FP) -> ok;
|
||||
image_write(TByte, FP) when is_integer(TByte), 0 =< TByte, TByte =< 511 ->
|
||||
pdp10_stdio:fputc(TByte, FP).
|
||||
312
erlang/apps/as/src/parse.erl
Normal file
312
erlang/apps/as/src/parse.erl
Normal file
@ -0,0 +1,312 @@
|
||||
%%% -*- erlang-indent-level: 2 -*-
|
||||
%%%
|
||||
%%% parser for pdp10-elf as
|
||||
%%% Copyright (C) 2013-2019 Mikael Pettersson
|
||||
%%%
|
||||
%%% This file is part of pdp10-tools.
|
||||
%%%
|
||||
%%% pdp10-tools is free software: you can redistribute it and/or modify
|
||||
%%% it under the terms of the GNU General Public License as published by
|
||||
%%% the Free Software Foundation, either version 3 of the License, or
|
||||
%%% (at your option) any later version.
|
||||
%%%
|
||||
%%% pdp10-tools is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
%%% GNU General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License
|
||||
%%% along with pdp10-tools. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
-module(parse).
|
||||
|
||||
-export([ stmt/1
|
||||
]).
|
||||
|
||||
-include("token.hrl").
|
||||
-include("tunit.hrl").
|
||||
-include_lib("lib/include/pdp10_opcodes.hrl").
|
||||
|
||||
stmt(ScanState) ->
|
||||
case scan:token(ScanState) of
|
||||
{ok, ?T_DOT_FILE} -> dot_file(ScanState);
|
||||
{ok, ?T_DOT_GLOBL} -> dot_globl(ScanState);
|
||||
{ok, ?T_DOT_IDENT} -> dot_ident(ScanState);
|
||||
{ok, ?T_DOT_SIZE} -> dot_size(ScanState);
|
||||
{ok, ?T_DOT_TEXT} -> dot_text(ScanState);
|
||||
{ok, ?T_DOT_TYPE} -> dot_type(ScanState);
|
||||
{ok, {?T_SYMBOL, Name}} -> stmt_after_symbol(ScanState, Name);
|
||||
{ok, ?T_NEWLINE} -> stmt(ScanState);
|
||||
{ok, ?T_EOF} -> eof;
|
||||
ScanRes -> badtok(ScanState, "expected directive, label, or instruction", ScanRes)
|
||||
end.
|
||||
|
||||
%% Instructions and labels -----------------------------------------------------
|
||||
%%
|
||||
%% Recognize:
|
||||
%%
|
||||
%% <label> ::= <symbol> ":"
|
||||
%%
|
||||
%% <insn> ::= <symbol> (<accumulator> ",")? <address> <newline>
|
||||
%%
|
||||
%% <accumulator> ::= <uinteger> [uint <= 0xF]
|
||||
%%
|
||||
%% <address> ::= "@"? <displacement>? <index>?
|
||||
%%
|
||||
%% <displacement> ::= <uinteger>
|
||||
%%
|
||||
%% <index> ::= "(" <accumulator> ")"
|
||||
%%
|
||||
%% Examples:
|
||||
%%
|
||||
%% foo:
|
||||
%% popj 17,
|
||||
%% pushj 17,bar
|
||||
%% movei 1,@fum(2)
|
||||
%%
|
||||
%% TODO: <displacement> should be <expr> and permit parentheses and
|
||||
%% various operators.
|
||||
%%
|
||||
%% Ambiguous example:
|
||||
%%
|
||||
%% <symbol> (<uinteger>) <newline>
|
||||
%%
|
||||
%% This is ambiguous since we have no special notation for <register>, and the
|
||||
%% same kind of parentheses are used for expression grouping in the displacement
|
||||
%% as for the index register.
|
||||
%%
|
||||
%% This might denote an insn with a parenthesized displacement and no index,
|
||||
%% or it might denote an insn with an index but no displacement.
|
||||
%%
|
||||
%% However, an index always uses the displacement, even if zero, so we require
|
||||
%% an explicit displacement if an index is used.
|
||||
%%
|
||||
%% This means that "opcode (...)" is interpreted as having a displacement but no
|
||||
%% index. Use "opcode 0(index)" if an index with zero displacement is needed.
|
||||
|
||||
stmt_after_symbol(ScanState, Name) ->
|
||||
case scan:token(ScanState) of
|
||||
{ok, ?T_COLON} -> {ok, #s_label{name = Name}};
|
||||
{ok, ?T_NEWLINE} -> make_insn(ScanState, Name, false, false, false, false);
|
||||
{ok, {?T_UINTEGER, UInt}} -> insn_uint(ScanState, Name, UInt);
|
||||
ScanRes -> badtok(ScanState, "junk after symbol", ScanRes)
|
||||
end.
|
||||
|
||||
%% Seen "<symbol> <uinteger>". The <uinteger> is the <accumulator> if followed
|
||||
%% by ",", otherwise (the start of) the <displacement>.
|
||||
insn_uint(ScanState, Name, UInt) ->
|
||||
case scan:token(ScanState) of
|
||||
{ok, ?T_COMMA} -> % the Uint is the Accumulator, parse EA next
|
||||
insn_ea(ScanState, Name, _AccOrDev = UInt);
|
||||
{ok, ?T_LPAREN} -> % the Uint is the Displacement, parse Index next
|
||||
insn_ea_index(ScanState, Name, _AccOrDev = false, _At = false, _Displacement = UInt);
|
||||
{ok, ?T_NEWLINE} -> % the Uint is the Displacement
|
||||
make_insn(ScanState, Name, _AccOrDev = false, _At = false, _Displacement = UInt, _Index = false);
|
||||
ScanRes -> badtok(ScanState, "junk after <symbol> <uinteger>", ScanRes)
|
||||
end.
|
||||
|
||||
%% <symbol> <accordev> "," . [ ["@"] <displacement> ["(" <index> ")"] ] <newline>
|
||||
insn_ea(ScanState, Name, AccOrDev) ->
|
||||
case scan:token(ScanState) of
|
||||
{ok, ?T_NEWLINE} ->
|
||||
make_insn(ScanState, Name, AccOrDev, _At = false, _Displacement = false, _Index = false);
|
||||
{ok, ?T_AT} -> insn_ea_at(ScanState, Name, AccOrDev);
|
||||
{ok, {?T_UINTEGER, Displacement}} ->
|
||||
insn_ea_disp(ScanState, Name, AccOrDev, _At = false, Displacement);
|
||||
ScanRes -> badtok(ScanState, "junk after comma", ScanRes)
|
||||
end.
|
||||
|
||||
%% <symbol> [<accordev> ","] "@" . <displacement> . ["(" <index> ")"] <newline>
|
||||
insn_ea_at(ScanState, Name, AccOrDev) ->
|
||||
case scan:token(ScanState) of
|
||||
{ok, {?T_UINTEGER, Displacement}} ->
|
||||
insn_ea_disp(ScanState, Name, AccOrDev, _At = true, Displacement);
|
||||
ScanRes -> badtok(ScanState, "junk after @", ScanRes)
|
||||
end.
|
||||
|
||||
%% <symbol> [<accordev> ","] ["@"] <displacement> . ["(" <index> ")"] <newline>
|
||||
insn_ea_disp(ScanState, Name, AccOrDev, At, Displacement) ->
|
||||
case scan:token(ScanState) of
|
||||
{ok, ?T_LPAREN} -> insn_ea_index(ScanState, Name, AccOrDev, At, Displacement);
|
||||
{ok, ?T_NEWLINE} -> make_insn(ScanState, Name, AccOrDev, At, Displacement, _Index = false);
|
||||
ScanRes -> badtok(ScanState, "junk after <displacement>", ScanRes)
|
||||
end.
|
||||
|
||||
%% <symbol> [<accordev> ","] ["@"] <displacement> "(" . <index> ")" <newline>
|
||||
insn_ea_index(ScanState, Name, AccOrDev, At, Displacement) ->
|
||||
case scan:token(ScanState) of
|
||||
{ok, {?T_UINTEGER, Index}} when Index =< 8#17 ->
|
||||
case scan:token(ScanState) of
|
||||
{ok, ?T_RPAREN} ->
|
||||
case scan:token(ScanState) of
|
||||
{ok, ?T_NEWLINE} ->
|
||||
make_insn(ScanState, Name, AccOrDev, At, Displacement, Index);
|
||||
ScanRes -> badtok(ScanState, "junk after <index>", ScanRes)
|
||||
end;
|
||||
ScanRes -> badtok(ScanState, "junk in <index>", ScanRes)
|
||||
end;
|
||||
ScanRes -> badtok(ScanState, "junk in <index>", ScanRes)
|
||||
end.
|
||||
|
||||
make_insn(ScanState, Name, AccOrDev, At, Displacement, Index) ->
|
||||
Models = ?PDP10_KL10_271, % FIXME: make dynamic
|
||||
case pdp10_opcodes:insn_from_name(Models, Name, AccOrDev =/= false) of
|
||||
false -> badinsn(ScanState, "invalid mnemonic ~s", Name);
|
||||
#pdp10_insn_desc{ high13 = High13
|
||||
, format = Format
|
||||
, e_unused = EUnused
|
||||
, extended = false % TODO: handle extended opcodes
|
||||
} ->
|
||||
case make_high13(ScanState, Name, AccOrDev, High13, Format) of
|
||||
{error, _Reason} = Error -> Error;
|
||||
{ok, FinalHigh13} ->
|
||||
case check_e(ScanState, Name, At, Displacement, Index, EUnused) of
|
||||
{error, _Reason} = Error -> Error;
|
||||
ok -> {ok, #s_insn{ high13 = FinalHigh13
|
||||
, at = At
|
||||
, address = if Displacement =:= false -> 0; true -> Displacement end
|
||||
, index = if Index =:= false -> 0; true -> Index end
|
||||
}}
|
||||
end
|
||||
end
|
||||
end.
|
||||
|
||||
make_high13(ScanState, Name, AccOrDev, High13, Format) ->
|
||||
case {Format, AccOrDev} of
|
||||
{?PDP10_INSN_A_OPCODE, false} ->
|
||||
{ok, High13};
|
||||
{?PDP10_INSN_A_OPCODE, _} ->
|
||||
badinsn(ScanState, "~s: extraneous accumulator operand", Name);
|
||||
{?PDP10_INSN_IO, false} ->
|
||||
badinsn(ScanState, "~s: missing device operand", Name);
|
||||
{?PDP10_INSN_IO, _} ->
|
||||
make_high13_io(High13, AccOrDev);
|
||||
{?PDP10_INSN_BASIC, false} ->
|
||||
badinsn(ScanState, "~s: missing accumulator operand", Name);
|
||||
{?PDP10_INSN_BASIC, _} ->
|
||||
make_high13_basic(High13, AccOrDev);
|
||||
{?PDP10_INSN_A_NONZERO, false} ->
|
||||
badinsn(ScanState, "~s: missing accumulator operand", Name);
|
||||
{?PDP10_INSN_A_NONZERO, 0} ->
|
||||
badinsn(ScanState, "~s: accumulator must not be zero", Name);
|
||||
{?PDP10_INSN_A_NONZERO, _} ->
|
||||
make_high13_basic(High13, AccOrDev)
|
||||
end.
|
||||
|
||||
make_high13_basic(High13, Accumulator) ->
|
||||
Mask = ((1 bsl 4) - 1),
|
||||
{ok, (High13 band bnot Mask) bor (Accumulator band Mask)}.
|
||||
|
||||
make_high13_io(High13, Device) ->
|
||||
Mask = ((1 bsl 7) - 1),
|
||||
{ok, (High13 band bnot (Mask bsl 3)) bor ((Device band Mask) bsl 3)}.
|
||||
|
||||
check_e(ScanState, Name, At, Displacement, Index, EUnused) ->
|
||||
HaveE = At orelse Displacement =/= false orelse Index =/= false,
|
||||
case {EUnused, HaveE} of
|
||||
{true, false} -> ok;
|
||||
{true, true} -> badinsn(ScanState, "~s: extraneous address operand", Name);
|
||||
{false, false} -> badinsn(ScanState, "~s: missing address operand", Name);
|
||||
{false, true} -> ok
|
||||
end.
|
||||
|
||||
badinsn(ScanState, Fmt, Mnemonic) ->
|
||||
fmterr(ScanState, Fmt, [Mnemonic]).
|
||||
|
||||
%% Directives ------------------------------------------------------------------
|
||||
|
||||
dot_file(ScanState) ->
|
||||
dot_file_or_ident(ScanState, fun(String) -> #s_dot_file{string = String} end,
|
||||
"junk after .file").
|
||||
|
||||
dot_ident(ScanState) ->
|
||||
dot_file_or_ident(ScanState, fun(String) -> #s_dot_ident{string = String} end,
|
||||
"junk after .ident").
|
||||
|
||||
dot_file_or_ident(ScanState, MkStmt, ErrMsg) ->
|
||||
case scan:token(ScanState) of
|
||||
{ok, {?T_STRING, String}} ->
|
||||
case scan:token(ScanState) of
|
||||
{ok, ?T_NEWLINE} -> {ok, MkStmt(String)};
|
||||
ScanRes -> badtok(ScanState, ErrMsg, ScanRes)
|
||||
end;
|
||||
ScanRes -> badtok(ScanState, ErrMsg, ScanRes)
|
||||
end.
|
||||
|
||||
dot_globl(ScanState) ->
|
||||
case scan:token(ScanState) of
|
||||
{ok, {?T_SYMBOL, Name}} ->
|
||||
case scan:token(ScanState) of
|
||||
{ok, ?T_NEWLINE} -> {ok, #s_dot_globl{name = Name}};
|
||||
ScanRes -> badtok(ScanState, "junk after .globl", ScanRes)
|
||||
end;
|
||||
ScanRes -> badtok(ScanState, "junk after .globl", ScanRes)
|
||||
end.
|
||||
|
||||
%% For now only accepts ".size <sym>,.-<sym>". TODO: extend
|
||||
dot_size(ScanState) ->
|
||||
case scan:token(ScanState) of
|
||||
{ok, {?T_SYMBOL, Name}} ->
|
||||
case scan:token(ScanState) of
|
||||
{ok, ?T_COMMA} ->
|
||||
case scan:token(ScanState) of
|
||||
{ok, ?T_DOT} ->
|
||||
case scan:token(ScanState) of
|
||||
{ok, ?T_MINUS} ->
|
||||
case scan:token(ScanState) of
|
||||
{ok, {?T_SYMBOL, Name}} -> % same Name as above
|
||||
case scan:token(ScanState) of
|
||||
{ok, ?T_NEWLINE} -> {ok, #s_dot_size{name = Name}};
|
||||
ScanRes -> badtok(ScanState, "junk after .size", ScanRes)
|
||||
end;
|
||||
ScanRes -> badtok(ScanState, "junk after .size", ScanRes)
|
||||
end;
|
||||
ScanRes -> badtok(ScanState, "junk after .size", ScanRes)
|
||||
end;
|
||||
ScanRes -> badtok(ScanState, "junk after .size", ScanRes)
|
||||
end;
|
||||
ScanRes -> badtok(ScanState, "junk after .size", ScanRes)
|
||||
end;
|
||||
ScanRes -> badtok(ScanState, "junk after .size", ScanRes)
|
||||
end.
|
||||
|
||||
dot_text(ScanState) ->
|
||||
case scan:token(ScanState) of
|
||||
{ok, ?T_NEWLINE} -> {ok, #s_dot_text{}};
|
||||
ScanRes -> badtok(ScanState, "junk after .text", ScanRes)
|
||||
end.
|
||||
|
||||
%% For now only accepts ".type <sym>,@function". TODO: extend
|
||||
dot_type(ScanState) ->
|
||||
case scan:token(ScanState) of
|
||||
{ok, {?T_SYMBOL, Name}} ->
|
||||
case scan:token(ScanState) of
|
||||
{ok, ?T_COMMA} ->
|
||||
case scan:token(ScanState) of
|
||||
{ok, ?T_AT} ->
|
||||
case scan:token(ScanState) of
|
||||
{ok, {?T_SYMBOL, "function"}} ->
|
||||
case scan:token(ScanState) of
|
||||
{ok, ?T_NEWLINE} -> {ok, #s_dot_type{name = Name}};
|
||||
ScanRes -> badtok(ScanState, "junk after .type", ScanRes)
|
||||
end;
|
||||
ScanRes -> badtok(ScanState, "junk after .type", ScanRes)
|
||||
end;
|
||||
ScanRes -> badtok(ScanState, "junk after .type", ScanRes)
|
||||
end;
|
||||
ScanRes -> badtok(ScanState, "junk after .type", ScanRes)
|
||||
end;
|
||||
ScanRes -> badtok(ScanState, "junk after .type", ScanRes)
|
||||
end.
|
||||
|
||||
%% Error reporting -------------------------------------------------------------
|
||||
|
||||
badtok(_ScanState, _ErrMsg, {error, _Reason} = Error) -> Error;
|
||||
badtok(ScanState, ErrMsg, {ok, Token}) ->
|
||||
fmterr(ScanState, ErrMsg ++ "; current token is ~s", [token:to_string(Token)]).
|
||||
|
||||
fmterr(ScanState, Fmt, Args) ->
|
||||
{ok, FileName} = scan_state:filename(ScanState),
|
||||
{ok, LineNr} = scan_state:linenr(ScanState),
|
||||
{error, lists:flatten(io_lib:format("file ~s line ~p: " ++ Fmt,
|
||||
[FileName, LineNr | Args]))}.
|
||||
232
erlang/apps/as/src/scan.erl
Normal file
232
erlang/apps/as/src/scan.erl
Normal file
@ -0,0 +1,232 @@
|
||||
%%% -*- erlang-indent-level: 2 -*-
|
||||
%%%
|
||||
%%% scanner for pdp10-elf as
|
||||
%%% Copyright (C) 2013-2019 Mikael Pettersson
|
||||
%%%
|
||||
%%% This file is part of pdp10-tools.
|
||||
%%%
|
||||
%%% pdp10-tools is free software: you can redistribute it and/or modify
|
||||
%%% it under the terms of the GNU General Public License as published by
|
||||
%%% the Free Software Foundation, either version 3 of the License, or
|
||||
%%% (at your option) any later version.
|
||||
%%%
|
||||
%%% pdp10-tools is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
%%% GNU General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License
|
||||
%%% along with pdp10-tools. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
-module(scan).
|
||||
|
||||
-export([ token/1
|
||||
]).
|
||||
|
||||
-include("token.hrl").
|
||||
|
||||
token(ScanState) ->
|
||||
case scan_state:fgetc(ScanState) of
|
||||
{error, _Reason} = Error -> Error;
|
||||
eof -> {ok, ?T_EOF};
|
||||
{ok, Ch} ->
|
||||
case Ch of
|
||||
$\s -> token(ScanState);
|
||||
$\t -> token(ScanState);
|
||||
$\r -> token(ScanState);
|
||||
$\f -> token(ScanState);
|
||||
$\n -> {ok, ?T_NEWLINE};
|
||||
$# -> do_line_comment(ScanState);
|
||||
$@ -> {ok, ?T_AT};
|
||||
$: -> {ok, ?T_COLON};
|
||||
$, -> {ok, ?T_COMMA};
|
||||
$( -> {ok, ?T_LPAREN};
|
||||
$) -> {ok, ?T_RPAREN};
|
||||
$/ -> do_slash(ScanState);
|
||||
$\" -> do_string(ScanState, []);
|
||||
$- -> {ok, ?T_MINUS};
|
||||
_ ->
|
||||
if $0 =< Ch, Ch =< $9 -> do_number(ScanState, Ch);
|
||||
($A =< Ch andalso Ch =< $Z) orelse
|
||||
($a =< Ch andalso Ch =< $z) orelse
|
||||
Ch =:= $. orelse
|
||||
Ch =:= $$ orelse
|
||||
Ch =:= $_ -> do_symbol(ScanState, [Ch]);
|
||||
true -> badchar(ScanState, Ch, "")
|
||||
end
|
||||
end
|
||||
end.
|
||||
|
||||
%% Scan after seeing '#'.
|
||||
do_line_comment(ScanState) ->
|
||||
case scan_state:fgetc(ScanState) of
|
||||
{error, _Reason} = Error -> Error;
|
||||
eof -> badchar(ScanState, eof, "in line comment");
|
||||
{ok, $\n} -> {ok, ?T_NEWLINE};
|
||||
{ok, _Ch} -> do_line_comment(ScanState)
|
||||
end.
|
||||
|
||||
%% Scan after seeing '/'.
|
||||
do_slash(ScanState) ->
|
||||
case scan_state:fgetc(ScanState) of
|
||||
{error, _Reason} = Error -> Error;
|
||||
{ok, $*} -> do_c_comment(ScanState, false);
|
||||
{ok, Ch} ->
|
||||
scan_state:ungetc(Ch, ScanState),
|
||||
badchar(ScanState, Ch, "after /"); % TODO: NYI: T_DIV
|
||||
eof ->
|
||||
badchar(ScanState, eof, "after /")
|
||||
end.
|
||||
|
||||
%% Scan after seeing '/* ...'.
|
||||
do_c_comment(ScanState, PrevWasStar) ->
|
||||
case scan_state:fgetc(ScanState) of
|
||||
{error, _Reason} = Error -> Error;
|
||||
eof -> badchar(ScanState, eof, "in /*...*/ comment");
|
||||
{ok, $*} -> do_c_comment(ScanState, true);
|
||||
{ok, $/} when PrevWasStar -> token(ScanState);
|
||||
{ok, _Ch} -> do_c_comment(ScanState, false)
|
||||
end.
|
||||
|
||||
%% Scan after seeing '"'.
|
||||
do_string(ScanState, Chars) ->
|
||||
case scan_state:fgetc(ScanState) of
|
||||
{error, _Reason} = Error -> Error;
|
||||
eof -> badchar(ScanState, eof, "in string literal");
|
||||
{ok, $\n} -> badchar(ScanState, $\n, "in string literal");
|
||||
{ok, $\"} -> {ok, {?T_STRING, lists:reverse(Chars)}};
|
||||
{ok, $\\} ->
|
||||
case do_escape(ScanState) of
|
||||
{error, _Reason} = Error -> Error;
|
||||
{ok, Ch} -> do_string(ScanState, [Ch | Chars])
|
||||
end;
|
||||
{ok, Ch} -> do_string(ScanState, [Ch | Chars])
|
||||
end.
|
||||
|
||||
%% Scan after seeing '\' in a string literal.
|
||||
do_escape(ScanState) ->
|
||||
case scan_state:fgetc(ScanState) of
|
||||
{error, _Reason} = Error -> Error;
|
||||
eof -> badchar(ScanState, eof, "in \\ character escape");
|
||||
{ok, Ch} ->
|
||||
case Ch of
|
||||
$n -> {ok, $\n};
|
||||
$t -> {ok, $\t};
|
||||
$f -> {ok, $\f};
|
||||
$r -> {ok, $\r};
|
||||
$b -> {ok, $\b};
|
||||
$\\ -> {ok, $\\};
|
||||
$\' -> {ok, $\'};
|
||||
$\" -> {ok, $\"};
|
||||
_ ->
|
||||
if $0 =< Ch, Ch =< $7 -> do_octal_escape(ScanState, Ch - $0, 2);
|
||||
true -> badchar(ScanState, Ch, "in \\ character escape")
|
||||
end
|
||||
end
|
||||
end.
|
||||
|
||||
do_octal_escape(_ScanState, Val, 0) -> {ok, Val};
|
||||
do_octal_escape(ScanState, Val, N) ->
|
||||
case scan_state:fgetc(ScanState) of
|
||||
{error, _Reason} = Error -> Error;
|
||||
eof -> badchar(ScanState, eof, "in \\ character escape");
|
||||
{ok, Ch} ->
|
||||
if $0 =< Ch, Ch =< $t -> do_octal_escape(ScanState, Val * 8 + (Ch - $0), N - 1);
|
||||
true ->
|
||||
case scan_state:ungetc(Ch, ScanState) of
|
||||
{error, _Reason} = Error -> Error;
|
||||
ok -> {ok, Val}
|
||||
end
|
||||
end
|
||||
end.
|
||||
|
||||
do_symbol(ScanState, Chars) ->
|
||||
case scan_state:fgetc(ScanState) of
|
||||
{error, _Reason} = Error -> Error;
|
||||
eof -> do_symbol(lists:reverse(Chars));
|
||||
{ok, Ch} ->
|
||||
if ($A =< Ch andalso Ch =< $Z) orelse
|
||||
($a =< Ch andalso Ch =< $z) orelse
|
||||
($0 =< Ch andalso Ch =< $9) orelse
|
||||
Ch =:= $. orelse
|
||||
Ch =:= $$ orelse
|
||||
Ch =:= $_ -> do_symbol(ScanState, [Ch | Chars]);
|
||||
true ->
|
||||
case scan_state:ungetc(Ch, ScanState) of
|
||||
{error, _Reason} = Error -> Error;
|
||||
ok -> do_symbol(lists:reverse(Chars))
|
||||
end
|
||||
end
|
||||
end.
|
||||
|
||||
do_symbol(Chars) ->
|
||||
case Chars of
|
||||
[$.] -> {ok, ?T_DOT};
|
||||
[$. | _] -> {ok, token:from_symbol(Chars)};
|
||||
_ -> {ok, {?T_SYMBOL, Chars}}
|
||||
end.
|
||||
|
||||
do_number(ScanState, Dig0) ->
|
||||
case Dig0 of
|
||||
$0 ->
|
||||
case scan_state:fgetc(ScanState) of
|
||||
{error, _Reason} = Error -> Error;
|
||||
eof -> {ok, {?T_UINTEGER, Dig0 - $0}};
|
||||
{ok, Ch} ->
|
||||
if Ch =:= $x; Ch =:= $X ->
|
||||
%% must have hex digit after 0x
|
||||
case scan_state:fgetc(ScanState) of
|
||||
{error, _Reason} = Error -> Error;
|
||||
eof -> badchar(ScanState, eof, "after 0x in number");
|
||||
{ok, Ch} ->
|
||||
case chval(Ch) of
|
||||
ChVal when ChVal < 16 ->
|
||||
do_number(ScanState, _Base = 16, ChVal);
|
||||
_Val -> badchar(ScanState, Ch, "after 0x in number")
|
||||
end
|
||||
end;
|
||||
true ->
|
||||
case scan_state:ungetc(Ch, ScanState) of
|
||||
{error, _Reason} = Error -> Error;
|
||||
ok -> do_number(ScanState, _Base = 8, _Val = 0)
|
||||
end
|
||||
end
|
||||
end;
|
||||
_ -> do_number(ScanState, _Base = 10, _Val = Dig0 - $0)
|
||||
end.
|
||||
|
||||
do_number(ScanState, Base, Val) ->
|
||||
case scan_state:fgetc(ScanState) of
|
||||
{error, _Reason} = Error -> Error;
|
||||
eof -> {ok, {?T_UINTEGER, Val}};
|
||||
{ok, Ch} ->
|
||||
case chval(Ch) of
|
||||
ChVal when ChVal < Base ->
|
||||
do_number(ScanState, Base, Val * Base + ChVal);
|
||||
_ChVal ->
|
||||
%% TODO: check for <decimal>[bf] which is a local label reference
|
||||
case scan_state:ungetc(Ch, ScanState) of
|
||||
{error, _Reason} = Error -> Error;
|
||||
ok -> {ok, {?T_UINTEGER, Val}}
|
||||
end
|
||||
end
|
||||
end.
|
||||
|
||||
chval(Ch) ->
|
||||
if $0 =< Ch, Ch =< $9 -> Ch - $0;
|
||||
$A =< Ch, Ch =< $F -> Ch - ($A - 10);
|
||||
$a =< Ch, Ch =< $f -> Ch - ($a - 10);
|
||||
true -> 255 % signals invalid by being >= any valid base
|
||||
end.
|
||||
|
||||
badchar(ScanState, Ch, Context) ->
|
||||
ChStr = char_to_string(Ch),
|
||||
{ok, FileName} = scan_state:filename(ScanState),
|
||||
{ok, LineNr} = scan_state:linenr(ScanState),
|
||||
{error, lists:flatten(io_lib:format("~s line ~p: invalid character '~s' ~s",
|
||||
[FileName, LineNr, ChStr, Context]))}.
|
||||
|
||||
char_to_string(eof) -> "<EOF>";
|
||||
char_to_string(Ch) when $\s =< Ch, Ch =< $~ -> [$', Ch, $'];
|
||||
char_to_string(Ch) ->
|
||||
[$', $\\, $0 + ((Ch bsr 6) band 3), $0 + ((Ch bsr 3) band 7), $0 + (Ch band 7), $'].
|
||||
171
erlang/apps/as/src/scan_state.erl
Normal file
171
erlang/apps/as/src/scan_state.erl
Normal file
@ -0,0 +1,171 @@
|
||||
%%% -*- erlang-indent-level: 2 -*-
|
||||
%%%
|
||||
%%% scanner state manager for pdp10-elf as
|
||||
%%% Copyright (C) 2019 Mikael Pettersson
|
||||
%%%
|
||||
%%% This file is part of pdp10-tools.
|
||||
%%%
|
||||
%%% pdp10-tools is free software: you can redistribute it and/or modify
|
||||
%%% it under the terms of the GNU General Public License as published by
|
||||
%%% the Free Software Foundation, either version 3 of the License, or
|
||||
%%% (at your option) any later version.
|
||||
%%%
|
||||
%%% pdp10-tools is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
%%% GNU General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License
|
||||
%%% along with pdp10-tools. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
-module(scan_state).
|
||||
-behaviour(gen_server).
|
||||
|
||||
%% API
|
||||
-export([ % file I/O wrappers
|
||||
fclose/1
|
||||
, fgetc/1
|
||||
, fopen/1
|
||||
, stdin/0
|
||||
, ungetc/2
|
||||
% meta-data accessors
|
||||
, filename/1
|
||||
, linenr/1
|
||||
]).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([ init/1
|
||||
, handle_call/3
|
||||
, handle_cast/2
|
||||
, handle_info/2
|
||||
, terminate/2
|
||||
, code_change/3
|
||||
]).
|
||||
|
||||
%% The scanner state records the I/O handle, implements a one-character
|
||||
%% pushback buffer, and maintains the current line number.
|
||||
%% TODO: maintain column number too?
|
||||
-record(state,
|
||||
{ filename :: string()
|
||||
, iodev :: file:fd() | standard_io
|
||||
, ungetc :: [] | byte()
|
||||
, linenr :: pos_integer()
|
||||
}).
|
||||
|
||||
%% API -------------------------------------------------------------------------
|
||||
|
||||
-spec fclose(pid()) -> ok | {error, any()}.
|
||||
fclose(Pid) ->
|
||||
gen_server:call(Pid, fclose, infinity).
|
||||
|
||||
-spec fgetc(pid()) -> {ok, byte()} | eof | {error, any()}.
|
||||
fgetc(Pid) ->
|
||||
gen_server:call(Pid, fgetc, infinity).
|
||||
|
||||
-spec fopen(string()) -> {ok, pid()} | {error, any()}.
|
||||
fopen(File) ->
|
||||
do_fopen(File).
|
||||
|
||||
-spec stdin() -> {ok, pid()}.
|
||||
stdin() ->
|
||||
do_fopen(stdin).
|
||||
|
||||
do_fopen(File) ->
|
||||
gen_server:start(?MODULE, File, []).
|
||||
|
||||
-spec ungetc(byte(), pid()) -> ok | {error, any()}.
|
||||
ungetc(Ch, Pid) ->
|
||||
gen_server:call(Pid, {ungetc, Ch}, infinity).
|
||||
|
||||
-spec filename(pid()) -> {ok, string()}.
|
||||
filename(Pid) ->
|
||||
gen_server:call(Pid, filename, infinity).
|
||||
|
||||
-spec linenr(pid()) -> {ok, pos_integer()}.
|
||||
linenr(Pid) ->
|
||||
gen_server:call(Pid, linenr, infinity).
|
||||
|
||||
%% gen_server callbacks --------------------------------------------------------
|
||||
|
||||
init(stdin) ->
|
||||
do_init("<stdin>", standard_io);
|
||||
init(File) ->
|
||||
case file:open(File, [raw, read, read_ahead]) of
|
||||
{ok, IoDev} -> do_init(File, IoDev);
|
||||
{error, Reason} -> {stop, Reason}
|
||||
end.
|
||||
|
||||
do_init(FileName, IoDev) ->
|
||||
{ok, #state{ filename = FileName
|
||||
, iodev = IoDev
|
||||
, ungetc = []
|
||||
, linenr = 1
|
||||
}}.
|
||||
|
||||
handle_call(Req, _From, State) ->
|
||||
case Req of
|
||||
fclose ->
|
||||
handle_fclose(State);
|
||||
fgetc ->
|
||||
handle_fgetc(State);
|
||||
{ungetc, Ch} ->
|
||||
handle_ungetc(State, Ch);
|
||||
filename ->
|
||||
{reply, {ok, State#state.filename}, State};
|
||||
linenr ->
|
||||
{reply, {ok, State#state.linenr}, State};
|
||||
_ ->
|
||||
{reply, {error, {bad_request, Req}}, State}
|
||||
end.
|
||||
|
||||
handle_cast(_Req, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
handle_info(_Info, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, State) ->
|
||||
do_fclose(State).
|
||||
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
%% fclose ----------------------------------------------------------------------
|
||||
|
||||
handle_fclose(State) ->
|
||||
{stop, normal, ok, State}.
|
||||
|
||||
do_fclose(State) ->
|
||||
case State#state.iodev of
|
||||
standard_io -> ok;
|
||||
IoDev -> file:close(IoDev)
|
||||
end.
|
||||
|
||||
%% fgetc -----------------------------------------------------------------------
|
||||
|
||||
handle_fgetc(State) ->
|
||||
case State#state.ungetc of
|
||||
[] ->
|
||||
{Result, NewState} =
|
||||
case file:read(State#state.iodev, 1) of
|
||||
{ok, [Byte]} ->
|
||||
{{ok, Byte},
|
||||
case Byte of
|
||||
$\n -> State#state{linenr = State#state.linenr + 1};
|
||||
_ -> State
|
||||
end};
|
||||
eof -> {eof, State};
|
||||
{error, _Reason} = Error -> {Error, State}
|
||||
end,
|
||||
{reply, Result, NewState};
|
||||
Ch ->
|
||||
{reply, {ok, Ch}, State#state{ungetc = []}}
|
||||
end.
|
||||
|
||||
%% ungetc ----------------------------------------------------------------------
|
||||
|
||||
handle_ungetc(State, Ch) ->
|
||||
case State#state.ungetc of
|
||||
[] -> {reply, ok, State#state{ungetc = Ch}};
|
||||
_ -> {reply, {error, ungetc}, State}
|
||||
end.
|
||||
62
erlang/apps/as/src/token.erl
Normal file
62
erlang/apps/as/src/token.erl
Normal file
@ -0,0 +1,62 @@
|
||||
%%% -*- erlang-indent-level: 2 -*-
|
||||
%%%
|
||||
%%% token handling for pdp10-elf as
|
||||
%%% Copyright (C) 2013-2019 Mikael Pettersson
|
||||
%%%
|
||||
%%% This file is part of pdp10-tools.
|
||||
%%%
|
||||
%%% pdp10-tools is free software: you can redistribute it and/or modify
|
||||
%%% it under the terms of the GNU General Public License as published by
|
||||
%%% the Free Software Foundation, either version 3 of the License, or
|
||||
%%% (at your option) any later version.
|
||||
%%%
|
||||
%%% pdp10-tools is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
%%% GNU General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License
|
||||
%%% along with pdp10-tools. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
-module(token).
|
||||
|
||||
-export([ from_symbol/1
|
||||
, to_string/1
|
||||
]).
|
||||
|
||||
-include("token.hrl").
|
||||
|
||||
-spec from_symbol(string()) -> token().
|
||||
from_symbol(Name) ->
|
||||
case Name of
|
||||
".file" -> ?T_DOT_FILE;
|
||||
".globl" -> ?T_DOT_GLOBL;
|
||||
".ident" -> ?T_DOT_IDENT;
|
||||
".size" -> ?T_DOT_SIZE;
|
||||
".text" -> ?T_DOT_TEXT;
|
||||
".type" -> ?T_DOT_TYPE;
|
||||
_ -> {?T_SYMBOL, Name}
|
||||
end.
|
||||
|
||||
-spec to_string(token()) -> string().
|
||||
to_string(Token) ->
|
||||
case Token of
|
||||
?T_DOT_FILE -> ".file";
|
||||
?T_DOT_GLOBL -> ".globl";
|
||||
?T_DOT_IDENT -> ".ident";
|
||||
?T_DOT_SIZE -> ".size";
|
||||
?T_DOT_TEXT -> ".text";
|
||||
?T_DOT_TYPE -> ".type";
|
||||
{?T_SYMBOL, Name} -> io_lib:format("symbol:~s", [Name]);
|
||||
{?T_UINTEGER, Int} -> io_lib:format("uinteger:~p", [Int]);
|
||||
{?T_STRING, Str} -> io_lib:format("string:~p", [Str]); % FIXME: quoting
|
||||
?T_AT -> "@";
|
||||
?T_COLON -> ":";
|
||||
?T_COMMA -> ",";
|
||||
?T_DOT -> ".";
|
||||
?T_LPAREN -> "(";
|
||||
?T_MINUS -> "-";
|
||||
?T_RPAREN -> ")";
|
||||
?T_NEWLINE -> "<newline>";
|
||||
?T_EOF -> "<eof>"
|
||||
end.
|
||||
76
erlang/apps/as/src/token.hrl
Normal file
76
erlang/apps/as/src/token.hrl
Normal file
@ -0,0 +1,76 @@
|
||||
%%% -*- erlang-indent-level: 2 -*-
|
||||
%%%
|
||||
%%% token definitions for pdp10-elf as
|
||||
%%% Copyright (C) 2013-2019 Mikael Pettersson
|
||||
%%%
|
||||
%%% This file is part of pdp10-tools.
|
||||
%%%
|
||||
%%% pdp10-tools is free software: you can redistribute it and/or modify
|
||||
%%% it under the terms of the GNU General Public License as published by
|
||||
%%% the Free Software Foundation, either version 3 of the License, or
|
||||
%%% (at your option) any later version.
|
||||
%%%
|
||||
%%% pdp10-tools is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
%%% GNU General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License
|
||||
%%% along with pdp10-tools. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
-ifndef(TOKEN_HRL).
|
||||
-define(TOKEN_HRL, 1).
|
||||
|
||||
%% Manifest constants for tokens. These are passed around as-is for
|
||||
%% attribute-less tokens, or as {Token, Attribute} tuples for tokens
|
||||
%% with attributes (ordinary symbols, literals, errors).
|
||||
|
||||
%% reserved symbols including directives
|
||||
-define(T_DOT_FILE, 'T_DOT_FILE'). % .file
|
||||
-define(T_DOT_GLOBL, 'T_DOT_GLOBL'). % .globl
|
||||
-define(T_DOT_IDENT, 'T_DOT_IDENT'). % .ident
|
||||
-define(T_DOT_SIZE, 'T_DOT_SIZE'). % .size
|
||||
-define(T_DOT_TEXT, 'T_DOT_TEXT'). % .text
|
||||
-define(T_DOT_TYPE, 'T_DOT_TYPE'). % .type
|
||||
|
||||
%% ordinary (non-reserved, non-special non-synthetic) symbols
|
||||
-define(T_SYMBOL, 'T_SYMBOL'). % pushj, foo, .Lbar
|
||||
|
||||
%% literals
|
||||
-define(T_UINTEGER, 'T_UINTEGER'). % 017
|
||||
-define(T_STRING, 'T_STRING'). % "foo"
|
||||
|
||||
%% special symbols including operators and separators
|
||||
-define(T_AT, 'T_AT'). % @
|
||||
-define(T_COLON, 'T_COLON'). % :
|
||||
-define(T_COMMA, 'T_COMMA'). % ,
|
||||
-define(T_DOT, 'T_DOT'). % .
|
||||
-define(T_LPAREN, 'T_LPAREN'). % (
|
||||
-define(T_MINUS, 'T_MINUS'). % -
|
||||
-define(T_RPAREN, 'T_RPAREN'). % )
|
||||
|
||||
%% synthetic symbols
|
||||
-define(T_NEWLINE, 'T_NEWLINE'). % <end-of-line>
|
||||
-define(T_EOF, 'T_EOF'). % <end-of-file>
|
||||
|
||||
-type token() :: ?T_DOT_FILE
|
||||
| ?T_DOT_GLOBL
|
||||
| ?T_DOT_IDENT
|
||||
| ?T_DOT_SIZE
|
||||
| ?T_DOT_TEXT
|
||||
| ?T_DOT_TYPE
|
||||
| {?T_SYMBOL, string()}
|
||||
| {?T_UINTEGER, non_neg_integer()}
|
||||
| {?T_STRING, string()}
|
||||
| ?T_AT
|
||||
| ?T_COLON
|
||||
| ?T_COMMA
|
||||
| ?T_DOT
|
||||
| ?T_LPAREN
|
||||
| ?T_MINUS
|
||||
| ?T_RPAREN
|
||||
| ?T_NEWLINE
|
||||
| ?T_EOF
|
||||
.
|
||||
|
||||
-endif. % TOKEN_HRL
|
||||
50
erlang/apps/as/src/tunit.erl
Normal file
50
erlang/apps/as/src/tunit.erl
Normal file
@ -0,0 +1,50 @@
|
||||
%%% -*- erlang-indent-level: 2 -*-
|
||||
%%%
|
||||
%%% translation unit handling for pdp10-elf as.
|
||||
%%% Copyright (C) 2013-2019 Mikael Pettersson
|
||||
%%%
|
||||
%%% This file is part of pdp10-tools.
|
||||
%%%
|
||||
%%% pdp10-tools is free software: you can redistribute it and/or modify
|
||||
%%% it under the terms of the GNU General Public License as published by
|
||||
%%% the Free Software Foundation, either version 3 of the License, or
|
||||
%%% (at your option) any later version.
|
||||
%%%
|
||||
%%% pdp10-tools is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
%%% GNU General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License
|
||||
%%% along with pdp10-tools. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
-module(tunit).
|
||||
|
||||
-export([ new/0
|
||||
, get_section/2
|
||||
, put_section/2
|
||||
, get_symbol/2
|
||||
, put_symbol/2
|
||||
]).
|
||||
|
||||
-include("tunit.hrl").
|
||||
|
||||
%% API -------------------------------------------------------------------------
|
||||
|
||||
new() ->
|
||||
#tunit{ sections = #{}
|
||||
, cursect = false
|
||||
, symbols = #{}
|
||||
}.
|
||||
|
||||
get_section(#tunit{sections = Sections}, Name) ->
|
||||
maps:get(Name, Sections, false).
|
||||
|
||||
put_section(Tunit = #tunit{sections = Sections}, Section) ->
|
||||
Tunit#tunit{sections = maps:put(Section#section.name, Section, Sections)}.
|
||||
|
||||
get_symbol(#tunit{symbols = Symbols}, Name) ->
|
||||
maps:get(Name, Symbols, false).
|
||||
|
||||
put_symbol(Tunit = #tunit{symbols = Symbols}, Symbol) ->
|
||||
Tunit#tunit{symbols = maps:put(Symbol#symbol.name, Symbol, Symbols)}.
|
||||
116
erlang/apps/as/src/tunit.hrl
Normal file
116
erlang/apps/as/src/tunit.hrl
Normal file
@ -0,0 +1,116 @@
|
||||
%%% -*- erlang-indent-level: 2 -*-
|
||||
%%%
|
||||
%%% translation unit declarations for pdp10-elf as.
|
||||
%%% Copyright (C) 2013-2019 Mikael Pettersson
|
||||
%%%
|
||||
%%% This file is part of pdp10-tools.
|
||||
%%%
|
||||
%%% pdp10-tools is free software: you can redistribute it and/or modify
|
||||
%%% it under the terms of the GNU General Public License as published by
|
||||
%%% the Free Software Foundation, either version 3 of the License, or
|
||||
%%% (at your option) any later version.
|
||||
%%%
|
||||
%%% pdp10-tools is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
%%% GNU General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License
|
||||
%%% along with pdp10-tools. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
-ifndef(TUNIT_HRL).
|
||||
-define(TUNIT_HRL, 1).
|
||||
|
||||
%% A directive, label, or instruction is parsed to a statement, which is
|
||||
%% either interpreted immediately or appended to the representation of the
|
||||
%% current section.
|
||||
|
||||
%% .file "foo.c"
|
||||
-record(s_dot_file, {string :: string()}).
|
||||
|
||||
%% .globl foo
|
||||
-record(s_dot_globl, {name :: string()}).
|
||||
|
||||
%% .ident "..."
|
||||
-record(s_dot_ident, {string :: string()}).
|
||||
|
||||
%% .size foo,.-foo (TODO: extend)
|
||||
-record(s_dot_size, {name :: string()}).
|
||||
|
||||
%% .text
|
||||
-record(s_dot_text, {}).
|
||||
|
||||
%% .type foo,@function (TODO: extend)
|
||||
-record(s_dot_type, {name :: string()}).
|
||||
|
||||
%% foo:
|
||||
-record(s_label, {name :: string()}).
|
||||
|
||||
%% opcode accumulator,@address(index)
|
||||
-record(s_insn,
|
||||
{ high13 :: 0..((1 bsl 13) - 1)
|
||||
, at :: boolean()
|
||||
, address :: non_neg_integer() % TODO: relocatable expr
|
||||
, index :: 0..((1 bsl 4) - 1)
|
||||
}).
|
||||
|
||||
-type stmt() :: #s_dot_file{}
|
||||
| #s_dot_globl{}
|
||||
| #s_dot_ident{}
|
||||
| #s_dot_size{}
|
||||
| #s_dot_text{}
|
||||
| #s_dot_type{}
|
||||
| #s_label{}
|
||||
| #s_insn{}
|
||||
.
|
||||
|
||||
%% After the assembly phase sections contain raw image data.
|
||||
%% Image data is reduced to sequences of "target bytes".
|
||||
%% We do not use Erlang binaries here to avoid complicating
|
||||
%% mapping from octet-based hosts to nonet-based targets.
|
||||
|
||||
-type tbyte() :: 0..511. % may contain octets or nonets
|
||||
-type image() :: tbyte() | [image()].
|
||||
|
||||
%% Sections accumulate code or data, and define symbols.
|
||||
|
||||
-record(section,
|
||||
{ name :: string()
|
||||
, data :: {stmts, [stmt()]} % before assembly, in reverse
|
||||
| {image, image()} % after assembly
|
||||
, dot :: non_neg_integer()
|
||||
, shndx :: non_neg_integer() % assigned during output
|
||||
%% FIXME: should contain an #elf36_Shdr{} here instead
|
||||
, sh_name :: non_neg_integer() % assigned during output
|
||||
, sh_type :: non_neg_integer()
|
||||
, sh_offset :: non_neg_integer() % assigned during output
|
||||
, sh_flags :: non_neg_integer()
|
||||
, sh_link :: non_neg_integer() % assigned during output
|
||||
, sh_addralign :: non_neg_integer()
|
||||
, sh_entsize :: non_neg_integer()
|
||||
}).
|
||||
|
||||
%% Symbol values.
|
||||
|
||||
-record(symbol,
|
||||
{ name :: string()
|
||||
, section :: false | string() % false if UNDEF or ABS
|
||||
%% FIXME: should contain an #elf36_Sym{} here instead
|
||||
, st_value :: false | non_neg_integer()
|
||||
, st_size :: false | non_neg_integer()
|
||||
, st_info :: byte()
|
||||
, st_name :: non_neg_integer() % assigned during output
|
||||
, st_shndx :: non_neg_integer() % assigned during output
|
||||
}).
|
||||
|
||||
%% The translation unit object is the top-level container for the
|
||||
%% representation of the sections, other information collected from
|
||||
%% the input, and information synthesized during assembly.
|
||||
|
||||
-record(tunit,
|
||||
{ sections :: #{string() => #section{}}
|
||||
, cursect :: string()
|
||||
, symbols :: #{string() => #symbol{}}
|
||||
}).
|
||||
|
||||
-endif. % TUNIT_HRL
|
||||
@ -35,6 +35,7 @@
|
||||
[
|
||||
{'8to9', [{escript_main_app, '8to9'}]}
|
||||
, {ar, [{escript_main_app, ar}]}
|
||||
, {as, [{escript_main_app, as}]}
|
||||
, {nm, [{escript_main_app, nm}]}
|
||||
, {od, [{escript_main_app, od}]}
|
||||
]}.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user