mirror of
https://github.com/mikpe/pdp10-tools.git
synced 2026-03-10 20:53:47 +00:00
elf2boot: generate output one full word at a time
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
%%% -*- erlang-indent-level: 2 -*-
|
||||
%%%
|
||||
%%% Converts pdp10-elf executables to KLH10-bootable files
|
||||
%%% Copyright (C) 2023 Mikael Pettersson
|
||||
%%% Copyright (C) 2023-2025 Mikael Pettersson
|
||||
%%%
|
||||
%%% This file is part of pdp10-tools.
|
||||
%%%
|
||||
@@ -82,7 +82,7 @@ scan_option({$o, Output}, Opts) -> % -o / --output
|
||||
-type word() :: non_neg_integer().
|
||||
|
||||
-record(frag,
|
||||
{ nrbytes :: non_neg_integer()
|
||||
{ nrwords :: non_neg_integer()
|
||||
, src :: pagenr() % offset in input ELF file
|
||||
| [word()] % stub
|
||||
, mem :: pagenr() % offset in physical memory
|
||||
@@ -125,7 +125,7 @@ elf2boot(Opts, InFile, OutFile) ->
|
||||
%% - Consequently the entry vector refers to an XJRSTF which performs a long
|
||||
%% jump to the real entry point.
|
||||
|
||||
-define(MAX_GROUP_NRBYTES, (512*512*4)). % 512 pages x 512 words/page x 4 bytes/word
|
||||
-define(MAX_GROUP_NRWORDS, (512*512)). % 512 pages x 512 words/page
|
||||
|
||||
-record(group,
|
||||
{ frag :: #frag{}
|
||||
@@ -136,12 +136,12 @@ write_boot(Opts, InFP, Entry, Frags, OutFile) ->
|
||||
#frag{mem = EntryPageNr} = EntryFrag = entry_frag(Entry),
|
||||
Groups = groups([EntryFrag | Frags]),
|
||||
print_groups(Opts, Groups),
|
||||
case pdp10_stdio:fopen(OutFile, [raw, write, delayed_write]) of
|
||||
case outfp_fopen(OutFile) of
|
||||
{ok, OutFP} ->
|
||||
try
|
||||
write_boot_1(Opts, InFP, EntryPageNr, Groups, OutFP)
|
||||
after
|
||||
pdp10_stdio:fclose(OutFP)
|
||||
outfp_fclose(OutFP)
|
||||
end;
|
||||
{error, _Reason} = Error -> Error
|
||||
end.
|
||||
@@ -149,8 +149,8 @@ write_boot(Opts, InFP, Entry, Frags, OutFile) ->
|
||||
write_boot_1(Opts, InFP, EntryPageNr, Groups, OutFP) ->
|
||||
NrGroups = length(Groups),
|
||||
true = NrGroups =< 512, % assert
|
||||
HdrNrBytes = (1 + NrGroups * 2 + 3 + 1) * 4,
|
||||
HdrNrPages = nrbytes_to_nrpages(HdrNrBytes),
|
||||
HdrNrWords = 1 + NrGroups * 2 + 3 + 1,
|
||||
HdrNrPages = nrwords_to_nrpages(HdrNrWords),
|
||||
HdrWords = build_decexe_hdr(Groups, EntryPageNr, HdrNrPages),
|
||||
print_header(Opts, HdrWords),
|
||||
ok = write_words(HdrWords, OutFP),
|
||||
@@ -164,45 +164,101 @@ write_groups([Group | Groups], HdrNrPages, OutFP, InFP) ->
|
||||
end.
|
||||
|
||||
write_group(Group, HdrNrPages, OutFP, InFP) ->
|
||||
#group{frag = #frag{nrbytes = NrBytes, src = Src}, dst = DstPageNr} = Group,
|
||||
case padto(OutFP, nrpages_to_nrbytes(HdrNrPages + DstPageNr)) of
|
||||
#group{frag = #frag{nrwords = NrWords, src = Src}, dst = DstPageNr} = Group,
|
||||
case padto(OutFP, HdrNrPages + DstPageNr) of
|
||||
ok ->
|
||||
case write_src(NrBytes, Src, OutFP, InFP) of
|
||||
case write_src(NrWords, Src, OutFP, InFP) of
|
||||
ok ->
|
||||
NrPages = nrbytes_to_nrpages(NrBytes),
|
||||
padto(OutFP, nrpages_to_nrbytes(HdrNrPages + DstPageNr + NrPages));
|
||||
NrPages = nrwords_to_nrpages(NrWords),
|
||||
padto(OutFP, HdrNrPages + DstPageNr + NrPages);
|
||||
{error, _Reason} = Error -> Error
|
||||
end;
|
||||
{error, _Reason} = Error -> Error
|
||||
end.
|
||||
|
||||
write_src(NrBytes, Src, DstFP, SrcFP) ->
|
||||
write_src(NrWords, Src, DstFP, SrcFP) ->
|
||||
case Src of
|
||||
SrcPageNr when is_integer(SrcPageNr) ->
|
||||
archive:iocpy(DstFP, SrcFP, nrpages_to_nrbytes(SrcPageNr), NrBytes); % TODO: make non-archive-specific
|
||||
iocpy(DstFP, SrcFP, SrcPageNr, NrWords);
|
||||
Words when is_list(Words) ->
|
||||
write_words(Words, DstFP)
|
||||
end.
|
||||
|
||||
padto(DstFP, DstOffset) ->
|
||||
CurOffset = pdp10_stdio:ftell(DstFP),
|
||||
true = CurOffset =< DstOffset, % assert
|
||||
case CurOffset =:= DstOffset of
|
||||
padto(DstFP, DstPageNr) ->
|
||||
DstWordOffset = nrpages_to_nrwords(DstPageNr),
|
||||
CurWordOffset = outfp_ftellw(DstFP),
|
||||
true = CurWordOffset =< DstWordOffset, % assert
|
||||
case CurWordOffset =:= DstWordOffset of
|
||||
true -> ok;
|
||||
false ->
|
||||
case pdp10_stdio:fseek(DstFP, {bof, DstOffset - 1}) of
|
||||
ok -> pdp10_stdio:fputc(0, DstFP);
|
||||
case outfp_fseekw(DstFP, DstWordOffset - 1) of
|
||||
ok -> outfp_fputw(0, DstFP);
|
||||
{error, _Reason} = Error -> Error
|
||||
end
|
||||
end.
|
||||
|
||||
write_words([], _DstFP) -> ok;
|
||||
write_words([Word | Words], DstFP) ->
|
||||
case pdp10_stdio:fputs(pdp10_extint:uint36_to_ext(Word), DstFP) of
|
||||
case outfp_fputw(Word, DstFP) of
|
||||
ok -> write_words(Words, DstFP);
|
||||
{error, _Reason} = Error -> Error
|
||||
end.
|
||||
|
||||
%% Copy data between I/O devices ===============================================
|
||||
|
||||
iocpy(DstFP, SrcFP, SrcPageNr, NrWords) ->
|
||||
SrcWordOffset = nrpages_to_nrwords(SrcPageNr),
|
||||
case infp_fseekw(SrcFP, SrcWordOffset) of
|
||||
ok -> iocpy(DstFP, SrcFP, NrWords);
|
||||
{error, _Reason} = Error -> Error
|
||||
end.
|
||||
|
||||
iocpy(_DstFP, _SrcFP, _NrWords = 0) -> ok;
|
||||
iocpy(DstFP, SrcFP, NrWords) ->
|
||||
case infp_fgetw(SrcFP) of
|
||||
{ok, Word} ->
|
||||
case outfp_fputw(Word, DstFP) of
|
||||
ok -> iocpy(DstFP, SrcFP, NrWords - 1);
|
||||
{error, _Reason} = Error -> Error
|
||||
end;
|
||||
{error, _Reason} = Error -> Error;
|
||||
eof -> {error, eof}
|
||||
end.
|
||||
|
||||
%% Word-oriented input file abstraction ========================================
|
||||
|
||||
infp_fseekw(InFP, WordOffset) ->
|
||||
pdp10_stdio:fseek(InFP, {bof, WordOffset*4}).
|
||||
|
||||
infp_fgetw(SrcFP) ->
|
||||
infp_fgetw(_NrBytes = 4, SrcFP, _Acc = 0).
|
||||
|
||||
infp_fgetw(0, _SrcFP, Word) -> {ok, Word};
|
||||
infp_fgetw(N, SrcFP, Acc) ->
|
||||
case pdp10_stdio:fgetc(SrcFP) of
|
||||
{ok, Nonet} -> infp_fgetw(N - 1, SrcFP, (Acc bsl 9) bor Nonet);
|
||||
Else -> Else
|
||||
end.
|
||||
|
||||
%% Word-oriented output file abstraction =======================================
|
||||
|
||||
outfp_fopen(FileName) ->
|
||||
pdp10_stdio:fopen(FileName, [raw, write, delayed_write]).
|
||||
|
||||
outfp_fclose(OutFP) ->
|
||||
pdp10_stdio:fclose(OutFP).
|
||||
|
||||
outfp_ftellw(OutFP) ->
|
||||
ByteOffset = pdp10_stdio:ftell(OutFP),
|
||||
0 = ByteOffset band 3, % assert
|
||||
ByteOffset bsr 2.
|
||||
|
||||
outfp_fseekw(OutFP, WordOffset) ->
|
||||
pdp10_stdio:fseek(OutFP, {bof, WordOffset*4}).
|
||||
|
||||
outfp_fputw(Word, OutFP) ->
|
||||
pdp10_stdio:fputs(pdp10_extint:uint36_to_ext(Word), OutFP).
|
||||
|
||||
%% Optional debugging output ===================================================
|
||||
|
||||
print_entry(Opts, Entry, Frags) ->
|
||||
@@ -224,8 +280,8 @@ print_group(Group) ->
|
||||
print_frag(Frag).
|
||||
|
||||
print_frag(Frag) ->
|
||||
#frag{nrbytes = NrBytes, src = Src, mem = MemPageNr} = Frag,
|
||||
io:format("\tnrbytes ~p\n", [NrBytes]),
|
||||
#frag{nrwords = NrWords, src = Src, mem = MemPageNr} = Frag,
|
||||
io:format("\tnrwords ~p\n", [NrWords]),
|
||||
io:format("\tmem ~s\n", [format_lh(MemPageNr bsl 9)]),
|
||||
case Src of
|
||||
SrcPageNr when is_integer(SrcPageNr) ->
|
||||
@@ -275,9 +331,9 @@ build_decexe_dir(Groups = [_|_], HdrNrPages) ->
|
||||
].
|
||||
|
||||
build_decexe_dir_entry(Group, HdrNrPages) ->
|
||||
#group{frag = #frag{nrbytes = NrBytes, mem = MemPageNr}, dst = DstPageNr} = Group,
|
||||
true = NrBytes > 0 andalso NrBytes =< ?MAX_GROUP_NRBYTES, % assert
|
||||
NrPages = nrbytes_to_nrpages(NrBytes),
|
||||
#group{frag = #frag{nrwords = NrWords, mem = MemPageNr}, dst = DstPageNr} = Group,
|
||||
true = NrWords > 0 andalso NrWords =< ?MAX_GROUP_NRWORDS, % assert
|
||||
NrPages = nrwords_to_nrpages(NrWords),
|
||||
[ word927(0, HdrNrPages + DstPageNr)
|
||||
, word927(NrPages - 1, MemPageNr)
|
||||
].
|
||||
@@ -306,25 +362,25 @@ groups([Frag | Frags], PageNr, Groups) ->
|
||||
groups(Frag, Frags, PageNr, Groups).
|
||||
|
||||
groups(Frag, Frags, PageNr, Groups) ->
|
||||
NrBytes = Frag#frag.nrbytes,
|
||||
case NrBytes > ?MAX_GROUP_NRBYTES of
|
||||
NrWords = Frag#frag.nrwords,
|
||||
case NrWords > ?MAX_GROUP_NRWORDS of
|
||||
true ->
|
||||
NrPages = nrbytes_to_nrpages(?MAX_GROUP_NRBYTES),
|
||||
Group = #group{frag = Frag#frag{nrbytes = ?MAX_GROUP_NRBYTES}, dst = PageNr},
|
||||
NewFrag = Frag#frag{ nrbytes = NrBytes - ?MAX_GROUP_NRBYTES
|
||||
NrPages = nrwords_to_nrpages(?MAX_GROUP_NRWORDS),
|
||||
Group = #group{frag = Frag#frag{nrwords = ?MAX_GROUP_NRWORDS}, dst = PageNr},
|
||||
NewFrag = Frag#frag{ nrwords = NrWords - ?MAX_GROUP_NRWORDS
|
||||
, src = (Frag#frag.src) + NrPages
|
||||
, mem = (Frag#frag.mem) + NrPages
|
||||
},
|
||||
groups(NewFrag, Frags, PageNr + NrPages, [Group | Groups]);
|
||||
false ->
|
||||
NewGroups =
|
||||
case NrBytes of
|
||||
case NrWords of
|
||||
0 -> Groups;
|
||||
_ ->
|
||||
Group = #group{frag = Frag, dst = PageNr},
|
||||
[Group | Groups]
|
||||
end,
|
||||
NrPages = nrbytes_to_nrpages(NrBytes),
|
||||
NrPages = nrwords_to_nrpages(NrWords),
|
||||
groups(Frags, PageNr + NrPages, NewGroups)
|
||||
end.
|
||||
|
||||
@@ -347,7 +403,7 @@ entry_frag(Entry) ->
|
||||
, 8#000000_000000 % 0,,001003 0 ; flag-PC double-word: program flags
|
||||
, EntryPC % 0,,001004 EntryPC ; flag-PC double-word: PC
|
||||
],
|
||||
#frag{nrbytes = 4 * length(Src), src = Src, mem = MemPageNr}.
|
||||
#frag{nrwords = length(Src), src = Src, mem = MemPageNr}.
|
||||
|
||||
%% Reading ELF =================================================================
|
||||
|
||||
@@ -402,7 +458,7 @@ frag(Phdr) ->
|
||||
MemSz >= FileSz andalso
|
||||
no_excess_flags(Flags)) of
|
||||
true ->
|
||||
#frag{nrbytes = FileSz, src = nrbytes_to_nrpages(Offset), mem = nrbytes_to_nrpages(VAddr)};
|
||||
#frag{nrwords = nrbytes_to_nrwords(FileSz), src = nrbytes_to_nrpages(Offset), mem = nrbytes_to_nrpages(VAddr)};
|
||||
false -> error
|
||||
end;
|
||||
_ -> error
|
||||
@@ -413,6 +469,8 @@ no_excess_flags(Flags) ->
|
||||
|
||||
%% Operations on bytes and pages ===============================================
|
||||
|
||||
-define(LOG2_NR_WORDS_PER_PAGE, 9). % 512 words per page
|
||||
-define(LOG2_NR_BYTES_PER_WORD, 2). % 4 bytes (nonets) per word
|
||||
-define(LOG2_NR_BYTES_PER_PAGE, (2 + 9)). % 4 bytes per word, 512 words per page
|
||||
|
||||
is_page_aligned(Address) ->
|
||||
@@ -421,8 +479,14 @@ is_page_aligned(Address) ->
|
||||
nrbytes_to_nrpages(NrBytes) ->
|
||||
(NrBytes + ((1 bsl ?LOG2_NR_BYTES_PER_PAGE) - 1)) bsr ?LOG2_NR_BYTES_PER_PAGE.
|
||||
|
||||
nrpages_to_nrbytes(NrPages) ->
|
||||
NrPages bsl ?LOG2_NR_BYTES_PER_PAGE.
|
||||
nrpages_to_nrwords(NrPages) ->
|
||||
NrPages bsl ?LOG2_NR_WORDS_PER_PAGE.
|
||||
|
||||
nrbytes_to_nrwords(NrBytes) ->
|
||||
(NrBytes + ((1 bsl ?LOG2_NR_BYTES_PER_WORD) - 1)) bsr ?LOG2_NR_BYTES_PER_WORD.
|
||||
|
||||
nrwords_to_nrpages(NrWords) ->
|
||||
(NrWords + ((1 bsl ?LOG2_NR_WORDS_PER_PAGE) - 1)) bsr ?LOG2_NR_WORDS_PER_PAGE.
|
||||
|
||||
%% Error Formatting ============================================================
|
||||
|
||||
|
||||
Reference in New Issue
Block a user