From 504553aee69bb20115c2ecba0f972f977b46ccc0 Mon Sep 17 00:00:00 2001 From: bebbo Date: Sun, 6 Sep 2020 22:34:10 +0200 Subject: [PATCH 01/56] add locale --- amitools/data/fd/locale_lib.fd | 42 +++++++++ amitools/vamos/lib/LibList.py | 2 + amitools/vamos/lib/LocaleLibrary.py | 133 ++++++++++++++++++++++++++++ amitools/vamos/lib/TimerDevice.py | 16 +++- musashi/m68kfpu.c | 16 ++++ 5 files changed, 208 insertions(+), 1 deletion(-) create mode 100644 amitools/data/fd/locale_lib.fd create mode 100644 amitools/vamos/lib/LocaleLibrary.py diff --git a/amitools/data/fd/locale_lib.fd b/amitools/data/fd/locale_lib.fd new file mode 100644 index 00000000..11b34fad --- /dev/null +++ b/amitools/data/fd/locale_lib.fd @@ -0,0 +1,42 @@ +##base _LocaleBase +##bias 30 +##public +*--- functions in V38 or higher (Release 2.1) --- +##private +localePrivate1()() +##public +CloseCatalog(catalog)(a0) +CloseLocale(locale)(a0) +ConvToLower(locale,character)(a0,d0) +ConvToUpper(locale,character)(a0,d0) +FormatDate(locale,fmtTemplate,date,putCharFunc)(a0/a1/a2/a3) +FormatString(locale,fmtTemplate,dataStream,putCharFunc)(a0/a1/a2/a3) +GetCatalogStr(catalog,stringNum,defaultString)(a0,d0/a1) +GetLocaleStr(locale,stringNum)(a0,d0) +IsAlNum(locale,character)(a0,d0) +IsAlpha(locale,character)(a0,d0) +IsCntrl(locale,character)(a0,d0) +IsDigit(locale,character)(a0,d0) +IsGraph(locale,character)(a0,d0) +IsLower(locale,character)(a0,d0) +IsPrint(locale,character)(a0,d0) +IsPunct(locale,character)(a0,d0) +IsSpace(locale,character)(a0,d0) +IsUpper(locale,character)(a0,d0) +IsXDigit(locale,character)(a0,d0) +OpenCatalogA(locale,name,tags)(a0/a1/a2) +OpenLocale(name)(a0) +ParseDate(locale,date,fmtTemplate,getCharFunc)(a0/a1/a2/a3) +##private +localePrivate2()() +##public +StrConvert(locale,string,buffer,bufferSize,type)(a0/a1/a2,d0/d1) +StrnCmp(locale,string1,string2,length,type)(a0/a1/a2,d0/d1) +##private +localePrivate3()() +localePrivate4()() +localePrivate5()() +localePrivate6()() +localePrivate7()() +localePrivate8()() +##end diff --git a/amitools/vamos/lib/LibList.py b/amitools/vamos/lib/LibList.py index ad4e99c5..5289ded3 100644 --- a/amitools/vamos/lib/LibList.py +++ b/amitools/vamos/lib/LibList.py @@ -1,6 +1,7 @@ from .DosLibrary import DosLibrary from .ExecLibrary import ExecLibrary from .IntuitionLibrary import IntuitionLibrary +from .LocaleLibrary import LocaleLibrary from .MathFFPLibrary import MathFFPLibrary from .MathIEEEDoubBasLibrary import MathIEEEDoubBasLibrary from .MathIEEEDoubTransLibrary import MathIEEEDoubTransLibrary @@ -16,6 +17,7 @@ 'dos.library' : DosLibrary, 'exec.library' : ExecLibrary, 'intuition.library' : IntuitionLibrary, + 'locale.library' : LocaleLibrary, 'mathffp.library' : MathFFPLibrary, 'mathieeedoubbas.library' : MathIEEEDoubBasLibrary, 'mathieeedoubtrans.library' : MathIEEEDoubTransLibrary, diff --git a/amitools/vamos/lib/LocaleLibrary.py b/amitools/vamos/lib/LocaleLibrary.py new file mode 100644 index 00000000..37d6196a --- /dev/null +++ b/amitools/vamos/lib/LocaleLibrary.py @@ -0,0 +1,133 @@ +from amitools.vamos.machine.regs import * +from amitools.vamos.libcore import LibImpl +from amitools.vamos.lib.util.TagList import * +from amitools.vamos.lib.util.AmiDate import * +from amitools.vamos.log import * + +import string + +class LocaleLibrary(LibImpl): + def OpenLocale(self, ctx): + name = ctx.cpu.r_reg(REG_A0) + # dummy + return 1 + + def CloseLocale(self, ctx): + locale = ctx.cpu.r_reg(REG_A0) + # dummy - accept all + + def IsUpper(self, ctx): + locale = ctx.cpu.r_reg(REG_A0) + character = ctx.cpu.r_reg(REG_D0) + return chr(character) in string.uppercase + + def IsLower(self, ctx): + locale = ctx.cpu.r_reg(REG_A0) + character = ctx.cpu.r_reg(REG_D0) + return chr(character) in string.lowercase + + def IsDigit(self, ctx): + locale = ctx.cpu.r_reg(REG_A0) + character = ctx.cpu.r_reg(REG_D0) + return chr(character) in string.digits + + def IsPrint(self, ctx): + locale = ctx.cpu.r_reg(REG_A0) + character = ctx.cpu.r_reg(REG_D0) + return chr(character) in string.printable + + def IsCntrl(self, ctx): + locale = ctx.cpu.r_reg(REG_A0) + character = ctx.cpu.r_reg(REG_D0) + #maybe not ok... + return character < 32 and not chr(character) in string.printable + + def IsXDigit(self, ctx): + locale = ctx.cpu.r_reg(REG_A0) + character = ctx.cpu.r_reg(REG_D0) + return chr(character) in string.hexdigits + + def IsSpace(self, ctx): + locale = ctx.cpu.r_reg(REG_A0) + character = ctx.cpu.r_reg(REG_D0) + return chr(character) in string.whitespace + + def IsPunct(self, ctx): + locale = ctx.cpu.r_reg(REG_A0) + character = ctx.cpu.r_reg(REG_D0) + return chr(character) in string.punctuation + + def IsGraph(self, ctx): + locale = ctx.cpu.r_reg(REG_A0) + character = ctx.cpu.r_reg(REG_D0) + return chr(character) in string.printable and not chr(character) in string.whitespace + +""" + def CloseCatalog(self, ctx): + catalog = ctx.cpu.r_reg(REG_A0) + print "not implemented: CloseCatalog" + + def ConvToLower(self, ctx): + locale = ctx.cpu.r_reg(REG_A0) + character = ctx.cpu.r_reg(REG_D0) + print "not implemented: ConvToLower" + + def ConvToUpper(self, ctx): + locale = ctx.cpu.r_reg(REG_A0) + character = ctx.cpu.r_reg(REG_D0) + print "not implemented: ConvToUpper" + + def FormatDate(self, ctx): + locale = ctx.cpu.r_reg(REG_A0) + fmtTemplate = ctx.cpu.r_reg(REG_A1) + date = ctx.cpu.r_reg(REG_A2) + putCharFunc = ctx.cpu.r_reg(REG_A3) + print "not implemented: FormatDate" + + def FormatString(self, ctx): + locale = ctx.cpu.r_reg(REG_A0) + fmtTemplate = ctx.cpu.r_reg(REG_A1) + dataStream = ctx.cpu.r_reg(REG_A2) + putCharFunc = ctx.cpu.r_reg(REG_A3) + print "not implemented: FormatString" + + def GetCatalogStr(self, ctx): + catalog = ctx.cpu.r_reg(REG_A0) + defaultString = ctx.cpu.r_reg(REG_A1) + stringNum = ctx.cpu.r_reg(REG_D0) + print "not implemented: GetCatalogStr" + + def GetLocaleStr(self, ctx): + locale = ctx.cpu.r_reg(REG_A0) + stringNum = ctx.cpu.r_reg(REG_D0) + print "not implemented: GetLocaleStr" + + def OpenCatalogA(self, ctx): + locale = ctx.cpu.r_reg(REG_A0) + name = ctx.cpu.r_reg(REG_A1) + tagList = ctx.cpu.r_reg(REG_A2) + print "not implemented: OpenCatalogA" + + def ParseDate(self, ctx): + locale = ctx.cpu.r_reg(REG_A0) + date = ctx.cpu.r_reg(REG_A1) + fmtTemplate = ctx.cpu.r_reg(REG_A2) + putCharFunc = ctx.cpu.r_reg(REG_A3) + print "not implemented: ParseDate" + + def StrConvert(self, ctx): + locale = ctx.cpu.r_reg(REG_A0) + string = ctx.cpu.r_reg(REG_A1) + buffer = ctx.cpu.r_reg(REG_A2) + bufferSize = ctx.cpu.r_reg(REG_D0) + type = ctx.cpu.r_reg(REG_D1) + print "not implemented: StrConvert" + + def StrnCmp(self, ctx): + locale = ctx.cpu.r_reg(REG_A0) + string1 = ctx.cpu.r_reg(REG_A1) + string2 = ctx.cpu.r_reg(REG_A2) + length = ctx.cpu.r_reg(REG_D0) + type = ctx.cpu.r_reg(REG_D1) + print "not implemented: StrnCmp" +""" diff --git a/amitools/vamos/lib/TimerDevice.py b/amitools/vamos/lib/TimerDevice.py index 79db57ce..fbfd97d3 100644 --- a/amitools/vamos/lib/TimerDevice.py +++ b/amitools/vamos/lib/TimerDevice.py @@ -1,5 +1,19 @@ from amitools.vamos.libcore import LibImpl +from amitools.vamos.machine.regs import * +from amitools.vamos.astructs import * + +from datetime import datetime class TimerDevice(LibImpl): - pass + def ReadEClock(self, ctx): + eclockval = ctx.cpu.r_reg(REG_A0) + + dt = datetime.now() + + # abuse DateStampStruct + tv = AccessStruct(ctx.mem, DateStampStruct, struct_addr = eclockval) + tv.ds_Days = dt.microsecond / 1000000 + tv.ds_Minute = dt.microsecond % 1000000 + + return 50 diff --git a/musashi/m68kfpu.c b/musashi/m68kfpu.c index 082ef4f0..a0062098 100644 --- a/musashi/m68kfpu.c +++ b/musashi/m68kfpu.c @@ -790,6 +790,15 @@ static void fpgen_rm_reg(uint16 w2) source = REG_FP[src].f; } + int round = (opmode & 0x40) != 0; + int to_double = 0; + if (round) { + to_double = (opmode & 0x4) != 0; + opmode &= ~0x40; + if (to_double) + opmode &= ~0x4; + } + switch (opmode) { case 0x00: // FMOVE @@ -890,6 +899,13 @@ static void fpgen_rm_reg(uint16 w2) default: fatalerror("fpgen_rm_reg: unimplemented opmode %02X at %08X\n", opmode, REG_PC-4); } + + if (round) { + if (to_double) + REG_FP[dst].f = (double) REG_FP[dst].f; + else + REG_FP[dst].f = (float) REG_FP[dst].f; + } } static void fscc_mem(void) From 6f9ed088795a28217c32fd04f46c25ebc6a97b28 Mon Sep 17 00:00:00 2001 From: bebbo Date: Mon, 28 Sep 2020 19:04:46 +0200 Subject: [PATCH 02/56] fix ParsePattern and some other errors --- amitools/vamos/lib/DosLibrary.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/amitools/vamos/lib/DosLibrary.py b/amitools/vamos/lib/DosLibrary.py index c8a8dd8e..e0433e17 100644 --- a/amitools/vamos/lib/DosLibrary.py +++ b/amitools/vamos/lib/DosLibrary.py @@ -243,7 +243,7 @@ def SetFileDate(self, ctx): log_dos.info("SetFileDate: file=%s date=%d" % (name,seconds)) sys_path = self.path_mgr.ami_to_sys_path(self.get_current_dir(ctx),name,searchMulti=True) if sys_path == None: - log_file.info("file not found: '%s' -> '%s'" % (ami_path, sys_path)) + log_dos.info("file not found: '%s' -> '%s'" % (name, sys_path)) self.setioerr(ctx,ERROR_OBJECT_NOT_FOUND) return self.DOSFALSE else: @@ -262,7 +262,7 @@ def GetProgramName(self, ctx): n = len(prog_name) # return error if name is too long, but copy buffer size if n > max_len - 1: - self.setioerr(ctx,ERROR_LINE_TOOL_LONG) + self.setioerr(ctx,ERROR_LINE_TOO_LONG) ret = self.DOSFALSE prog_name = prog_name[0:max_len] else: @@ -385,6 +385,7 @@ def SetVar(self, ctx): size = ctx.cpu.r_reg(REG_D3) flags = ctx.cpu.r_reg(REG_D4) name = ctx.mem.r_cstr(name_ptr) + vtype = flags & 0xff if buff_ptr == 0: if not flags & self.GVF_GLOBAL_ONLY: node = self.find_var(ctx,name,vtype) @@ -729,6 +730,7 @@ def VFWritef(self, ctx): out = '' pos = 0 state = '' + val = 0 while pos < len(fmt): ch = fmt[pos].upper() pos = pos + 1 @@ -1174,10 +1176,10 @@ def parsePattern(self, ctx, ignore_case=False): return 0 def ParsePattern(self, ctx): - return self.ParsePattern(ctx) + return self.parsePattern(ctx, False) def ParsePatternNoCase(self, ctx): - return self.ParsePattern(ctx, ignore_case=True) + return self.parsePattern(ctx, True) def matchPattern(self, ctx, ignore_case=False): pat_ptr = ctx.cpu.r_reg(REG_D1) From 85ca7f8aa128199ced4378c09ec9771e1ea65017 Mon Sep 17 00:00:00 2001 From: bebbo Date: Mon, 28 Sep 2020 19:05:12 +0200 Subject: [PATCH 03/56] implement AllocSignal, FreeSignal --- amitools/vamos/lib/ExecLibrary.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/amitools/vamos/lib/ExecLibrary.py b/amitools/vamos/lib/ExecLibrary.py index edc46e37..24a1738c 100644 --- a/amitools/vamos/lib/ExecLibrary.py +++ b/amitools/vamos/lib/ExecLibrary.py @@ -14,6 +14,10 @@ class ExecLibrary(LibImpl): + + def __init__(self): + self.used_mask = 0 + def get_struct_def(self): return ExecLibraryStruct @@ -395,7 +399,7 @@ def DeleteIORequest(self,ctx): log_exec.info("DeleteIOREquest: 0x%06x -> %s" % (req,mb)) self.alloc.free_memory(mb) else: - raise VamosInternalError("DeleteIORequest: Unknown IORequest to delete: ptr=%06x" % addr) + raise VamosInternalError("DeleteIORequest: Unknown IORequest to delete: ptr=%06x" % req) def OpenDevice(self,ctx): name_ptr = ctx.cpu.r_reg(REG_A0) @@ -624,3 +628,20 @@ def Deallocate(self,ctx): num_bytes = ctx.cpu.r_reg(REG_D0) lexec.Alloc.deallocate(ctx, mh_addr, blk_addr, num_bytes) log_exec.info("Deallocate(%06x, %06x, %06x)" % (mh_addr, blk_addr, num_bytes)) + + def AllocSignal(self, ctx): + mask = ctx.cpu.r_reg(REG_D0) + i = 1 + while i < 0x80000000: + if (self.used_mask & i) == 0: + self.used_mask |= i + break + i += i + log_exec.info("AllocSignal(%08x) -> %08x" % (mask, i)) + return i + + def FreeSignal(self, ctx): + sig = ctx.cpu.r_reg(REG_D0) + self.used_mask &= ~sig + log_exec.info("FreeSignal(%08x)" % (sig)) + return 0 \ No newline at end of file From 55e3f3ad1b5de48082d685375e33e67efcddd166 Mon Sep 17 00:00:00 2001 From: bebbo Date: Mon, 28 Sep 2020 20:44:50 +0200 Subject: [PATCH 04/56] fix warning -> uint i; --- musashi/mem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/musashi/mem.c b/musashi/mem.c index e5af621d..54214055 100644 --- a/musashi/mem.c +++ b/musashi/mem.c @@ -238,7 +238,7 @@ unsigned int m68k_read_disassembler_32 (unsigned int address) int mem_init(uint ram_size_kib) { - int i; + uint i; ram_pages = (ram_size_kib + 63) / 64; ram_size = ram_pages * 64 * 1024; ram_data = (uint8_t *)malloc(ram_size); From 6f9566b0bdc97d1fa4519aefa940eeded76d2b9e Mon Sep 17 00:00:00 2001 From: bebbo Date: Mon, 28 Sep 2020 22:06:28 +0200 Subject: [PATCH 05/56] cleanup imports --- amitools/binfmt/elf/BinFmtELF.py | 7 ++++--- amitools/vamos/lib/dos/Process.py | 11 +++++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/amitools/binfmt/elf/BinFmtELF.py b/amitools/binfmt/elf/BinFmtELF.py index b425096b..3b654503 100644 --- a/amitools/binfmt/elf/BinFmtELF.py +++ b/amitools/binfmt/elf/BinFmtELF.py @@ -1,6 +1,7 @@ -from amitools.binfmt.BinImage import * -from .ELFFile import * -from .ELF import * +from amitools.binfmt.BinImage import BIN_IMAGE_TYPE_ELF, SEGMENT_TYPE_CODE, SEGMENT_TYPE_DATA, SEGMENT_FLAG_READ_ONLY, SEGMENT_TYPE_BSS, Segment, Relocations,\ + SymbolTable, Symbol, Reloc, DebugLine, DebugLineFile, DebugLineEntry, BinImage +from .ELFFile import ELFIdentifier, ELFHeader, ELFParseError +from .ELF import EM_68K, ELFOSABI_AROS, ELFOSABI_SYSV from .ELFReader import ELFReader from .DwarfDebugLine import DwarfDebugLine diff --git a/amitools/vamos/lib/dos/Process.py b/amitools/vamos/lib/dos/Process.py index 5cb37e6c..2cc081ec 100644 --- a/amitools/vamos/lib/dos/Process.py +++ b/amitools/vamos/lib/dos/Process.py @@ -1,11 +1,14 @@ -import os -from amitools.vamos.machine.regs import * +from amitools.vamos.astructs.dos import CLIStruct, DosPacketStruct, ProcessStruct +from amitools.vamos.astructs.exec_ import MessageStruct, MinListStruct +from amitools.vamos.lib.dos import CommandLine from amitools.vamos.log import log_proc -from amitools.vamos.astructs import * -from amitools.vamos.lib.lexec.PortManager import * +from amitools.vamos.machine.regs import REG_D0, REG_D1, REG_D2, REG_A0, REG_A2, REG_A5, REG_A6 from amitools.vamos.schedule import Stack, Task +import os + from .SysArgs import sys_args_to_ami_arg_str + NT_PROCESS = 13 From 39ea831998cd24dbc3b97ef1059a28ef7e6666cc Mon Sep 17 00:00:00 2001 From: bebbo Date: Mon, 28 Sep 2020 22:07:02 +0200 Subject: [PATCH 06/56] handle common symbols --- amitools/binfmt/elf/ELFFile.py | 1 + amitools/binfmt/elf/ELFReader.py | 15 +++++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/amitools/binfmt/elf/ELFFile.py b/amitools/binfmt/elf/ELFFile.py index 0518468e..1e25a3f8 100644 --- a/amitools/binfmt/elf/ELFFile.py +++ b/amitools/binfmt/elf/ELFFile.py @@ -122,6 +122,7 @@ def __init__(self, header, idx): self.symbols = [] self.relocations = None self.reloc_by_sect = {} + self.bss = None def get_rela(self): """return a list with all relocations""" diff --git a/amitools/binfmt/elf/ELFReader.py b/amitools/binfmt/elf/ELFReader.py index d2dc450a..81b92ccc 100644 --- a/amitools/binfmt/elf/ELFReader.py +++ b/amitools/binfmt/elf/ELFReader.py @@ -25,6 +25,8 @@ def _load_sections(self, f, ef): idx += 1 sect = self._load_section(f, sect_hdr, idx) ef.sections.append(sect) + if sect_hdr.type_ == SHT_NOBITS: + ef.bss = sect def _load_section(self, f, sect_hdr, idx): t = sect_hdr.type_ @@ -62,17 +64,22 @@ def _resolve_symtab_names(self, sect, sections): raise ELFParseError("Invalid strtab for symtab: " + strtab_seg_num) strtab = sections[strtab_seg_num] if strtab.__class__ != ELFSectionStringTable: - raise ELFParserError("Invalid strtab segment for symtab") + raise ELFParseError("Invalid strtab segment for symtab") # resolve all symbol names for sym in sect.symtab: sym.name_str = strtab.get_string(sym.name) - def _resolve_symtab_indices(self, sect, sections): + def _resolve_symtab_indices(self, sect, ef, sections): for sym in sect.symtab: if sym.shndx_str == None: # refers a valid section idx = sym.shndx - sym.section = sections[idx] + if idx == 65522: # common + sym.section = ef.bss + sym.value = ef.bss.header.size + ef.bss.header.size += sym.size + 3 & ~3 + else: + sym.section = sections[idx] def _assign_symbols_to_sections(self, sect): src_file_sym = None @@ -196,7 +203,7 @@ def load(self, f): # get names in symtab self._resolve_symtab_names(sect, ef.sections) # link sections to symbols - self._resolve_symtab_indices(sect, ef.sections) + self._resolve_symtab_indices(sect, ef, ef.sections) # assign symbols to sections self._assign_symbols_to_sections(sect) From f4876aa1f125add98d6d21d7ab3aff85cb704081 Mon Sep 17 00:00:00 2001 From: bebbo Date: Mon, 28 Sep 2020 22:30:26 +0200 Subject: [PATCH 07/56] support more section names --- amitools/binfmt/elf/BinFmtELF.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/amitools/binfmt/elf/BinFmtELF.py b/amitools/binfmt/elf/BinFmtELF.py index 3b654503..632b70db 100644 --- a/amitools/binfmt/elf/BinFmtELF.py +++ b/amitools/binfmt/elf/BinFmtELF.py @@ -61,19 +61,18 @@ def load_image_fobj(self, fobj): # walk through elf sections sect_to_seg = {} for sect in elf.sections: + if sect.header.type_ == 0 or sect.header.type_ == 4: + continue # determine segment type seg_type = None name = sect.name_str flags = 0 - if name == b".text": + if name.startswith(b".text"): seg_type = SEGMENT_TYPE_CODE - elif name == b".data": - seg_type = SEGMENT_TYPE_DATA - elif name == b".rodata": - seg_type = SEGMENT_TYPE_DATA - flags = SEGMENT_FLAG_READ_ONLY elif name == b".bss": seg_type = SEGMENT_TYPE_BSS + else: + seg_type = SEGMENT_TYPE_DATA # we got a segment if seg_type is not None: size = sect.header.size From f7d4cd86399f47c5d1a4ca4413da18922e89175d Mon Sep 17 00:00:00 2001 From: bebbo Date: Mon, 5 Oct 2020 10:09:05 +0200 Subject: [PATCH 08/56] remove unused imports --- amitools/binfmt/elf/ELFReader.py | 1 - amitools/vamos/lib/ExecLibrary.py | 2 +- amitools/vamos/lib/dos/CommandLine.py | 1 - amitools/vamos/loader/segload.py | 1 - 4 files changed, 1 insertion(+), 4 deletions(-) diff --git a/amitools/binfmt/elf/ELFReader.py b/amitools/binfmt/elf/ELFReader.py index 81b92ccc..9939972a 100644 --- a/amitools/binfmt/elf/ELFReader.py +++ b/amitools/binfmt/elf/ELFReader.py @@ -1,6 +1,5 @@ """A class for reading and writing ELF format binaries (esp. Amiga m68k ones)""" -import struct import os from .ELF import * from .ELFFile import * diff --git a/amitools/vamos/lib/ExecLibrary.py b/amitools/vamos/lib/ExecLibrary.py index d7ca7cf0..6066217c 100644 --- a/amitools/vamos/lib/ExecLibrary.py +++ b/amitools/vamos/lib/ExecLibrary.py @@ -3,7 +3,7 @@ from amitools.vamos.libcore import LibImpl from amitools.vamos.astructs import * from amitools.vamos.atypes import ExecLibrary as ExecLibraryType -from amitools.vamos.atypes import NodeType, Node, List +from amitools.vamos.atypes import NodeType, List from amitools.vamos.log import log_exec from amitools.vamos.error import * from .lexec.PortManager import PortManager diff --git a/amitools/vamos/lib/dos/CommandLine.py b/amitools/vamos/lib/dos/CommandLine.py index 09392fcd..386b3176 100644 --- a/amitools/vamos/lib/dos/CommandLine.py +++ b/amitools/vamos/lib/dos/CommandLine.py @@ -1,4 +1,3 @@ -from .SysArgs import ami_quote_str from .Item import ItemParser from .CSource import CSource diff --git a/amitools/vamos/loader/segload.py b/amitools/vamos/loader/segload.py index 9772f619..882cfa1c 100644 --- a/amitools/vamos/loader/segload.py +++ b/amitools/vamos/loader/segload.py @@ -2,7 +2,6 @@ from amitools.binfmt.BinFmt import BinFmt from amitools.binfmt.Relocate import Relocate -from amitools.vamos.label import LabelSegment from amitools.vamos.log import log_segload from .seglist import SegList From 1812a61a4d22e08d28296eaeb6a0008e459a8a90 Mon Sep 17 00:00:00 2001 From: bebbo Date: Wed, 17 Feb 2021 10:44:08 +0100 Subject: [PATCH 09/56] add some missing modes --- musashi/m68kfpu.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/musashi/m68kfpu.c b/musashi/m68kfpu.c index 2b3626de..26ab7a91 100644 --- a/musashi/m68kfpu.c +++ b/musashi/m68kfpu.c @@ -567,10 +567,26 @@ static uint64 READ_EA_64(int ea) h2 = m68ki_read_32(ea+4); return (uint64)(h1) << 32 | (uint64)(h2); } + case 6: // (An) + (Xn) + d8 + { + uint32 ea = EA_AY_IX_16(); + h1 = m68ki_read_32(ea+0); + h2 = m68ki_read_32(ea+4); + return (uint64)(h1) << 32 | (uint64)(h2); + } case 7: { switch (reg) { + case 1: // (xxx).L + { + uint32 d1 = OPER_I_16(); + uint32 d2 = OPER_I_16(); + uint32 ea = (d1 << 16) | d2; + h1 = m68ki_read_32(ea+0); + h2 = m68ki_read_32(ea+4); + return (uint64)(h1) << 32 | (uint64)(h2); + } case 4: // # { h1 = OPER_I_32(); @@ -944,6 +960,38 @@ static void WRITE_EA_64(int ea, uint64 data) m68ki_write_32(ea+4, (uint32)(data)); break; } + + case 6: // (An) + (Xn) + d8 + { + uint32 ea = EA_AY_IX_16(); + m68ki_write_32(ea+0, (uint32)(data >> 32)); + m68ki_write_32(ea+4, (uint32)(data)); + break; + } + case 7: + { + switch (reg) + { + case 1: // (xxx).L + { + uint32 d1 = OPER_I_16(); + uint32 d2 = OPER_I_16(); + uint32 ea = (d1 << 16) | d2; + m68ki_write_32(ea+0, (uint32)(data >> 32)); + m68ki_write_32(ea+4, (uint32)(data)); + break; + } + case 2: // (d16, PC) + { + uint32 ea = EA_PCDI_32(); + m68ki_write_32(ea+0, (uint32)(data >> 32)); + m68ki_write_32(ea+4, (uint32)(data)); + break; + } + default: fatalerror("M68kFPU: WRITE_EA_64: unhandled mode %d, data %08X%08X at %08X\n", mode, reg, (uint32)(data >> 32), (uint32)(data), REG_PC); + } + break; + } default: fatalerror("M68kFPU: WRITE_EA_64: unhandled mode %d, reg %d, data %08X%08X at %08X\n", mode, reg, (uint32)(data >> 32), (uint32)(data), REG_PC); } } @@ -1243,7 +1291,14 @@ static void fpgen_rm_reg(uint16 w2) case 0x20: // FDIV { REG_FP[dst] = floatx80_div(REG_FP[dst], source); - SET_CONDITION_CODES(REG_FP[dst]); // JFF + SET_CONDITION_CODES(REG_FP[dst]); // JFF + USE_CYCLES(43); + break; + } + case 0x24: // FSGLDIV + { + REG_FP[dst] = double_to_fx80((float)fx80_to_double(floatx80_div(REG_FP[dst], source))); + SET_CONDITION_CODES(REG_FP[dst]); // JFF USE_CYCLES(43); break; } @@ -1262,6 +1317,13 @@ static void fpgen_rm_reg(uint16 w2) USE_CYCLES(11); break; } + case 0x27: // FSGLMUL + { + REG_FP[dst] = double_to_fx80((float)fx80_to_double(floatx80_mul(REG_FP[dst], source))); + SET_CONDITION_CODES(REG_FP[dst]); + USE_CYCLES(11); + break; + } case 0x25: // FREM { REG_FP[dst] = floatx80_rem(REG_FP[dst], source); From 62a33bd26da43c6e052cac0753e532723e7f1765 Mon Sep 17 00:00:00 2001 From: bebbo Date: Wed, 17 Feb 2021 15:41:21 +0100 Subject: [PATCH 10/56] support reloc 68K_PC32 --- amitools/binfmt/BinImage.py | 6 +++++- amitools/binfmt/Relocate.py | 12 +++++++++--- amitools/binfmt/elf/BinFmtELF.py | 2 +- amitools/binfmt/elf/ELF.py | 2 +- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/amitools/binfmt/BinImage.py b/amitools/binfmt/BinImage.py index 2796fa02..a32670e6 100644 --- a/amitools/binfmt/BinImage.py +++ b/amitools/binfmt/BinImage.py @@ -13,14 +13,18 @@ class Reloc: - def __init__(self, offset, width=2, addend=0): + def __init__(self, offset, type, width=2, addend=0): self.offset = offset + self.type = type self.width = width self.addend = addend def get_offset(self): return self.offset + def get_type(self): + return self.type + def get_width(self): return self.width diff --git a/amitools/binfmt/Relocate.py b/amitools/binfmt/Relocate.py index 9567b273..2bd080d0 100644 --- a/amitools/binfmt/Relocate.py +++ b/amitools/binfmt/Relocate.py @@ -74,6 +74,7 @@ def _copy_data(self, data, segment, offset=0): def _reloc_data(self, data, segment, addrs, offset=0): # find relocations to_segs = segment.get_reloc_to_segs() + my_addr = addrs[segment.id] for to_seg in to_segs: # get target segment's address to_id = to_seg.id @@ -81,13 +82,18 @@ def _reloc_data(self, data, segment, addrs, offset=0): # get relocations reloc = segment.get_reloc(to_seg) for r in reloc.get_relocs(): - self._reloc(segment.id, data, r, to_addr, to_id, offset) + self._reloc(segment.id, data, r, my_addr, to_addr, to_id, offset) - def _reloc(self, my_id, data, reloc, to_addr, to_id, extra_offset): + def _reloc(self, my_id, data, reloc, my_addr, to_addr, to_id, extra_offset): """relocate one entry""" offset = reloc.get_offset() + extra_offset delta = self._read_long(data, offset) + reloc.addend - addr = to_addr + delta + if reloc.get_type() == 1: + addr = to_addr + delta + elif reloc.get_type() == 4: + addr = delta + to_addr - my_addr - offset + else: + raise(Exception("unsupported type %d" % reloc.get_type())) self._write_long(data, offset, addr) if self.verbose: print( diff --git a/amitools/binfmt/elf/BinFmtELF.py b/amitools/binfmt/elf/BinFmtELF.py index 632b70db..f8b3470e 100644 --- a/amitools/binfmt/elf/BinFmtELF.py +++ b/amitools/binfmt/elf/BinFmtELF.py @@ -118,7 +118,7 @@ def add_elf_rela(self, sect, seg, sect_to_seg): seg.add_reloc(to_seg, rl) # add relocations for rel in sect.get_rela_by_section(tgt_sect): - r = Reloc(rel.offset, addend=rel.section_addend) + r = Reloc(rel.offset, rel.type_, addend=rel.section_addend) rl.add_reloc(r) def add_elf_symbols(self, symbols, seg): diff --git a/amitools/binfmt/elf/ELF.py b/amitools/binfmt/elf/ELF.py index 709dcf0d..913d41d6 100644 --- a/amitools/binfmt/elf/ELF.py +++ b/amitools/binfmt/elf/ELF.py @@ -65,4 +65,4 @@ STV_values = {0: "DEFAULT", 1: "INTERNAL", 2: "HIDDEN", 3: "PROTECTED"} -R_68K_values = {0: "68K_NONE", 1: "68K_32"} +R_68K_values = {0: "68K_NONE", 1: "68K_32", 4: "68K_PC32"} From f2c0b1525809c0cbffcab7f0f27b56fbf40fb2ce Mon Sep 17 00:00:00 2001 From: bebbo Date: Wed, 17 Feb 2021 19:57:10 +0100 Subject: [PATCH 11/56] READ_EA_FPE: add mode 7 reg 1 --- musashi/m68kfpu.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/musashi/m68kfpu.c b/musashi/m68kfpu.c index 26ab7a91..eba57567 100644 --- a/musashi/m68kfpu.c +++ b/musashi/m68kfpu.c @@ -623,7 +623,6 @@ static floatx80 READ_EA_FPE(int mode, int reg, uint32 di_mode_ea) fpr = load_extended_float80(ea); break; } - case 3: // (An)+ { uint32 ea = REG_A[reg]; @@ -634,13 +633,19 @@ static floatx80 READ_EA_FPE(int mode, int reg, uint32 di_mode_ea) case 5: // (d16, An) (added by JFF) { fpr = load_extended_float80(di_mode_ea); - break; - + break; } - case 7: // extended modes + case 7: // extended modes { switch (reg) { + case 1: // (xxx).L + { + uint32 d1 = OPER_I_16(); + uint32 d2 = OPER_I_16(); + fpr = int32_to_floatx80((d1 << 16) | d2); + } + break; case 2: // (d16, PC) { uint32 ea = EA_PCDI_32(); From 22e5efc93fb10729fd2d1982520c5829ff1f1961 Mon Sep 17 00:00:00 2001 From: bebbo Date: Wed, 17 Feb 2021 23:13:02 +0100 Subject: [PATCH 12/56] fix mode 7, reg 1: load extended float correctly --- musashi/m68kfpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/musashi/m68kfpu.c b/musashi/m68kfpu.c index eba57567..e8d4c8cd 100644 --- a/musashi/m68kfpu.c +++ b/musashi/m68kfpu.c @@ -643,7 +643,7 @@ static floatx80 READ_EA_FPE(int mode, int reg, uint32 di_mode_ea) { uint32 d1 = OPER_I_16(); uint32 d2 = OPER_I_16(); - fpr = int32_to_floatx80((d1 << 16) | d2); + fpr = load_extended_float80((d1 << 16) | d2); } break; case 2: // (d16, PC) From 360f1e6677fe80b30a2b3bcd451d0448d6bf355e Mon Sep 17 00:00:00 2001 From: bebbo Date: Wed, 17 Feb 2021 23:13:29 +0100 Subject: [PATCH 13/56] also dump fp registers for 68040 --- amitools/vamos/machine/cpustate.py | 16 ++++++++++++++++ musashi/m68k.h | 2 ++ musashi/m68kcpu.c | 19 +++++++++++++++++++ musashi/pycpu.pyx | 10 ++++++++++ 4 files changed, 47 insertions(+) diff --git a/amitools/vamos/machine/cpustate.py b/amitools/vamos/machine/cpustate.py index ddffb11f..afc7bd5b 100644 --- a/amitools/vamos/machine/cpustate.py +++ b/amitools/vamos/machine/cpustate.py @@ -1,9 +1,11 @@ +from musashi.m68k import M68K_CPU_TYPE_68040 class CPUState: def __init__(self): self.pc = None self.sr = None self.dx = None self.ax = None + self.fx = None self.usp = None self.isp = None self.msp = None @@ -37,6 +39,12 @@ def get(self, cpu): self.ax = ax for i in range(8): ax.append(cpu.r_reg(8 + i)) + + if cpu.get_cpu_type() == M68K_CPU_TYPE_68040: + fx = [] + self.fx = fx + for i in range(8): + fx.append(cpu.r_fpreg(i)) def set(self, cpu): cpu.w_pc(self.pc) @@ -72,4 +80,12 @@ def dump(self): ax.append("A%d=%08x" % (pos, a)) pos += 1 res.append(" ".join(ax)) + + fx = [] + pos = 0 + for f in self.fx: + fx.append("F%d=%g" % (pos, f)) + pos += 1 + if len(fx) > 0: + res.append(" ".join(fx)) return res diff --git a/musashi/m68k.h b/musashi/m68k.h index d44d7e34..fb891377 100644 --- a/musashi/m68k.h +++ b/musashi/m68k.h @@ -373,6 +373,8 @@ void m68k_set_context(void* dst); void m68k_state_register(const char *type, int index); +double m68k_get_fpreg(void* context, int reg); + /* Peek at the internals of a CPU context. This can either be a context * retrieved using m68k_get_context() or the currently running context. * If context is NULL, the currently running CPU context will be used. diff --git a/musashi/m68kcpu.c b/musashi/m68kcpu.c index 652f2f0e..83761af9 100644 --- a/musashi/m68kcpu.c +++ b/musashi/m68kcpu.c @@ -729,6 +729,25 @@ void m68k_set_reg(m68k_register_t regnum, unsigned int value) } } +double m68k_get_fpreg(void* context, int regnum) +{ + m68ki_cpu_core* cpu = context != NULL ?(m68ki_cpu_core*)context : &m68ki_cpu; + + switch(regnum) + { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + return fx80_to_double(cpu->fpr[regnum]); + } + return 0; +} + /* Set the callbacks */ void m68k_set_int_ack_callback(int (*callback)(int int_level)) { diff --git a/musashi/pycpu.pyx b/musashi/pycpu.pyx index 7eaa57c9..bfa9db40 100644 --- a/musashi/pycpu.pyx +++ b/musashi/pycpu.pyx @@ -24,6 +24,7 @@ cdef extern from "m68k.h": int m68k_execute(int num_cycles) void m68k_end_timeslice() + double m68k_get_fpreg(void* context, int reg) unsigned int m68k_get_reg(void* context, m68k_register_t reg) void m68k_set_reg(m68k_register_t reg, unsigned int value) @@ -66,6 +67,9 @@ cdef class CPUContext: cdef void *get_data(self): return self.data + def r_fpreg(self, int reg): + return m68k_get_fpreg(self.data, reg) + def r_reg(self, int reg): return m68k_get_reg(self.data, reg) @@ -101,6 +105,12 @@ cdef class CPU: self.set_reset_instr_callback(None) self.set_instr_hook_callback(None) + def get_cpu_type(self): + return self.cpu_type + + def r_fpreg(self, int reg): + return m68k_get_fpreg(NULL, reg) + cdef unsigned int r_reg_internal(self, m68k_register_t reg): return m68k_get_reg(NULL, reg) From 4a3a03e139bc164120b3ba10f0064c7181278b2f Mon Sep 17 00:00:00 2001 From: bebbo Date: Thu, 18 Feb 2021 09:35:40 +0100 Subject: [PATCH 14/56] support all constants --- musashi/m68kfpu.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/musashi/m68kfpu.c b/musashi/m68kfpu.c index e8d4c8cd..bc7f46d1 100644 --- a/musashi/m68kfpu.c +++ b/musashi/m68kfpu.c @@ -1213,8 +1213,62 @@ static void fpgen_rm_reg(uint16 w2) source = int32_to_floatx80((sint32)10*10); break; + case 0x35: // 10^4 + source = int32_to_floatx80((sint32)10000); + break; + + case 0x36: // 10^8 + source = double_to_fx80(1e8); + break; + + case 0x37: // 10^16 + source = double_to_fx80(1e16); + break; + + case 0x38: // 10^32 + source = double_to_fx80(1e32); + break; + + case 0x39: // 10^64 + source = double_to_fx80(1e64); + break; + + case 0x3a: // 10^128 + source = double_to_fx80(1e128); + break; + + case 0x3b: // 10^256 + source = double_to_fx80(1e256); + break; + + case 0x3c: // 10^512 + source = double_to_fx80(1e256); + source = floatx80_mul(source, source); + break; + + case 0x3d: // 10^1024 + source = double_to_fx80(1e256); + source = floatx80_mul(source, source); + source = floatx80_mul(source, source); + break; + + case 0x3e: // 10^2048 + source = double_to_fx80(1e256); + source = floatx80_mul(source, source); + source = floatx80_mul(source, source); + source = floatx80_mul(source, source); + break; + + case 0x3f: // 10^4096 + source = double_to_fx80(1e256); + source = floatx80_mul(source, source); + source = floatx80_mul(source, source); + source = floatx80_mul(source, source); + source = floatx80_mul(source, source); + break; + default: - fatalerror("fmove_rm_reg: unknown constant ROM offset %x at %08x\n", w2&0x7f, REG_PC-4); + source = int32_to_floatx80((sint32)0); break; } From 908f830162560e84859b8a7640431a95ed750e93 Mon Sep 17 00:00:00 2001 From: bebbo Date: Thu, 18 Feb 2021 09:50:04 +0100 Subject: [PATCH 15/56] add missing modes --- musashi/m68kfpu.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/musashi/m68kfpu.c b/musashi/m68kfpu.c index bc7f46d1..f6fa733c 100644 --- a/musashi/m68kfpu.c +++ b/musashi/m68kfpu.c @@ -630,6 +630,13 @@ static floatx80 READ_EA_FPE(int mode, int reg, uint32 di_mode_ea) fpr = load_extended_float80(ea); break; } + case 4: // -(An) + { + REG_A[reg] -= 12; + uint32 ea = REG_A[reg]; + fpr = load_extended_float80(ea); + break; + } case 5: // (d16, An) (added by JFF) { fpr = load_extended_float80(di_mode_ea); @@ -701,6 +708,13 @@ static floatx80 READ_EA_PACK(int ea) fpr = load_pack_float80(ea); break; } + case 4: // -(An) + { + REG_A[reg] -= 12; + uint32 ea = REG_A[reg]; + fpr = load_pack_float80(ea); + break; + } case 7: // extended modes { From 2df4c3c5c68a9465ab6627b3ec532e28117fa2cf Mon Sep 17 00:00:00 2001 From: bebbo Date: Thu, 18 Feb 2021 10:46:07 +0100 Subject: [PATCH 16/56] add more modes WRITE_EA_FPE: add mode 7 reg 1 WRITE_EA_64: adde mode 3 --- musashi/m68kfpu.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/musashi/m68kfpu.c b/musashi/m68kfpu.c index f6fa733c..111965ea 100644 --- a/musashi/m68kfpu.c +++ b/musashi/m68kfpu.c @@ -963,6 +963,15 @@ static void WRITE_EA_64(int ea, uint64 data) m68ki_write_32(ea+4, (uint32)(data)); break; } + case 3: // (An)+ + { + uint32 ea; + ea = REG_A[reg]; + REG_A[reg] += 8; + m68ki_write_32(ea+0, (uint32)(data >> 32)); + m68ki_write_32(ea+4, (uint32)(data)); + break; + } case 4: // -(An) { uint32 ea; @@ -1059,6 +1068,15 @@ static void WRITE_EA_FPE(int mode, int reg, floatx80 fpr, uint32 di_mode_ea) { switch (reg) { + case 1: // (xxx).L + { + uint32 d1 = OPER_I_16(); + uint32 d2 = OPER_I_16(); + uint32 ea = (d1 << 16) | d2; + store_extended_float80(ea, fpr); + break; + } + default: fatalerror("M68kFPU: WRITE_EA_FPE: unhandled mode %d, reg %d, at %08X\n", mode, reg, REG_PC); } break; From 8dfc6c8b2c931c00e9daec6e84cc19ab325acba9 Mon Sep 17 00:00:00 2001 From: bebbo Date: Sun, 21 Feb 2021 13:44:33 +0100 Subject: [PATCH 17/56] fix hunk loading --- amitools/binfmt/hunk/BinFmtHunk.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amitools/binfmt/hunk/BinFmtHunk.py b/amitools/binfmt/hunk/BinFmtHunk.py index 47339456..a6fbd50c 100644 --- a/amitools/binfmt/hunk/BinFmtHunk.py +++ b/amitools/binfmt/hunk/BinFmtHunk.py @@ -166,7 +166,7 @@ def _add_hunk_relocs(self, blks, seg, all_segs): rl = Relocations(to_seg) # add offsets for o in offsets: - r = Reloc(o) + r = Reloc(o, 1) rl.add_reloc(r) seg.add_reloc(to_seg, rl) From c29d1efc5041eb944bac71b46a7dd459c4168704 Mon Sep 17 00:00:00 2001 From: bebbo Date: Fri, 26 Feb 2021 14:52:05 +0100 Subject: [PATCH 18/56] fix regster printing for non 68040... --- amitools/vamos/machine/cpustate.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/amitools/vamos/machine/cpustate.py b/amitools/vamos/machine/cpustate.py index afc7bd5b..577f4863 100644 --- a/amitools/vamos/machine/cpustate.py +++ b/amitools/vamos/machine/cpustate.py @@ -1,4 +1,5 @@ from musashi.m68k import M68K_CPU_TYPE_68040 +from hyperlink._url import NoneType class CPUState: def __init__(self): self.pc = None @@ -81,11 +82,12 @@ def dump(self): pos += 1 res.append(" ".join(ax)) - fx = [] - pos = 0 - for f in self.fx: - fx.append("F%d=%g" % (pos, f)) - pos += 1 - if len(fx) > 0: - res.append(" ".join(fx)) + if not type(self.fx) is NoneType: + fx = [] + pos = 0 + for f in self.fx: + fx.append("F%d=%g" % (pos, f)) + pos += 1 + if len(fx) > 0: + res.append(" ".join(fx)) return res From cf7ea1e3c76087b5a1172a433db95327414a6b52 Mon Sep 17 00:00:00 2001 From: bebbo Date: Sat, 27 Feb 2021 08:51:57 +0100 Subject: [PATCH 19/56] default locale is 0 now --- amitools/vamos/lib/LocaleLibrary.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amitools/vamos/lib/LocaleLibrary.py b/amitools/vamos/lib/LocaleLibrary.py index d9e2a608..a134d98d 100644 --- a/amitools/vamos/lib/LocaleLibrary.py +++ b/amitools/vamos/lib/LocaleLibrary.py @@ -7,7 +7,7 @@ class LocaleLibrary(LibImpl): def OpenLocale(self, ctx): self.name = ctx.cpu.r_reg(REG_A0) # dummy - return 1 + return 0 def CloseLocale(self, ctx): self.locale = ctx.cpu.r_reg(REG_A0) From 2a5061927a76e22fb9e1e5004e5e7392f2b1d7e3 Mon Sep 17 00:00:00 2001 From: bebbo Date: Sat, 27 Feb 2021 20:38:56 +0100 Subject: [PATCH 20/56] suppress warning if chunk is too small --- amitools/vamos/lib/lexec/Alloc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amitools/vamos/lib/lexec/Alloc.py b/amitools/vamos/lib/lexec/Alloc.py index 12f75961..efba83ef 100644 --- a/amitools/vamos/lib/lexec/Alloc.py +++ b/amitools/vamos/lib/lexec/Alloc.py @@ -107,7 +107,7 @@ def allocate(ctx, mh_addr, num_bytes): mc_next = mc.read_next(ctx) log_exec.debug("read: %s", mc_next) if mc_next is None: - log_exec.warning("invalid mem chunk list!") +# log_exec.warning("invalid mem chunk list!") return 0 mc_last = mc mc = mc_next From 275010efb186441a98bce90b52e334f8422c05fa Mon Sep 17 00:00:00 2001 From: bebbo Date: Sat, 27 Mar 2021 20:09:25 +0100 Subject: [PATCH 21/56] report proper cycles --- musashi/m68kcpu.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/musashi/m68kcpu.c b/musashi/m68kcpu.c index 83761af9..223d90e6 100644 --- a/musashi/m68kcpu.c +++ b/musashi/m68kcpu.c @@ -1035,7 +1035,11 @@ int m68k_execute(int num_cycles) SET_CYCLES(0); /* return how many clocks we used */ - return m68ki_initial_cycles - GET_CYCLES(); + if (num_cycles == m68ki_initial_cycles) + return m68ki_initial_cycles - GET_CYCLES(); + + /* modified by end_timeslice. */ + return num_cycles - m68ki_initial_cycles; } From 56e352fdc68413cec1f320b56e9eac2472f18e57 Mon Sep 17 00:00:00 2001 From: bebbo Date: Sun, 30 May 2021 09:25:02 +0200 Subject: [PATCH 22/56] init process's tc_SPUpper and tc_SPLower --- amitools/vamos/lib/dos/Process.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/amitools/vamos/lib/dos/Process.py b/amitools/vamos/lib/dos/Process.py index 2cc081ec..795d62b1 100644 --- a/amitools/vamos/lib/dos/Process.py +++ b/amitools/vamos/lib/dos/Process.py @@ -56,7 +56,7 @@ def __init__( self.shell_message = None self.shell_packet = None self.shell_port = None - self.init_task_struct(input_fh, output_fh) + self.init_task_struct(input_fh, output_fh, self.stack) self.set_cwd() self._init_task() @@ -265,7 +265,7 @@ def run_system(self): return self.shell_packet.addr # ----- task struct ----- - def init_task_struct(self, input_fh, output_fh): + def init_task_struct(self, input_fh, output_fh, stack): # Inject arguments into input stream (Needed for C:Execute) self.this_task = self.ctx.alloc.alloc_struct( self.bin_basename + "_ThisTask", ProcessStruct @@ -280,6 +280,11 @@ def init_task_struct(self, input_fh, output_fh): self.this_task.access.w_s( "pr_COS", output_fh.b_addr << 2 ) # compensate BCPL auto-conversion + + # init stack + self.this_task.access.w_s("pr_Task.tc_SPUpper", stack.upper) + self.this_task.access.w_s("pr_Task.tc_SPLower", stack.lower) + # setup console task console_task = self.ctx.dos_lib.file_mgr.get_console_handler_port() self.this_task.access.w_s("pr_ConsoleTask", console_task) From 096d191c486242b19d6258b091d863d05fee802b Mon Sep 17 00:00:00 2001 From: bebbo Date: Sun, 30 May 2021 21:03:37 +0200 Subject: [PATCH 23/56] add READ_EA_FPE mode 6 --- musashi/m68kfpu.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/musashi/m68kfpu.c b/musashi/m68kfpu.c index 111965ea..95fd603d 100644 --- a/musashi/m68kfpu.c +++ b/musashi/m68kfpu.c @@ -642,6 +642,12 @@ static floatx80 READ_EA_FPE(int mode, int reg, uint32 di_mode_ea) fpr = load_extended_float80(di_mode_ea); break; } + case 6: // (An) + (Xn) + d8 + { + uint32 ea = EA_AY_IX_16(); + fpr = load_extended_float80(ea); + break; + } case 7: // extended modes { switch (reg) From 22e38f8409ade8d0e13b1036c003d09dd84e132a Mon Sep 17 00:00:00 2001 From: bebbo Date: Sun, 30 May 2021 21:08:10 +0200 Subject: [PATCH 24/56] add READ_EA_64 mode 4 --- musashi/m68kfpu.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/musashi/m68kfpu.c b/musashi/m68kfpu.c index 95fd603d..a0560d3c 100644 --- a/musashi/m68kfpu.c +++ b/musashi/m68kfpu.c @@ -560,6 +560,14 @@ static uint64 READ_EA_64(int ea) h2 = m68ki_read_32(ea+4); return (uint64)(h1) << 32 | (uint64)(h2); } + case 4: // -(An) + { + REG_A[reg] -= 8; + uint32 ea = REG_A[reg]; + h1 = m68ki_read_32(ea+0); + h2 = m68ki_read_32(ea+4); + return (uint64)(h1) << 32 | (uint64)(h2); + } case 5: // (d16, An) { uint32 ea = EA_AY_DI_32(); From d74865bae4b42ac1587e39c38d888a78c175548a Mon Sep 17 00:00:00 2001 From: bebbo Date: Sun, 30 May 2021 21:13:03 +0200 Subject: [PATCH 25/56] more reads mode 3/4 --- musashi/m68kfpu.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/musashi/m68kfpu.c b/musashi/m68kfpu.c index a0560d3c..394ca121 100644 --- a/musashi/m68kfpu.c +++ b/musashi/m68kfpu.c @@ -375,6 +375,16 @@ static uint8 READ_EA_8(int ea) uint32 ea = REG_A[reg]; return m68ki_read_8(ea); } + case 3: // (An)+ + { + uint32 ea = EA_AY_PI_8(); + return m68ki_read_8(ea); + } + case 4: // -(An) + { + uint32 ea = EA_AY_PD_8(); + return m68ki_read_8(ea); + } case 5: // (d16, An) { uint32 ea = EA_AY_DI_8(); @@ -431,6 +441,16 @@ static uint16 READ_EA_16(int ea) uint32 ea = REG_A[reg]; return m68ki_read_16(ea); } + case 3: // (An)+ + { + uint32 ea = EA_AY_PI_16(); + return m68ki_read_16(ea); + } + case 4: // -(An) + { + uint32 ea = EA_AY_PD_16(); + return m68ki_read_16(ea); + } case 5: // (d16, An) { uint32 ea = EA_AY_DI_16(); @@ -493,6 +513,11 @@ static uint32 READ_EA_32(int ea) uint32 ea = EA_AY_PI_32(); return m68ki_read_32(ea); } + case 4: // -(An) + { + uint32 ea = EA_AY_PD_32(); + return m68ki_read_32(ea); + } case 5: // (d16, An) { uint32 ea = EA_AY_DI_32(); From a8fc1043171346607944685ab9080bf9b64d7249 Mon Sep 17 00:00:00 2001 From: bebbo Date: Sun, 30 May 2021 22:53:14 +0200 Subject: [PATCH 26/56] attempt to fix comparison with inf --- musashi/m68kfpu.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/musashi/m68kfpu.c b/musashi/m68kfpu.c index 394ca121..f6990a53 100644 --- a/musashi/m68kfpu.c +++ b/musashi/m68kfpu.c @@ -1169,6 +1169,11 @@ static void WRITE_EA_PACK(int ea, int k, floatx80 fpr) } } +static inline int is_inf(floatx80 reg) { + if (((reg.high & 0x7fff) == 0x7fff) && ((reg.low<<1) == 0)) + return reg.high & 0x8000 ? -1 : 1; + return 0; +} static void fpgen_rm_reg(uint16 w2) { @@ -1471,8 +1476,30 @@ static void fpgen_rm_reg(uint16 w2) case 0x38: // FCMP { floatx80 res; - res = floatx80_sub(REG_FP[dst], source); - SET_CONDITION_CODES(res); + // handle inf in comparison if there is no nan. + int d = is_inf(REG_FP[dst]); + int s = is_inf(source); + if (!floatx80_is_nan(REG_FP[dst]) && !floatx80_is_nan(source) && (d || s)) + { + REG_FPSR &= ~(FPCC_N|FPCC_Z|FPCC_I|FPCC_NAN); + + if (s < 0) { + if (d < 0) + REG_FPSR |= FPCC_N | FPCC_Z; + } else + if (s > 0) { + if (d > 0) + REG_FPSR |= FPCC_Z; + else + REG_FPSR |= FPCC_N; + } else + if (d < 0) + REG_FPSR |= FPCC_N; + + } else { + res = floatx80_sub(REG_FP[dst], source); + SET_CONDITION_CODES(res); + } USE_CYCLES(7); break; } From a8d70519ca7a51058c424751ad488387cb33dee0 Mon Sep 17 00:00:00 2001 From: bebbo Date: Mon, 31 May 2021 08:00:11 +0200 Subject: [PATCH 27/56] also print fpu status register --- amitools/vamos/machine/cpustate.py | 5 +++++ musashi/m68k.h | 1 + musashi/m68kcpu.c | 5 +++++ musashi/pycpu.pyx | 4 ++++ 4 files changed, 15 insertions(+) diff --git a/amitools/vamos/machine/cpustate.py b/amitools/vamos/machine/cpustate.py index 577f4863..0cd5fa45 100644 --- a/amitools/vamos/machine/cpustate.py +++ b/amitools/vamos/machine/cpustate.py @@ -46,6 +46,7 @@ def get(self, cpu): self.fx = fx for i in range(8): fx.append(cpu.r_fpreg(i)) + self.fpsr = cpu.r_fpsr() def set(self, cpu): cpu.w_pc(self.pc) @@ -89,5 +90,9 @@ def dump(self): fx.append("F%d=%g" % (pos, f)) pos += 1 if len(fx) > 0: + fx.append("N=%d" % ((self.fpsr >> 27) & 1)) + fx.append("Z=%d" % ((self.fpsr >> 26) & 1)) + fx.append("I=%d" % ((self.fpsr >> 25) & 1)) + fx.append("NAN=%d" % ((self.fpsr >> 24) & 1)) res.append(" ".join(fx)) return res diff --git a/musashi/m68k.h b/musashi/m68k.h index fb891377..e6fed7aa 100644 --- a/musashi/m68k.h +++ b/musashi/m68k.h @@ -374,6 +374,7 @@ void m68k_state_register(const char *type, int index); double m68k_get_fpreg(void* context, int reg); +int m68k_get_fpsr(void* context); /* Peek at the internals of a CPU context. This can either be a context * retrieved using m68k_get_context() or the currently running context. diff --git a/musashi/m68kcpu.c b/musashi/m68kcpu.c index 223d90e6..d1e3b7be 100644 --- a/musashi/m68kcpu.c +++ b/musashi/m68kcpu.c @@ -748,6 +748,11 @@ double m68k_get_fpreg(void* context, int regnum) return 0; } +int m68k_get_fpsr(void*context) +{ + return REG_FPSR; +} + /* Set the callbacks */ void m68k_set_int_ack_callback(int (*callback)(int int_level)) { diff --git a/musashi/pycpu.pyx b/musashi/pycpu.pyx index bfa9db40..35c7752c 100644 --- a/musashi/pycpu.pyx +++ b/musashi/pycpu.pyx @@ -25,6 +25,7 @@ cdef extern from "m68k.h": void m68k_end_timeslice() double m68k_get_fpreg(void* context, int reg) + int m68k_get_fpsr(void * context) unsigned int m68k_get_reg(void* context, m68k_register_t reg) void m68k_set_reg(m68k_register_t reg, unsigned int value) @@ -111,6 +112,9 @@ cdef class CPU: def r_fpreg(self, int reg): return m68k_get_fpreg(NULL, reg) + def r_fpsr(self): + return m68k_get_fpsr(NULL) + cdef unsigned int r_reg_internal(self, m68k_register_t reg): return m68k_get_reg(NULL, reg) From e5300e1003bdc2dd6fa6f1268f6c3f358f9dcbb1 Mon Sep 17 00:00:00 2001 From: bebbo Date: Mon, 31 May 2021 12:22:33 +0200 Subject: [PATCH 28/56] Merge remote-tracking branch 'upstream/master' --- .travis.yml | 3 +- CHANGELOG.md | 40 +- README.md | 52 +- amitools/binfmt/BinImage.py | 8 +- amitools/binfmt/Relocate.py | 7 +- amitools/binfmt/elf/BinFmtELF.py | 18 +- amitools/binfmt/elf/DwarfDebugLine.py | 19 +- amitools/binfmt/elf/ELFReader.py | 4 +- amitools/binfmt/hunk/HunkBlockFile.py | 6 +- amitools/data/fd/vamostest_lib.fd | 1 + amitools/data/fd/vamostestdev_lib.fd | 2 +- amitools/data/splitdata/README | 1 + amitools/data/splitdata/ks40.dat | Bin 131860 -> 138644 bytes amitools/data/splitdata/romid.idat | Bin 7584 -> 8748 bytes amitools/fs/ADFSVolume.py | 4 +- amitools/fs/DosType.py | 4 +- amitools/fs/FSString.py | 8 +- amitools/fs/blkdev/DiskGeometry.py | 8 +- amitools/fs/rdb/Partition.py | 6 +- amitools/fs/rdb/RDisk.py | 31 +- amitools/fs/validate/BlockScan.py | 4 +- amitools/fs/validate/DirScan.py | 4 +- amitools/fs/validate/Validator.py | 16 +- amitools/rom/kickrom.py | 3 +- amitools/rom/rompatcher.py | 41 +- amitools/rom/romsplitter.py | 2 +- amitools/scan/FileScanner.py | 4 +- amitools/tools/fdtool.py | 4 +- amitools/tools/geotool.py | 7 +- amitools/tools/hunktool.py | 4 +- amitools/tools/rdbtool.py | 62 +- amitools/tools/romtool.py | 24 +- amitools/tools/typetool.py | 4 +- amitools/tools/vamos.py | 12 +- amitools/tools/vamospath.py | 4 +- amitools/tools/vamostool.py | 4 +- amitools/tools/xdfscan.py | 4 +- amitools/tools/xdftool.py | 4 +- amitools/util/ByteSize.py | 6 +- amitools/vamos/__init__.py | 2 +- amitools/vamos/astructs/__init__.py | 15 +- amitools/vamos/astructs/access.py | 68 +- amitools/vamos/astructs/array.py | 77 ++ amitools/vamos/astructs/astruct.py | 710 +++++++++--------- amitools/vamos/astructs/astructdef.py | 253 ++----- amitools/vamos/astructs/baddr.py | 67 -- .../vamos/{atypes => astructs}/bitfield.py | 108 ++- amitools/vamos/astructs/dos.py | 338 --------- amitools/vamos/astructs/dump.py | 103 +++ amitools/vamos/{atypes => astructs}/enum.py | 85 +-- amitools/vamos/astructs/exec_.py | 298 -------- amitools/vamos/astructs/pointer.py | 214 ++++++ amitools/vamos/astructs/scalar.py | 132 ++++ amitools/vamos/astructs/string.py | 196 +++++ amitools/vamos/astructs/typebase.py | 150 ++++ amitools/vamos/astructs/util.py | 21 - amitools/vamos/atypes/__init__.py | 20 - amitools/vamos/atypes/atype.py | 142 ---- amitools/vamos/atypes/atypedef.py | 236 ------ amitools/vamos/atypes/bstring.py | 97 --- amitools/vamos/atypes/cstring.py | 92 --- amitools/vamos/atypes/execlib.py | 71 -- amitools/vamos/atypes/library.py | 155 ---- amitools/vamos/atypes/list_.py | 179 ----- amitools/vamos/atypes/lock.py | 13 - amitools/vamos/atypes/msg.py | 44 -- amitools/vamos/atypes/node.py | 105 --- amitools/vamos/atypes/process.py | 28 - amitools/vamos/atypes/resident.py | 134 ---- amitools/vamos/atypes/task.py | 46 -- amitools/vamos/cfgcore/main.py | 22 +- amitools/vamos/lib/DosLibrary.py | 28 +- amitools/vamos/lib/ExecLibrary.py | 33 +- amitools/vamos/lib/LibList.py | 2 +- amitools/vamos/lib/LocaleLibrary.py | 109 ++- amitools/vamos/lib/TimerDevice.py | 25 +- amitools/vamos/lib/VamosTestLibrary.py | 88 ++- amitools/vamos/lib/dos/Args.py | 20 +- amitools/vamos/lib/dos/CSource.py | 2 +- amitools/vamos/lib/dos/DosList.py | 5 +- amitools/vamos/lib/dos/FileHandle.py | 2 +- amitools/vamos/lib/dos/FileManager.py | 6 +- amitools/vamos/lib/dos/Lock.py | 11 +- amitools/vamos/lib/dos/LockManager.py | 6 +- amitools/vamos/lib/dos/MatchFirstNext.py | 4 +- amitools/vamos/lib/dos/Process.py | 14 +- amitools/vamos/lib/dos/SysArgs.py | 12 +- amitools/vamos/lib/lexec/Alloc.py | 59 +- amitools/vamos/lib/lexec/ExecLibCtx.py | 3 +- amitools/vamos/lib/lexec/PortManager.py | 2 +- amitools/vamos/lib/lexec/SemaphoreManager.py | 3 +- amitools/vamos/lib/util/AmiDate.py | 7 +- amitools/vamos/lib/util/TagList.py | 3 +- amitools/vamos/libcore/__init__.py | 5 +- amitools/vamos/libcore/create.py | 21 +- amitools/vamos/libcore/ctx.py | 18 - amitools/vamos/libcore/impl.py | 145 +++- amitools/vamos/libcore/mgr.py | 32 +- amitools/vamos/libcore/profile.py | 27 +- amitools/vamos/libcore/proxy.py | 115 +++ amitools/vamos/libcore/stub.py | 164 ++-- amitools/vamos/libcore/vlib.py | 14 +- amitools/vamos/libmgr/__init__.py | 1 + amitools/vamos/libmgr/mgr.py | 36 +- amitools/vamos/libmgr/proxy.py | 86 +++ amitools/vamos/libmgr/setup.py | 19 +- amitools/vamos/libnative/initresident.py | 33 +- amitools/vamos/libnative/libfuncs.py | 7 +- amitools/vamos/libnative/loader.py | 6 +- amitools/vamos/libnative/makefuncs.py | 4 +- amitools/vamos/libnative/makelib.py | 10 +- amitools/vamos/libnative/mgr.py | 25 +- amitools/vamos/libstructs/__init__.py | 3 + amitools/vamos/libstructs/dos.py | 369 +++++++++ amitools/vamos/libstructs/exec_.py | 437 +++++++++++ amitools/vamos/libstructs/util.py | 25 + amitools/vamos/libtypes/__init__.py | 12 + amitools/vamos/libtypes/execlib.py | 25 + amitools/vamos/libtypes/library.py | 104 +++ amitools/vamos/libtypes/list_.py | 157 ++++ amitools/vamos/libtypes/lock.py | 12 + amitools/vamos/libtypes/msg.py | 22 + amitools/vamos/libtypes/node.py | 54 ++ amitools/vamos/libtypes/process.py | 20 + amitools/vamos/libtypes/resident.py | 64 ++ amitools/vamos/libtypes/task.py | 14 + amitools/vamos/loader/segload.py | 2 +- amitools/vamos/log.py | 4 + amitools/vamos/machine/hwaccess.py | 8 +- amitools/vamos/machine/machine.py | 52 +- amitools/vamos/machine/regs.py | 20 + amitools/vamos/main.py | 20 +- amitools/vamos/mem/alloc.py | 46 +- amitools/vamos/mem/cache.py | 2 +- amitools/vamos/path/amipath.py | 98 +-- amitools/vamos/path/assign.py | 12 +- amitools/vamos/path/mgr.py | 120 +-- amitools/vamos/path/spec.py | 6 +- amitools/vamos/path/vamos.py | 6 +- amitools/vamos/path/volume.py | 36 +- amitools/vamos/schedule/scheduler.py | 22 +- amitools/vamos/tools/type.py | 36 +- amitools/vamos/trace/mgr.py | 20 +- docs/conf.py | 4 +- setup.py | 8 +- test/Makefile | 2 +- test/bin/test_execpy_agcc | Bin 0 -> 9440 bytes test/bin/test_execpy_agcc_dbg | Bin 0 -> 22900 bytes test/bin/test_execpy_gcc | Bin 0 -> 2364 bytes test/bin/test_execpy_gcc_dbg | Bin 0 -> 21344 bytes test/bin/test_execpy_sc | Bin 0 -> 16072 bytes test/bin/test_execpy_sc_dbg | Bin 0 -> 16172 bytes test/bin/test_execpy_vc | Bin 0 -> 1352 bytes test/bin/test_execpy_vc_dbg | Bin 0 -> 2116 bytes test/conftest.py | 33 +- test/extra/shell_seg.py | 28 +- test/helper/runner.py | 187 +++-- test/include/Makefile | 12 +- test/include/clib/vamostest_protos.h | 1 + test/include/defines/testnix.h | 19 +- test/include/defines/vamostest.h | 67 +- test/include/inline/vamostest.h | 9 + test/include/inline/vamostest_protos.h | 3 + test/include/pragma/vamostest_lib.h | 2 + test/include/sfd/testnix_lib.sfd | 10 + test/include/sfd/vamostest_lib.sfd | 16 + test/src/test_execpy.c | 19 + test/suite/dos_examine.py | 15 +- test/suite/dos_stdout.py | 5 +- test/suite/dos_system.py | 2 +- test/suite/test_execpy.py | 65 ++ test/tools/typetool.py | 27 + test/tools/xdftool.py | 4 +- test/unit/astructs_access.py | 65 +- test/unit/astructs_array.py | 46 ++ test/unit/astructs_astruct.py | 416 +++++++--- test/unit/astructs_baddr.py | 41 - test/unit/astructs_bitfield.py | 80 ++ test/unit/astructs_dump.py | 124 +++ test/unit/astructs_enum.py | 44 ++ test/unit/astructs_pointer.py | 98 +++ test/unit/astructs_scalar.py | 106 +++ test/unit/astructs_string.py | 167 ++++ test/unit/atypes_atype.py | 180 ----- test/unit/atypes_bitfield.py | 64 -- test/unit/atypes_bstring.py | 76 -- test/unit/atypes_cstring.py | 73 -- test/unit/atypes_enum.py | 33 - test/unit/atypes_execlib.py | 20 - test/unit/atypes_library.py | 115 --- test/unit/atypes_msg.py | 45 -- test/unit/atypes_node.py | 123 --- test/unit/atypes_resident.py | 95 --- test/unit/atypes_task.py | 25 - test/unit/cfg_path.py | 9 +- test/unit/dos_args.py | 1 - test/unit/dos_item.py | 1 - test/unit/fd_format.py | 6 +- test/unit/libcore_create.py | 6 +- test/unit/libcore_impl.py | 88 ++- test/unit/libcore_mgr.py | 24 +- test/unit/libcore_proxy.py | 133 ++++ test/unit/libcore_stub.py | 44 +- test/unit/libmgr_mgr.py | 36 +- test/unit/libnative_initres.py | 36 +- test/unit/libnative_libfuncs.py | 55 +- test/unit/libnative_loader.py | 22 +- test/unit/libnative_makelib.py | 12 +- test/unit/libnative_mgr.py | 19 +- test/unit/libstructs_dos.py | 9 + test/unit/libstructs_exec.py | 29 + test/unit/libstructs_util.py | 9 + test/unit/libtypes_execlib.py | 24 + test/unit/libtypes_library.py | 112 +++ .../unit/{atypes_list.py => libtypes_list.py} | 52 +- test/unit/libtypes_msg.py | 46 ++ test/unit/libtypes_node.py | 122 +++ ...{atypes_process.py => libtypes_process.py} | 35 +- test/unit/libtypes_resident.py | 96 +++ test/unit/libtypes_task.py | 26 + test/unit/machine_hwaccess.py | 2 + test/unit/profiler_main.py | 12 +- test/unit/trace_mem.py | 2 +- test/unit/trace_mgr.py | 10 +- tox.ini | 6 +- 225 files changed, 6607 insertions(+), 4747 deletions(-) create mode 100644 amitools/vamos/astructs/array.py delete mode 100644 amitools/vamos/astructs/baddr.py rename amitools/vamos/{atypes => astructs}/bitfield.py (63%) delete mode 100644 amitools/vamos/astructs/dos.py create mode 100644 amitools/vamos/astructs/dump.py rename amitools/vamos/{atypes => astructs}/enum.py (50%) delete mode 100644 amitools/vamos/astructs/exec_.py create mode 100644 amitools/vamos/astructs/pointer.py create mode 100644 amitools/vamos/astructs/scalar.py create mode 100644 amitools/vamos/astructs/string.py create mode 100644 amitools/vamos/astructs/typebase.py delete mode 100644 amitools/vamos/astructs/util.py delete mode 100644 amitools/vamos/atypes/__init__.py delete mode 100644 amitools/vamos/atypes/atype.py delete mode 100644 amitools/vamos/atypes/atypedef.py delete mode 100644 amitools/vamos/atypes/bstring.py delete mode 100644 amitools/vamos/atypes/cstring.py delete mode 100644 amitools/vamos/atypes/execlib.py delete mode 100644 amitools/vamos/atypes/library.py delete mode 100644 amitools/vamos/atypes/list_.py delete mode 100644 amitools/vamos/atypes/lock.py delete mode 100644 amitools/vamos/atypes/msg.py delete mode 100644 amitools/vamos/atypes/node.py delete mode 100644 amitools/vamos/atypes/process.py delete mode 100644 amitools/vamos/atypes/resident.py delete mode 100644 amitools/vamos/atypes/task.py create mode 100644 amitools/vamos/libcore/proxy.py create mode 100644 amitools/vamos/libmgr/proxy.py create mode 100644 amitools/vamos/libstructs/__init__.py create mode 100644 amitools/vamos/libstructs/dos.py create mode 100644 amitools/vamos/libstructs/exec_.py create mode 100644 amitools/vamos/libstructs/util.py create mode 100644 amitools/vamos/libtypes/__init__.py create mode 100644 amitools/vamos/libtypes/execlib.py create mode 100644 amitools/vamos/libtypes/library.py create mode 100644 amitools/vamos/libtypes/list_.py create mode 100644 amitools/vamos/libtypes/lock.py create mode 100644 amitools/vamos/libtypes/msg.py create mode 100644 amitools/vamos/libtypes/node.py create mode 100644 amitools/vamos/libtypes/process.py create mode 100644 amitools/vamos/libtypes/resident.py create mode 100644 amitools/vamos/libtypes/task.py create mode 100755 test/bin/test_execpy_agcc create mode 100755 test/bin/test_execpy_agcc_dbg create mode 100755 test/bin/test_execpy_gcc create mode 100755 test/bin/test_execpy_gcc_dbg create mode 100755 test/bin/test_execpy_sc create mode 100755 test/bin/test_execpy_sc_dbg create mode 100755 test/bin/test_execpy_vc create mode 100755 test/bin/test_execpy_vc_dbg create mode 100644 test/include/sfd/testnix_lib.sfd create mode 100644 test/include/sfd/vamostest_lib.sfd create mode 100644 test/src/test_execpy.c create mode 100644 test/suite/test_execpy.py create mode 100644 test/unit/astructs_array.py delete mode 100644 test/unit/astructs_baddr.py create mode 100644 test/unit/astructs_bitfield.py create mode 100644 test/unit/astructs_dump.py create mode 100644 test/unit/astructs_enum.py create mode 100644 test/unit/astructs_pointer.py create mode 100644 test/unit/astructs_scalar.py create mode 100644 test/unit/astructs_string.py delete mode 100644 test/unit/atypes_atype.py delete mode 100644 test/unit/atypes_bitfield.py delete mode 100644 test/unit/atypes_bstring.py delete mode 100644 test/unit/atypes_cstring.py delete mode 100644 test/unit/atypes_enum.py delete mode 100644 test/unit/atypes_execlib.py delete mode 100644 test/unit/atypes_library.py delete mode 100644 test/unit/atypes_msg.py delete mode 100644 test/unit/atypes_node.py delete mode 100644 test/unit/atypes_resident.py delete mode 100644 test/unit/atypes_task.py create mode 100644 test/unit/libcore_proxy.py create mode 100644 test/unit/libstructs_dos.py create mode 100644 test/unit/libstructs_exec.py create mode 100644 test/unit/libstructs_util.py create mode 100644 test/unit/libtypes_execlib.py create mode 100644 test/unit/libtypes_library.py rename test/unit/{atypes_list.py => libtypes_list.py} (83%) create mode 100644 test/unit/libtypes_msg.py create mode 100644 test/unit/libtypes_node.py rename test/unit/{atypes_process.py => libtypes_process.py} (50%) create mode 100644 test/unit/libtypes_resident.py create mode 100644 test/unit/libtypes_task.py diff --git a/.travis.yml b/.travis.yml index 638ad4a6..39353ccf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,10 @@ sudo: false language: python python: - - "3.4" - - "3.5" - "3.6" - "3.7" - "3.8" + - "3.9" env: - PIP=pip diff --git a/CHANGELOG.md b/CHANGELOG.md index a0b5a839..7629e62a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,40 @@ # Change Log +## [0.6.0][6] (2021-04-13) + +### global + +* Requires Python 3.6 minimum +* Added Python 3.9 support +* Update README to py3 (#153) + +### rdbtool + +* added 'addimg', 'remap', and 'adjust' command + +### xdftool + +* py3 fix for delete with wipe + +### vamos + +* allow fd arguments in func impls +* added ctx func support for testing +* Allocate()/Deallocate() fixes (#152) +* dos.library fixes (@bebbo #151) +* import fixes (@bebbo #151) +* added ReadEClock in timer.device (@bebbo #151) +* added locale.library (@bebbo #151) +* fixed ExNext() +* fixed WriteChars() +* fixed closing console +* added support for 'endcli' +* dos ReadArgs() allow empty key (fixes 'echo') +* trace BADDR fixes +* updated Musashi to 4.10 +* dos pattern match: fixed not-any patterns e.g. ~(#?.o) + + ## [0.5.0][5] (2020-06-13) ### global @@ -14,7 +49,7 @@ * fixed reading HUNK_INDEX with empty unit names * switched disasm to machine DisAsm -### xdftool +### xdftool * added support for HD disk images * in DOS5 (DirCache) fixed creating empty directories @@ -89,7 +124,7 @@ * allow to auto create assign dirs * machine: removed obsolete Trampoline (replaced by machine sub runs) * complete rewrite of config infrastructure. added .json configs -* rewrote lib handling and support creation via MakeLibrary() +* rewrote lib handling and support creation via MakeLibrary() * replaced VamosRun with a machine layer * honor cwd and progdir in OpenLibrary() calls * added GetProgramDir() @@ -131,3 +166,4 @@ [3]: https://github.com/cnvogelg/amitools/tree/v0.3.0 [4]: https://github.com/cnvogelg/amitools/tree/v0.4.0 [5]: https://github.com/cnvogelg/amitools/tree/v0.5.0 +[6]: https://github.com/cnvogelg/amitools/tree/v0.6.0 diff --git a/README.md b/README.md index ed2ed0ed..68b8f00d 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ ## Introduction `amitools` is a collection of Python 3 tools that I've written to work with -*Amiga OS* binaries and files on Mac OS X and all other *nix-like platforms +*Amiga OS* binaries and files on macOS and all other *nix-like platforms supporting Python. Windows might work as well, but is heavily untested. However, patches are welcome. @@ -19,7 +19,7 @@ will be very helpful. ## Prerequisites -- Python >= ```3.5``` +- Python >= ```3.6``` - pip ### Optional Packages @@ -29,11 +29,11 @@ will be very helpful. ### Install pip -First make sure to have the Python package installer ```pip```: +First make sure to have the Python 3 package installer ```pip3```: #### macOS -On macOS you have multiple ways of installing ```pip```: +On macOS you have multiple ways of installing ```pip3```: #### System Python @@ -43,18 +43,18 @@ sudo easy_install pip #### Homebrew Package Manager -With the [Homebrew][3] package manager (```pip``` is included in the ```python3``` package): +With the [Homebrew][3] package manager (```pip3``` is included in the ```python3``` package): ```bash -brew install python +brew install python3 ``` #### Linux/Ubuntu -On Linux Ubuntu use the provided packages ```python-pip``` +On Linux Ubuntu use the provided packages ```python3-pip``` ```bash -sudo apt-get install python-pip +sudo apt-get install python3-pip ``` #### Centos @@ -63,12 +63,12 @@ To get pip run: ```bash curl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py" -python get-pip.py +python3 get-pip.py ``` #### Windows with Visual Studio -- Install the latest native Windows Python >= 3.4 from [python.org][6] +- Install the latest native Windows Python >= 3.6 from [python.org][6] - There is a special Edition for Visual Studio available that allows to compile Python 3.x modules: Install [VCpython3][5] - Open the Command Shell of the Compiler and run @@ -109,8 +109,10 @@ pacman -S mingw-w64-x86_64-python2-pip mingw-w64-x86_64-gcc git make ### The Easy Way for Users +#### Release Version + ```bash -pip install amitools +pip3 install amitools ``` Note: @@ -118,6 +120,15 @@ Note: - on Linux/macOS may use ``sudo`` to install for all users - requires a host C compiler to compile the extension. +#### Current Version from GitHub + +```bash +pip3 install -U git+https://github.com/cnvogelg/amitools.git +``` + +This will install the latest version found in the github repository. +You find the latest features but it may also be unstable from time to time. + ### Developers - Follow this route if you want to hack around with the amitools codebase @@ -125,7 +136,7 @@ Note: - Ensure to have Cython (version >= **0.25**) installed: ```bash -sudo pip install cython +sudo pip3 install cython ``` You have multiple variants to install the tools with Python's `setuptools`: @@ -133,20 +144,20 @@ You have multiple variants to install the tools with Python's `setuptools`: - **Global Install** is available for all users of your system and needs root privileges ```bash -sudo python setup.py install +sudo python3 setup.py install ``` - **User Install** is available for your user only but does not require special privileges ```bash -python setup.py install --user +python3 setup.py install --user ``` - **Developer Setup** only links this code into your installation and allows you to change/develop the code and test it immediately. (I prefer user install here) ```bash -python setup.py develop --user +python3 setup.py develop --user ``` - **Run In Place** allows you to run the binaries directly from the `bin` directory @@ -154,13 +165,20 @@ python setup.py develop --user of vamos: ```bash -python setup.py build_ext -i +python3 setup.py build_ext -i ``` or if you have installed `GNU make` simply use: ```bash -make +make init # global or virtualenv setup +make init_user # user setup +``` + +For more help on the `make` targets run: + +```bash +make help ``` ## Contents diff --git a/amitools/binfmt/BinImage.py b/amitools/binfmt/BinImage.py index a32670e6..b1c3f19c 100644 --- a/amitools/binfmt/BinImage.py +++ b/amitools/binfmt/BinImage.py @@ -11,9 +11,12 @@ bin_image_type_names = ["hunk", "elf"] +BIN_IMAGE_RELOC_32 = 1 +BIN_IMAGE_RELOC_PC32 = 4 + class Reloc: - def __init__(self, offset, type, width=2, addend=0): + def __init__(self, offset, type=BIN_IMAGE_RELOC_32, width=2, addend=0): self.offset = offset self.type = type self.width = width @@ -247,8 +250,7 @@ def find_debug_line(self, offset): class BinImage: - """A binary image contains all the segments of a program's binary image. - """ + """A binary image contains all the segments of a program's binary image.""" def __init__(self, file_type): self.segments = [] diff --git a/amitools/binfmt/Relocate.py b/amitools/binfmt/Relocate.py index 2bd080d0..f5e3d5ec 100644 --- a/amitools/binfmt/Relocate.py +++ b/amitools/binfmt/Relocate.py @@ -1,4 +1,5 @@ import struct +from .BinImage import BIN_IMAGE_RELOC_32, BIN_IMAGE_RELOC_PC32 class Relocate: @@ -88,12 +89,12 @@ def _reloc(self, my_id, data, reloc, my_addr, to_addr, to_id, extra_offset): """relocate one entry""" offset = reloc.get_offset() + extra_offset delta = self._read_long(data, offset) + reloc.addend - if reloc.get_type() == 1: + if reloc.get_type() == BIN_IMAGE_RELOC_32: addr = to_addr + delta - elif reloc.get_type() == 4: + elif reloc.get_type() == BIN_IMAGE_RELOC_PC32: addr = delta + to_addr - my_addr - offset else: - raise(Exception("unsupported type %d" % reloc.get_type())) + raise (Exception("unsupported type %d" % reloc.get_type())) self._write_long(data, offset, addr) if self.verbose: print( diff --git a/amitools/binfmt/elf/BinFmtELF.py b/amitools/binfmt/elf/BinFmtELF.py index f8b3470e..596e64e8 100644 --- a/amitools/binfmt/elf/BinFmtELF.py +++ b/amitools/binfmt/elf/BinFmtELF.py @@ -1,5 +1,19 @@ -from amitools.binfmt.BinImage import BIN_IMAGE_TYPE_ELF, SEGMENT_TYPE_CODE, SEGMENT_TYPE_DATA, SEGMENT_FLAG_READ_ONLY, SEGMENT_TYPE_BSS, Segment, Relocations,\ - SymbolTable, Symbol, Reloc, DebugLine, DebugLineFile, DebugLineEntry, BinImage +from amitools.binfmt.BinImage import ( + BIN_IMAGE_TYPE_ELF, + SEGMENT_TYPE_CODE, + SEGMENT_TYPE_DATA, + SEGMENT_FLAG_READ_ONLY, + SEGMENT_TYPE_BSS, + Segment, + Relocations, + SymbolTable, + Symbol, + Reloc, + DebugLine, + DebugLineFile, + DebugLineEntry, + BinImage, +) from .ELFFile import ELFIdentifier, ELFHeader, ELFParseError from .ELF import EM_68K, ELFOSABI_AROS, ELFOSABI_SYSV from .ELFReader import ELFReader diff --git a/amitools/binfmt/elf/DwarfDebugLine.py b/amitools/binfmt/elf/DwarfDebugLine.py index 09e1f6e5..9a223206 100644 --- a/amitools/binfmt/elf/DwarfDebugLine.py +++ b/amitools/binfmt/elf/DwarfDebugLine.py @@ -26,17 +26,14 @@ def clone(self): return state def __str__(self): - return ( - "[address=%08x file=%d line=%d column=%d is_stmt=%s basic_block=%s end_sequence=%d]" - % ( - self.address, - self.file, - self.line, - self.column, - self.is_stmt, - self.basic_block, - self.end_sequence, - ) + return "[address=%08x file=%d line=%d column=%d is_stmt=%s basic_block=%s end_sequence=%d]" % ( + self.address, + self.file, + self.line, + self.column, + self.is_stmt, + self.basic_block, + self.end_sequence, ) diff --git a/amitools/binfmt/elf/ELFReader.py b/amitools/binfmt/elf/ELFReader.py index 9939972a..1c2e86ee 100644 --- a/amitools/binfmt/elf/ELFReader.py +++ b/amitools/binfmt/elf/ELFReader.py @@ -73,7 +73,7 @@ def _resolve_symtab_indices(self, sect, ef, sections): if sym.shndx_str == None: # refers a valid section idx = sym.shndx - if idx == 65522: # common + if idx == 65522: # common sym.section = ef.bss sym.value = ef.bss.header.size ef.bss.header.size += sym.size + 3 & ~3 @@ -159,7 +159,7 @@ def _resolve_rela_links(self, sect, sections): def load(self, f): """load an ELF file from the given file object f - and return an ELFFile instance or None if loading failed""" + and return an ELFFile instance or None if loading failed""" ef = ELFFile() diff --git a/amitools/binfmt/hunk/HunkBlockFile.py b/amitools/binfmt/hunk/HunkBlockFile.py index 450a377b..b6a66225 100644 --- a/amitools/binfmt/hunk/HunkBlockFile.py +++ b/amitools/binfmt/hunk/HunkBlockFile.py @@ -35,8 +35,8 @@ def _read_word(self, f): def _read_name(self, f): """read name stored in longs - return size, string - """ + return size, string + """ num_longs = self._read_long(f) if num_longs == 0: return 0, "" @@ -718,7 +718,7 @@ def detect_type(self): def peek_type(self, f): """look into given file obj stream to determine file format. - stream is read and later on seek'ed back.""" + stream is read and later on seek'ed back.""" pos = f.tell() tag = f.read(4) # EOF diff --git a/amitools/data/fd/vamostest_lib.fd b/amitools/data/fd/vamostest_lib.fd index 3d8cb22f..9e73874f 100644 --- a/amitools/data/fd/vamostest_lib.fd +++ b/amitools/data/fd/vamostest_lib.fd @@ -7,4 +7,5 @@ Add(a,b)(d0,d1) Swap(a,b)(d0,d1) Dummy(a,b)(d0,d1) RaiseError(str)(a0) +ExecutePy(argc,argv)(d0,a0) ##end diff --git a/amitools/data/fd/vamostestdev_lib.fd b/amitools/data/fd/vamostestdev_lib.fd index d6acfe5d..1fa18e39 100644 --- a/amitools/data/fd/vamostestdev_lib.fd +++ b/amitools/data/fd/vamostestdev_lib.fd @@ -1,5 +1,5 @@ ##base _VamosTestDevBase ##bias 42 ##public -Add(a,b)(a0/a1) +Add(a,b)(d0/d1) ##end diff --git a/amitools/data/splitdata/README b/amitools/data/splitdata/README index b4bd5eba..e6a4909a 100644 --- a/amitools/data/splitdata/README +++ b/amitools/data/splitdata/README @@ -8,6 +8,7 @@ http://www.doobreynet.co.uk Current version: ROMsplit 1.25 +Remus Datafiles Update 1.78u2 http://www.doobreynet.co.uk/beta/index.html diff --git a/amitools/data/splitdata/ks40.dat b/amitools/data/splitdata/ks40.dat index 497644926d577deaec3b2f9c9cfbfe2aead1e32b..4304194e4304859fe47467f0db2af1f0ebc29997 100644 GIT binary patch delta 56197 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 delta 49735 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 diff --git a/amitools/fs/ADFSVolume.py b/amitools/fs/ADFSVolume.py index 6ed599d8..f4d0e28e 100644 --- a/amitools/fs/ADFSVolume.py +++ b/amitools/fs/ADFSVolume.py @@ -185,8 +185,8 @@ def get_file_path_name(self, path_name): def get_create_path_name(self, path_name, suggest_name=None): """get a parent node and path name for creation - return: parent_node_or_none, file_name_or_none - """ + return: parent_node_or_none, file_name_or_none + """ # make sure input is correct if not isinstance(path_name, FSString): raise ValueError("get_create_path_name's path_name must be a FSString") diff --git a/amitools/fs/DosType.py b/amitools/fs/DosType.py index 6d5c47d6..5558f409 100644 --- a/amitools/fs/DosType.py +++ b/amitools/fs/DosType.py @@ -42,8 +42,8 @@ def parse_dos_type_str(string): """parse a dos type string - return None if its invalid or dostype value - """ + return None if its invalid or dostype value + """ comp = string.split("+") if "ffs" in comp: if "dc" in comp or "dircache" in comp: diff --git a/amitools/fs/FSString.py b/amitools/fs/FSString.py index 6d1afde2..ea654668 100644 --- a/amitools/fs/FSString.py +++ b/amitools/fs/FSString.py @@ -4,13 +4,13 @@ class FSString: """Simple string class that allows to manage strings encoded in Latin-1 used for the Amiga FS. - It stores the string internally as a python UTF-8 string but allows to convert to Amiga format. - """ + It stores the string internally as a python UTF-8 string but allows to convert to Amiga format. + """ def __init__(self, txt="", encoding="Latin-1"): """Init the string. Either with a string or with bytes. - If the latter is given then the "encoding" flag determines the encoding. - """ + If the latter is given then the "encoding" flag determines the encoding. + """ if type(txt) is str: self.txt = txt elif type(txt) is bytes: diff --git a/amitools/fs/blkdev/DiskGeometry.py b/amitools/fs/blkdev/DiskGeometry.py index 4c5fafcc..651e715c 100644 --- a/amitools/fs/blkdev/DiskGeometry.py +++ b/amitools/fs/blkdev/DiskGeometry.py @@ -37,8 +37,8 @@ def _update_block_size(self, options): def detect(self, byte_size, options=None): """detect a geometry from a given image size and optional options. - return bytes required by geometry or None if geomtry is invalid - """ + return bytes required by geometry or None if geomtry is invalid + """ c = None h = None s = None @@ -63,8 +63,8 @@ def detect(self, byte_size, options=None): def setup(self, options, cyls=None, heads=None, sectors=None): """setup a new geometry by giving options - return bytes required by geometry or None if geometry is invalid - """ + return bytes required by geometry or None if geometry is invalid + """ if options is None: return False (c, h, s) = self._parse_chs(options) diff --git a/amitools/fs/rdb/Partition.py b/amitools/fs/rdb/Partition.py index 1e325761..401b79f6 100644 --- a/amitools/fs/rdb/Partition.py +++ b/amitools/fs/rdb/Partition.py @@ -152,13 +152,11 @@ def import_data(self, file_name): # check sizes if total < file_size: raise ValueError( - "import image too large: partition=%d != file=%d" % - (total, file_size) + "import image too large: partition=%d != file=%d" % (total, file_size) ) if total > file_size: raise ValueError( - "import image too small: partition=%d != file=%d" % - (total, file_size) + "import image too small: partition=%d != file=%d" % (total, file_size) ) # copy image with open(file_name, "rb") as fh: diff --git a/amitools/fs/rdb/RDisk.py b/amitools/fs/rdb/RDisk.py index 1b0c214d..c5d3e799 100644 --- a/amitools/fs/rdb/RDisk.py +++ b/amitools/fs/rdb/RDisk.py @@ -326,26 +326,28 @@ def create( self.valid = True def resize(self, new_lo_cyl=None, new_hi_cyl=None, adjust_physical=False): - # if the rdb region has to be minimized then check if no partition + # if the rdb region has to be minimized then check if no partition # is in the way if not new_lo_cyl: new_lo_cyl = self.rdb.log_drv.lo_cyl if not new_hi_cyl: new_hi_cyl = self.rdb.log_drv.hi_cyl # same range? silently ignore and return true - if new_lo_cyl == self.rdb.log_drv.lo_cyl and \ - new_hi_cyl == self.rdb.log_drv.hi_cyl: + if ( + new_lo_cyl == self.rdb.log_drv.lo_cyl + and new_hi_cyl == self.rdb.log_drv.hi_cyl + ): return # invalid range if new_lo_cyl > new_hi_cyl: - raise ValueError("Invalid cylinder range: %d > %d" % - (new_lo_cyl, new_hi_cyl)) + raise ValueError( + "Invalid cylinder range: %d > %d" % (new_lo_cyl, new_hi_cyl) + ) # check partitions for p in self.parts: (lo, hi) = p.get_cyl_range() if lo < new_lo_cyl or hi > new_hi_cyl: - raise ValueError("Partition %d does not fit in new range!" - % p.num) + raise ValueError("Partition %d does not fit in new range!" % p.num) # need to adjust phyisical disc? if new_hi_cyl != self.rdb.phy_drv.cyls - 1: if adjust_physical: @@ -368,8 +370,7 @@ def remap(self, new_heads=None, new_secs=None): if not new_secs: new_secs = old_secs # nothing to do? - if new_heads == old_heads and \ - new_secs == old_secs: + if new_heads == old_heads and new_secs == old_secs: return # validate if remapping is possible old_cyl_blocks = old_heads * old_secs @@ -385,6 +386,7 @@ def remap(self, new_heads=None, new_secs=None): # conversion op def op(x): return x * factor + else: # coarser resolution wanted # check that factor is integer multiple @@ -394,6 +396,7 @@ def op(x): def check(x): return x % factor == 0 + # check physical drive range pd = self.rdb.phy_drv if not check(pd.cyls): @@ -401,14 +404,16 @@ def check(x): # check logical drive range ld = self.rdb.log_drv if not check(ld.lo_cyl) or not check(ld.hi_cyl): - raise ValueError("Logical Range [%d,%d] can't be remapped!" - % (ld.lo_cyl, ld.hi_cyl)) + raise ValueError( + "Logical Range [%d,%d] can't be remapped!" % (ld.lo_cyl, ld.hi_cyl) + ) # check partition lo/hi cyls for p in self.parts: lo, hi = p.get_cyl_range() if not check(lo) or not check(hi): - raise ValueError("Partition %d [%d,%d] can't be remapped!" - % (p.num, lo, hi)) + raise ValueError( + "Partition %d [%d,%d] can't be remapped!" % (p.num, lo, hi) + ) # conversion op def op(x): diff --git a/amitools/fs/validate/BlockScan.py b/amitools/fs/validate/BlockScan.py index c831ef3e..6ef9b30a 100644 --- a/amitools/fs/validate/BlockScan.py +++ b/amitools/fs/validate/BlockScan.py @@ -65,8 +65,8 @@ def __init__(self, blkdev, log, dos_type): def scan_all(self, progress=lambda x: x): """Scan all blocks of the given block device - Return True if there is a chance that a file system will be found there - """ + Return True if there is a chance that a file system will be found there + """ # range to scan begin_blk = self.blkdev.reserved num_blk = self.blkdev.num_blocks - self.blkdev.reserved diff --git a/amitools/fs/validate/DirScan.py b/amitools/fs/validate/DirScan.py index 72844dfd..0d6c52c9 100644 --- a/amitools/fs/validate/DirScan.py +++ b/amitools/fs/validate/DirScan.py @@ -118,8 +118,8 @@ def scan_tree(self, root_blk_num, progress=None): def scan_dir(self, dir_bi, progress): """check a directory by scanning through the hash table entries and follow the chains - Returns (all_chains_ok, dir_obj) - """ + Returns (all_chains_ok, dir_obj) + """ # create new dir info di = DirInfo(dir_bi) self.dirs.append(di) diff --git a/amitools/fs/validate/Validator.py b/amitools/fs/validate/Validator.py index 282d1e53..e04666b7 100644 --- a/amitools/fs/validate/Validator.py +++ b/amitools/fs/validate/Validator.py @@ -24,10 +24,10 @@ def __init__(self, blkdev, min_level, debug=False, progress=None): def scan_boot(self): """Step 1: scan boot block. - Returns (True, x) if boot block has a valid dos type. - Returns (x, True) if boot block is bootable - Invalid checksum of the block is tolerated but remarked. - """ + Returns (True, x) if boot block has a valid dos type. + Returns (x, True) if boot block is bootable + Invalid checksum of the block is tolerated but remarked. + """ # check boot block boot = BootBlock(self.blkdev) boot.read() @@ -48,9 +48,9 @@ def scan_boot(self): def scan_root(self): """Step 2: scan root block. - Try to determine root block from boot block or guess number. - Returns True if the root block could be decoded. - """ + Try to determine root block from boot block or guess number. + Returns True if the root block could be decoded. + """ if self.boot != None: # retrieve root block number from boot block root_blk_num = self.boot.got_root_blk @@ -102,7 +102,7 @@ def scan_root(self): def scan_dir_tree(self): """Step 3: scan directory structure - Return false if structure is not healthy""" + Return false if structure is not healthy""" self.block_scan = BlockScan(self.blkdev, self.log, self.dos_type) self.dir_scan = DirScan(self.block_scan, self.log) ok = self.dir_scan.scan_tree(self.root.blk_num, progress=self.progress) diff --git a/amitools/rom/kickrom.py b/amitools/rom/kickrom.py index c7409701..ce41f481 100644 --- a/amitools/rom/kickrom.py +++ b/amitools/rom/kickrom.py @@ -2,6 +2,7 @@ import os import struct +import logging from .romaccess import RomAccess @@ -40,7 +41,7 @@ def detect_kick_rom(self): def check_header(self): # expect 0x1114 0x4ef9 val = self.read_long(0) - print("Header %08x" % val, self.kib) + logging.debug("Header %08x" % val, self.kib) if self.kib == 512: return val == self.ROMHDR_512K elif self.kib == 256: diff --git a/amitools/rom/rompatcher.py b/amitools/rom/rompatcher.py index d81de13e..3836c274 100644 --- a/amitools/rom/rompatcher.py +++ b/amitools/rom/rompatcher.py @@ -26,7 +26,7 @@ def __init__(self): def apply_patch(self, access, args=None): off = 8 - while off < 0x400: + while off < 0x430: v = access.read_long(off) if v == 0xF80000: v4 = access.read_long(off + 4) @@ -56,6 +56,43 @@ def apply_patch(self, access, args=None): logging.error("Exec Table not found!") return False +class FourMegRomPatch(RomPatch): + def __init__(self): + RomPatch.__init__( + self, "4mb_rom", "Patch Kickstart to support ext ROM with 3584 KiB" + ) + + def apply_patch(self, access, args=None): + off = 8 + while off < 0x430: + v = access.read_long(off) + if v == 0xF80000: + v4 = access.read_long(off + 4) + v8 = access.read_long(off + 8) + vc = access.read_long(off + 0xC) + v10 = access.read_long(off + 0x10) + if ( + v4 == 0x1000000 + and v8 == 0xF00000 + and vc == 0xF80000 + and v10 == 0xFFFFFFFF + ): + vp8 = access.read_long(off - 8) + if vp8 == 0xF80000: + access.write_long(off - 4, 0x1000000) + access.write_long(off, 0x1000000) + access.write_long(off + 4, 0x1400000) + logging.info("@%08x Variant A", off) + return True + else: + access.write_long(off, 0xF00000) + access.write_long(off + 8, 0x1000000) + access.write_long(off + 0xC, 0x1400000) + logging.info("@%08x Variant B", off) + return True + off += 2 + logging.error("Exec Table not found!") + return False class BootConRomPatch(RomPatch): def __init__(self): @@ -100,7 +137,7 @@ def apply_patch(self, access, args): class RomPatcher: # list of all available patch classes - patches = [OneMegRomPatch(), BootConRomPatch()] + patches = [OneMegRomPatch(), FourMegRomPatch(), BootConRomPatch()] def __init__(self, rom): self.access = RomAccess(rom) diff --git a/amitools/rom/romsplitter.py b/amitools/rom/romsplitter.py index d2d11592..fe66b0fb 100644 --- a/amitools/rom/romsplitter.py +++ b/amitools/rom/romsplitter.py @@ -36,7 +36,7 @@ def list_roms(self, out, query=None, show_entries=False): def find_rom(self, rom_path): """load ROM and try to find a matching dat file. - Returns True if ROM was matched""" + Returns True if ROM was matched""" self.rom_data = kickrom.Loader.load(rom_path) # get check sum kh = kickrom.KickRomAccess(self.rom_data) diff --git a/amitools/scan/FileScanner.py b/amitools/scan/FileScanner.py index 98ead5b6..951a7a27 100644 --- a/amitools/scan/FileScanner.py +++ b/amitools/scan/FileScanner.py @@ -20,8 +20,8 @@ def __init__( warning_handler=None, ): """the handler will be called with all the scanned files. - the optional ignore_filters contains a list of glob pattern to - ignore file names""" + the optional ignore_filters contains a list of glob pattern to + ignore file names""" self.handler = handler self.error_handler = error_handler self.warning_handler = warning_handler diff --git a/amitools/tools/fdtool.py b/amitools/tools/fdtool.py index fdfa746c..5df2b105 100755 --- a/amitools/tools/fdtool.py +++ b/amitools/tools/fdtool.py @@ -64,7 +64,7 @@ def generate_sasc_code(fname, fd, add_private, prefix=""): # ----- main ----- -def main(): +def main(args=None): # parse args parser = argparse.ArgumentParser() parser.add_argument("files", nargs="+") @@ -99,7 +99,7 @@ def main(): default="", help="add prefix to functions in C", ) - args = parser.parse_args() + args = parser.parse_args(args=args) # main loop files = args.files diff --git a/amitools/tools/geotool.py b/amitools/tools/geotool.py index d8557657..4307e42a 100644 --- a/amitools/tools/geotool.py +++ b/amitools/tools/geotool.py @@ -9,8 +9,11 @@ from amitools.fs.blkdev.BlkDevFactory import BlkDevFactory -def main(): - a = sys.argv +def main(args=None): + if not args: + a = sys.argv + else: + a = args n = len(a) if n < 3: print( diff --git a/amitools/tools/hunktool.py b/amitools/tools/hunktool.py index 82bfd4be..1a6c935e 100755 --- a/amitools/tools/hunktool.py +++ b/amitools/tools/hunktool.py @@ -216,7 +216,7 @@ def run(self): # ----- main ----- -def main(): +def main(args=None): # call scanner and process all files with selected command cmd_map = { "validate": Validator, @@ -299,7 +299,7 @@ def main(): default="68000", help="disassemble for given cpu (objdump only)", ) - args = parser.parse_args() + args = parser.parse_args(args=args) cmd = args.command if cmd not in cmd_map: diff --git a/amitools/tools/rdbtool.py b/amitools/tools/rdbtool.py index 1628484f..6e4f9ae0 100755 --- a/amitools/tools/rdbtool.py +++ b/amitools/tools/rdbtool.py @@ -328,14 +328,14 @@ def handle_rdisk(self, rdisk): print("Usage: adjust ( auto [force] | lo= hi= [phys] )") return None opts = KeyValue.parse_key_value_strings(self.opts) - if 'auto' in opts: + if "auto" in opts: # automatic mode # get max cyl from image total_blocks = self.blkdev.geo.get_num_blocks() c, h, s = rdisk.get_cyls_heads_secs() num_cyl = total_blocks // (h * s) if num_cyl > 65535: - if 'force' not in opts: + if "force" not in opts: print("ERROR: cylinder count too high:", num_cyl) return 1 lo_cyl = None @@ -343,16 +343,16 @@ def handle_rdisk(self, rdisk): phys = True else: # manual mode - if 'lo' in opts: - lo_cyl = int(opts['lo']) + if "lo" in opts: + lo_cyl = int(opts["lo"]) else: lo_cyl = None - if 'hi' in opts: - hi_cyl = int(opts['hi']) + if "hi" in opts: + hi_cyl = int(opts["hi"]) else: hi_cyl = None - if 'phys' in opts: - phys = opts['phys'] + if "phys" in opts: + phys = opts["phys"] else: phys = False # try to resize @@ -397,12 +397,20 @@ def handle_rdisk(self, rdisk): # blkdev info geo = self.blkdev.geo extra = "heads=%d sectors=%d block_size=%d" % ( - geo.heads, geo.secs, geo.block_bytes + geo.heads, + geo.secs, + geo.block_bytes, + ) + print( + "BlockDevice: %8d %8d %10d %s %s" + % ( + 0, + geo.cyls - 1, + geo.get_num_blocks(), + ByteSize.to_byte_size_str(geo.get_num_bytes()), + extra, + ) ) - print("BlockDevice: %8d %8d %10d %s %s" % - (0, geo.cyls - 1, geo.get_num_blocks(), - ByteSize.to_byte_size_str(geo.get_num_bytes()), - extra)) lines = rdisk.get_info(part_name) for l in lines: print(l) @@ -615,8 +623,7 @@ def handle_rdisk(self, rdisk): boot_pri = self.get_boot_pri() more_dos_env = self.get_more_dos_env() fs_bs = self.get_fs_block_size(empty=True) - print("creating: '%s' %s %s" % (drv_name, lo_hi, - num_to_tag_str(dostype))) + print("creating: '%s' %s %s" % (drv_name, lo_hi, num_to_tag_str(dostype))) # add partition rdisk.add_partition( drv_name, @@ -647,13 +654,16 @@ def handle_rdisk(self, rdisk): return 1 num_cyls = file_size // cyl_bytes # get cyl start - if 'start' in self.popts: - start = int(self.popts['start']) + if "start" in self.popts: + start = int(self.popts["start"]) else: start = rdisk.find_free_cyl_range_start(num_cyls) if not start: - print("ERROR: no partition region found for image with", - num_cyls, "cylinders!") + print( + "ERROR: no partition region found for image with", + num_cyls, + "cylinders!", + ) return 1 lo_hi = (start, start + num_cyls - 1) # more options @@ -668,8 +678,10 @@ def handle_rdisk(self, rdisk): boot_pri = self.get_boot_pri() more_dos_env = self.get_more_dos_env() fs_bs = self.get_fs_block_size(empty=True) - print("creating: '%s' %s %s from '%s'" % (drv_name, lo_hi, - num_to_tag_str(dostype), file_name)) + print( + "creating: '%s' %s %s from '%s'" + % (drv_name, lo_hi, num_to_tag_str(dostype), file_name) + ) # add partition p = rdisk.add_partition( drv_name, @@ -965,20 +977,20 @@ def handle_rdisk(self, rdisk): # ----- main ----- -def main(argv=None, defaults=None): +def main(args=None, defaults=None): # call scanner and process all files with selected command cmd_map = { "open": OpenCommand, "create": CreateCommand, "resize": ResizeCommand, "init": InitCommand, - "adjust" : AdjustCommand, + "adjust": AdjustCommand, "remap": RemapCommand, "info": InfoCommand, "show": ShowCommand, "free": FreeCommand, "add": AddCommand, - "addimg" : AddImageCommand, + "addimg": AddImageCommand, "fill": FillCommand, "fsget": FSGetCommand, "fsadd": FSAddCommand, @@ -1027,7 +1039,7 @@ def main(argv=None, defaults=None): ) if defaults: parser.set_defaults(defaults) - args = parser.parse_args(argv) + args = parser.parse_args(args) cmd_list = args.command_list sep = args.seperator diff --git a/amitools/tools/romtool.py b/amitools/tools/romtool.py index 6cb92ee0..c2824ab3 100644 --- a/amitools/tools/romtool.py +++ b/amitools/tools/romtool.py @@ -241,6 +241,7 @@ def do_diff_cmd(args): print_hex_diff( rom_a, rom_b, num=args.columns, show_same=args.same, base_addr=base_addr ) + return 0 def do_dump_cmd(args): @@ -258,6 +259,7 @@ def do_dump_cmd(args): logging.error("Not a KickROM! Can't detect base address.") return 3 print_hex(rom_img, num=args.columns, base_addr=base_addr) + return 0 def do_copy_cmd(args): @@ -272,6 +274,7 @@ def do_copy_cmd(args): logging.info("saving ROM to '%s'", out_img) with open(out_img, "wb") as fh: fh.write(kh.get_data()) + return 0 def do_info_cmd(args): @@ -301,6 +304,7 @@ def do_info_cmd(args): v = ["%-20s %s" % (x[0], x[1] % x[2]) for x in values] for i in v: print(i) + return 0 def do_patch_cmd(args): @@ -347,6 +351,7 @@ def do_patches_cmd(args): if args_desc is not None: for ad in args_desc: print("%10s %-10s %s" % ("", ad, args_desc[ad])) + return 0 def do_combine_cmd(args): @@ -440,6 +445,7 @@ def do_scan_cmd(args): "@%08x +%08x %-12s %+4d %s %s" % (off, r.skip_off, nt, r.pri, name, id_string) ) + return 0 def setup_list_parser(parser): @@ -651,7 +657,7 @@ def setup_copy_parser(parser): parser.set_defaults(cmd=do_copy_cmd) -def parse_args(): +def parse_args(args=None): """parse args and return (args, opts)""" parser = argparse.ArgumentParser(description=DESC) @@ -711,23 +717,25 @@ def parse_args(): setup_copy_parser(copy_parser) # parse - args = parser.parse_args() - if "cmd" not in args: + opts = parser.parse_args(args=args) + if "cmd" not in opts: parser.print_help() sys.exit(1) - return args + return opts # ----- main ----- -def main(): +def main(args=None): # parse args and init logging - args = parse_args() - setup_logging(args) + opts = parse_args(args) + setup_logging(opts) # say hello logging.info("Welcom to romtool") # run command try: - return args.cmd(args) + result = opts.cmd(opts) + assert type(result) is int + return result except IOError as e: logging.error("IO Error: %s", e) return 1 diff --git a/amitools/tools/typetool.py b/amitools/tools/typetool.py index f7361364..b2a04373 100755 --- a/amitools/tools/typetool.py +++ b/amitools/tools/typetool.py @@ -11,7 +11,7 @@ from amitools.vamos.tools import tools_main, TypeTool -def main(): +def main(args=None): cfg_files = ( # first look in current dir os.path.join(os.getcwd(), ".vamosrc"), @@ -19,7 +19,7 @@ def main(): os.path.expanduser("~/.vamosrc"), ) tools = [TypeTool()] - sys.exit(tools_main(tools, cfg_files)) + return tools_main(tools, cfg_files, args) if __name__ == "__main__": diff --git a/amitools/tools/vamos.py b/amitools/tools/vamos.py index 8214474f..1dc1d335 100755 --- a/amitools/tools/vamos.py +++ b/amitools/tools/vamos.py @@ -12,7 +12,7 @@ from amitools.vamos.main import main_profile -def main(): +def main(args=None): cfg_files = ( # first look in current dir os.path.join(os.getcwd(), ".vamosrc"), @@ -26,12 +26,14 @@ def main(): profile_file = None else: profile_file = vamos_profile - ret_code = main_profile(cfg_files, profile_file=profile_file, dump_profile=True) + ret_code = main_profile( + cfg_files, args=args, profile_file=profile_file, dump_profile=True + ) # regular run else: - ret_code = vmain(cfg_files) - sys.exit(ret_code) + ret_code = vmain(cfg_files, args=args) + return ret_code if __name__ == "__main__": - main() + sys.exit(main()) diff --git a/amitools/tools/vamospath.py b/amitools/tools/vamospath.py index 7229f6e8..0992b0dd 100755 --- a/amitools/tools/vamospath.py +++ b/amitools/tools/vamospath.py @@ -11,7 +11,7 @@ from amitools.vamos.tools import tools_main, PathTool -def main(): +def main(args=None): cfg_files = ( # first look in current dir os.path.join(os.getcwd(), ".vamosrc"), @@ -19,7 +19,7 @@ def main(): os.path.expanduser("~/.vamosrc"), ) tools = [PathTool()] - sys.exit(tools_main(tools, cfg_files)) + return tools_main(tools, cfg_files, args) if __name__ == "__main__": diff --git a/amitools/tools/vamostool.py b/amitools/tools/vamostool.py index dd345002..a40356d1 100644 --- a/amitools/tools/vamostool.py +++ b/amitools/tools/vamostool.py @@ -10,7 +10,7 @@ from amitools.vamos.tools import * -def main(): +def main(args=None): cfg_files = ( # first look in current dir os.path.join(os.getcwd(), ".vamosrc"), @@ -18,7 +18,7 @@ def main(): os.path.expanduser("~/.vamosrc"), ) tools = [PathTool(), TypeTool(), LibProfilerTool()] - sys.exit(tools_main(tools, cfg_files)) + return tools_main(tools, cfg_files, args) if __name__ == "__main__": diff --git a/amitools/tools/xdfscan.py b/amitools/tools/xdfscan.py index 7c02ba31..30bb4446 100755 --- a/amitools/tools/xdfscan.py +++ b/amitools/tools/xdfscan.py @@ -157,7 +157,7 @@ def scan_file(path, args): # ----- main ----- -def main(): +def main(args=None): parser = argparse.ArgumentParser() parser.add_argument( "input", nargs="+", help="input image file or directory (to scan tree)" @@ -196,7 +196,7 @@ def main(): default=False, help="do not scan hard disk images", ) - args = parser.parse_args() + args = parser.parse_args(args=args) # main scan loop ret = 0 diff --git a/amitools/tools/xdftool.py b/amitools/tools/xdftool.py index 8eaea029..093e9b7f 100755 --- a/amitools/tools/xdftool.py +++ b/amitools/tools/xdftool.py @@ -837,7 +837,7 @@ def handle_blkdev(self, blkdev): # ----- main ----- -def main(argv=None, defaults=None): +def main(args=None, defaults=None): # call scanner and process all files with selected command cmd_map = { "open": OpenCmd, @@ -891,7 +891,7 @@ def main(argv=None, defaults=None): ) if defaults: parser.set_defaults(defaults) - args = parser.parse_args(argv) + args = parser.parse_args(args) cmd_list = args.command_list sep = args.seperator diff --git a/amitools/util/ByteSize.py b/amitools/util/ByteSize.py index 978d4312..22e817f6 100644 --- a/amitools/util/ByteSize.py +++ b/amitools/util/ByteSize.py @@ -33,9 +33,9 @@ def to_byte_size_str(size, kibi_units=True): def parse_byte_size_str(s): """parse a string to derive a byte value. - can read e.g. 10Ki, 2.1k or 2048. - returns None if the string is invalid or a byte value - """ + can read e.g. 10Ki, 2.1k or 2048. + returns None if the string is invalid or a byte value + """ s = s.lower() n = len(s) if n == 0: diff --git a/amitools/vamos/__init__.py b/amitools/vamos/__init__.py index c28a133f..5505aa8a 100644 --- a/amitools/vamos/__init__.py +++ b/amitools/vamos/__init__.py @@ -1 +1 @@ -from .main import main +# python module marker diff --git a/amitools/vamos/astructs/__init__.py b/amitools/vamos/astructs/__init__.py index 0cf00270..0c6df639 100644 --- a/amitools/vamos/astructs/__init__.py +++ b/amitools/vamos/astructs/__init__.py @@ -1,7 +1,10 @@ from .access import AccessStruct -from .astruct import AmigaStruct -from .astructdef import AmigaStructDef -from .baddr import BAddr -from .exec_ import * -from .dos import * -from .util import * +from .astruct import AmigaStruct, AmigaStructTypes, APTR_SELF, BPTR_SELF +from .astructdef import AmigaStructDef, AmigaClassDef +from .scalar import ULONG, LONG, UWORD, WORD, UBYTE, BYTE +from .pointer import BPTR, APTR, BPTR_VOID, APTR_VOID, PointerType +from .array import ARRAY, ArrayIter +from .string import CSTR, BSTR +from .dump import TypeDumper +from .bitfield import BitField, BitFieldType +from .enum import Enum, EnumType diff --git a/amitools/vamos/astructs/access.py b/amitools/vamos/astructs/access.py index de11c514..6ac56197 100644 --- a/amitools/vamos/astructs/access.py +++ b/amitools/vamos/astructs/access.py @@ -1,3 +1,7 @@ +from .astruct import AmigaStruct +from .pointer import BCPLPointerType + + class AccessStruct(object): _size_to_width = [None, 0, 1, None, 2] @@ -5,43 +9,45 @@ class AccessStruct(object): def __init__(self, mem, struct_def, struct_addr): self.mem = mem self.struct = struct_def(mem, struct_addr) - self.trace_mgr = getattr(self.mem, "trace_mgr", None) def w_s(self, name, val): - struct, field = self.struct.write_field(name, val) - if self.trace_mgr is not None: - off = field.offset - addr = struct.get_addr() + off - tname = struct.get_type_name() - addon = "%s+%d = %s" % (tname, off, name) - width = self._size_to_width[field.size] - self.trace_mgr.trace_int_mem( - "W", width, addr, val, text="Struct", addon=addon - ) + field, field_def = self._get_field_for_name(name) + # BPTR auto conversion + if issubclass(field_def.type, BCPLPointerType): + field.set_ref_addr(val) + else: + field.set(val) def r_s(self, name): - struct, field, val = self.struct.read_field_ext(name) - if self.trace_mgr is not None: - off = field.offset - addr = struct.get_addr() + off - tname = struct.get_type_name() - addon = "%s+%d = %s" % (tname, off, name) - width = self._size_to_width[field.size] - self.trace_mgr.trace_int_mem( - "R", width, addr, val, text="Struct", addon=addon - ) + field, field_def = self._get_field_for_name(name) + # BPTR auto conversion + if issubclass(field_def.type, BCPLPointerType): + val = field.get_ref_addr() + else: + val = field.get() return val def s_get_addr(self, name): - return self.struct.get_addr_for_name(name) - - def r_all(self): - """return a namedtuple with all values of the struct""" - return self.struct.read_data() - - def w_all(self, nt): - """set values stored in a named tuple""" - self.struct.write_data(nt) + field, _ = self._get_field_for_name(name) + return field.get_addr() def get_size(self): - return self.struct.get_size() + return self.struct.get_byte_size() + + def _get_field_for_name(self, name): + struct = self.struct + field_def = None + field = None + # walk along fields in name "bla.foo.bar" + for field_name in name.split("."): + assert struct is not None + field_def = struct.sdef.find_field_def_by_name(field_name) + if not field_def: + raise KeyError(self, name) + field = struct.sfields.get_field_by_index(field_def.index) + # find potential next struct + if isinstance(field, AmigaStruct): + struct = field + else: + struct = None + return field, field_def diff --git a/amitools/vamos/astructs/array.py b/amitools/vamos/astructs/array.py new file mode 100644 index 00000000..c57cc2e8 --- /dev/null +++ b/amitools/vamos/astructs/array.py @@ -0,0 +1,77 @@ +from .typebase import TypeBase + + +class ArrayType(TypeBase): + _element_type = None + _array_size = None + _element_byte_size = None + + @classmethod + def get_element_type(cls): + return cls._element_type + + @classmethod + def get_array_size(cls): + return cls._array_size + + @classmethod + def get_signature(cls): + return "{}[{}]".format(cls._element_type.get_signature(), cls._array_size) + + def __init__(self, mem, addr, **kwargs): + super(ArrayType, self).__init__(mem, addr, **kwargs) + + def get(self, index): + """Return n-th element in array""" + entry_addr = self._get_entry_addr(index) + cls_type = self._element_type.get_alias_type() + return cls_type(self._mem, entry_addr) + + def _get_entry_addr(self, index): + assert index >= 0 and index < self._array_size + return self.get_addr() + index * self._element_byte_size + + def __getitem__(self, key): + return self.get(key) + + +class ArrayIter: + def __init__(self, array): + self._array = array + self._index = 0 + + def __iter__(self): + return self + + def __next__(self): + a = self._array + if self._index == a.get_array_size(): + raise StopIteration + entry = a.get(self._index) + self._index += 1 + return entry + + +ARRAYTypes = {} + + +def ARRAY(element_type, array_size): + """create a new array type class the references the given type""" + name = "ARRAY[" + str(array_size) + "]_" + element_type.__name__ + if name in ARRAYTypes: + return ARRAYTypes[name] + else: + element_byte_size = element_type.get_byte_size() + byte_size = element_byte_size * array_size + new_type = type( + name, + (ArrayType,), + dict( + _element_type=element_type, + _array_size=array_size, + _element_byte_size=element_byte_size, + _byte_size=byte_size, + ), + ) + ARRAYTypes[name] = new_type + return new_type diff --git a/amitools/vamos/astructs/astruct.py b/amitools/vamos/astructs/astruct.py index 300416c7..b39080ca 100644 --- a/amitools/vamos/astructs/astruct.py +++ b/amitools/vamos/astructs/astruct.py @@ -1,379 +1,405 @@ +import re import collections -from .baddr import BAddr +from .typebase import TypeBase -class AmigaStruct(object): +class APTR_SELF: + """Helper Type to identify pointer to structures of my type""" - # store all children of this class - _struct_pool = {} + pass - # overwrite in derived class! - _format = None - # name all internal types - # and map to (byte width in 2**n, is_bcpl_ptr, signed) - _types = { - "UBYTE": (0, False, False), - "BYTE": (0, False, True), - "char": (0, False, False), - "UWORD": (1, False, False), - "WORD": (1, False, True), - "ULONG": (2, False, False), - "LONG": (2, False, True), - "APTR": (2, False, False), - "BPTR": (2, True, False), - "BSTR": (2, True, False), - "VOIDFUNC": (2, False, False), - "void": (2, False, False), - } - _ptr_type = (2, False, False) - - # these values are filled by the decorator - _type_name = None - _total_size = None - _fields = None - _name_to_field = None - _field_names = None - _num_fields = None - _data_class = None +class BPTR_SELF: + """Helper Type to identify pointer to structures of my type""" - @classmethod - def get_type_name(cls): - return cls._type_name + pass - @classmethod - def get_size(cls): - return cls._total_size - @classmethod - def get_fields(cls): - return cls._fields +class AmigaStructPool: + def __init__(self): + self._pool = {} - @classmethod - def get_field_by_name(cls, name): - return cls._name_to_field[name] + def add_struct(self, cls): + assert issubclass(cls, AmigaStruct) + type_name = cls.sdef.get_type_name() + if not type_name in self._pool: + self._pool[type_name] = cls - @classmethod - def get_field_by_offset(cls, offset): - for f in cls._fields: - end = f.offset + f.size - if offset >= f.offset and offset < end: - return f + def find_struct(self, name): + if name in self._pool: + return self._pool[name] - @classmethod - def get_fields_by_offset(cls, offset): - res = [] - while True: - f = cls.get_field_by_offset(offset) - if f is None: - break - res.append(f) - s = f.struct_type - if not s or f.is_pointer: - break - offset -= f.offset - cls = s - return res + def get_all_structs(self): + return list(self._pool.values()) - @classmethod - def get_fields_by_path(cls, name): - parts = name.split(".") - return cls.get_fields_for_parts(parts) + def get_all_struct_names(self): + return list(self._pool.keys()) - @classmethod - def get_fields_for_parts(cls, parts): - struct = cls - res = [] - for name in parts: - field = struct.get_field_by_name(name) - res.append(field) - struct = field.struct_type - return res - @classmethod - def get_field_offset_by_name(cls, name): - field = cls.get_field_by_name(name) - return field.offset +# collect all types +AmigaStructTypes = AmigaStructPool() - @classmethod - def get_field_offset_for_path(cls, name): - fields = cls.get_fields_by_path(name) - offsets = [x.offset for x in fields] - return sum(offsets) - @classmethod - def get_field_by_index(cls, idx): - return cls._fields[idx] +FieldDefBase = collections.namedtuple( + "FieldDefBase", ["index", "offset", "type", "name", "size", "struct"] +) - @classmethod - def get_field_names(cls): - return cls._field_names - @classmethod - def get_num_fields(cls): - return cls._num_fields +class FieldDef(FieldDefBase): - @classmethod - def get_data_class(cls): - return cls._data_class + _base_offset = 0 + _parent_def = None + + def copy(self): + return FieldDef( + self.index, self.offset, self.type, self.name, self.size, self.struct + ) + + def get_base_offset(self): + return self._base_offset + self.offset + + def get_parent_def(self): + return self._parent_def + + def get_def_path(self): + # build array of all parent defs (if any) + field_def = self + result = [field_def] + while True: + field_def = field_def.get_parent_def() + if not field_def: + break + result.insert(0, field_def) + return result + + def get_sub_field_by_name(self, name): + # only works for astructs + if issubclass(self.type, AmigaStruct): + sub_field = getattr(self.type.sdef, name) + return sub_field + + def __getattr__(self, name): + """allow to access the field def of a sub astruct by name directly""" + # special names + if name == "base_offset": + return self.get_base_offset() + elif name == "parent_def": + return self._parent_def + # search sub field + sub_field = self.get_sub_field_by_name(name) + if sub_field: + # clone field def and adjust base offset + new_field = sub_field.copy() + new_field._base_offset = self._base_offset + self.offset + new_field._parent_def = self + return new_field + else: + raise AttributeError(name) + + +class AmigaStructFieldDefs: + def __init__(self, type_name): + self._type_name = type_name + self._field_defs = [] + self._name_to_field_def = {} + self._total_size = 0 + self._alias_names = {} + self._alias_type = None + + def get_num_field_defs(self): + return len(self._field_defs) + + def get_total_size(self): + return self._total_size + + def get_alias_type(self): + return self._alias_type + + def add_field_def(self, field_def): + field_name = field_def.name + self._field_defs.append(field_def) + self._name_to_field_def[field_name] = field_def + self._total_size += field_def.size + # find alias name + alias_name = self._to_alias_name(field_def.name) + if alias_name != field_name: + self._alias_names[alias_name] = field_name + + def get_type_name(self): + return self._type_name + + def get_field_defs(self): + return self._field_defs + + def get_field_def(self, idx): + return self._field_defs[idx] + + def __getitem__(self, key): + return self._field_defs[key] + + def find_field_def_by_name(self, name): + fdef = self._name_to_field_def.get(name) + if not fdef: + # try alias + name = self._alias_names.get(name) + if name: + fdef = self._name_to_field_def.get(name) + return fdef + + def __getattr__(self, name): + fdef = self.find_field_def_by_name(name) + if fdef: + return fdef + raise AttributeError("Invalid key '{}'".format(name)) + + def find_field_def_by_offset(self, offset): + """return field_def, delta that matches offset, otherwise None, 0""" + for f in self._field_defs: + end = f.offset + f.size + if offset >= f.offset and offset < end: + delta = offset - f.offset + return f, delta + return None, 0 + + def find_sub_field_defs_by_offset(self, base_offset): + """return array with field_def leading to embedded struct field at offset + + return [field_defs], delta or None, 0 + """ + field_defs = [] + cur_cls = self + offset = base_offset + while True: + field_def, delta = cur_cls.find_field_def_by_offset(offset) + if not field_def: + return None, 0 + field_defs.append(field_def) + # is it an embedded struct? + if not issubclass(field_def.type, AmigaStruct): + break + # new search + offset -= field_def.offset + cur_cls = field_def.type.sdef + return field_defs, delta + + def find_sub_field_defs_by_name(self, *names): + """return array with field_defs or None""" + if not names: + return None + field_defs = [] + cur_cls = self + for name in names: + if not cur_cls: + return None + field_def = cur_cls.find_field_def_by_name(name) + if not field_def: + return None + field_defs.append(field_def) + # next sub class + if issubclass(field_def.type, AmigaStruct): + cur_cls = field_def.type.sdef + return field_defs + + def get_alias_name(self, name): + return self._alias_names.get(name) + + def _to_alias_name(self, name): + """convert camel case names to underscore and remove prefix""" + # strip leading prefix + pos = name.find("_") + if pos > 0: + name = name[pos + 1 :] + # to underscore + s1 = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", name) + return re.sub("([a-z0-9])([A-Z])", r"\1_\2", s1).lower() + + +class AmigaStructFields: + def __init__(self, astruct): + self.astruct = astruct + self.sdef = astruct.sdef + self._fields = [] + self._name_to_field = {} + for field_def in self.sdef.get_field_defs(): + field = self._create_field_type(field_def) + self._fields.append(field) + self._name_to_field[field_def.name] = field + + def get_fields(self): + """return all field instances""" + return self._fields + + def get_field_by_index(self, index): + """return the type instance associated with the field""" + return self._fields[index] + + def get_field_by_name(self, name): + return self._name_to_field.get(name) + + def get_field_by_name_or_alias(self, name, subfield_aliases=None): + field = self._name_to_field.get(name) + if field is None: + # alias name + alias_name = self.sdef.get_alias_name(name) + if alias_name: + field = self._name_to_field.get(alias_name) + # subfield alias + if field is None and subfield_aliases: + field_def_path = subfield_aliases.get(name) + if field_def_path: + return self.find_sub_field_by_def_path(field_def_path) + return field + + def find_field_by_offset(self, offset): + """return field, delta or None, 0""" + field_def, delta = self.sdef.find_field_def_by_offset(offset) + if not field_def: + return None, 0 + return self._fields[field_def.index], delta + + def find_sub_fields_by_offset(self, base_offset): + """return [fields], delta or None, 0""" + cur_self = self + offset = base_offset + fields = [] + while True: + field, delta = cur_self.find_field_by_offset(offset) + if not field: + return None, 0 + fields.append(field) + # is a struct? + if not isinstance(field, AmigaStruct): + break + # next sub struct + cur_self = field.sfields + offset -= field.get_offset() + return fields, delta + + def find_sub_field_by_def(self, field_def): + """return field associated with the field_def""" + def_path = field_def.get_def_path() + return self.find_sub_field_by_def_path(def_path) + + def find_sub_field_by_def_path(self, def_path): + """return field or None""" + field = None + astruct = self.astruct + for field_def in def_path: + idx = field_def.index + assert isinstance(astruct, field_def.struct) + assert field_def == astruct.sdef.get_field_def(idx) + field = astruct.sfields.get_field_by_index(idx) + if not isinstance(field, AmigaStruct): + break + # next + astruct = field + return field + + def find_field_def_by_addr(self, addr): + """return field, delta or None, 0""" + offset = addr - self.astruct._addr + return self.sdef.find_field_def_by_offset(offset) + + def find_field_by_addr(self, addr): + """return field, delta or None, 0""" + offset = addr - self.astruct._addr + return self.find_field_by_offset(offset) + + def find_sub_fields_by_addr(self, addr): + """return [fields], delta or None, 0""" + offset = addr - self.astruct._addr + return self.find_sub_fields_by_offset(offset) + + def _create_field_type(self, field_def): + astruct = self.astruct + addr = astruct._addr + field_def.offset + base_offset = astruct._base_offset + field_def.offset + cls_type = field_def.type.get_alias_type() + field = cls_type( + astruct._mem, addr, offset=field_def.offset, base_offset=base_offset + ) + return field + + +class AmigaStruct(TypeBase): + + # overwrite in derived class! + _format = None + # top-level alias names for subfields + _subfield_aliases = None + _sfdp = None + # the structure definition is filled in by the decorator + sdef = None @classmethod - def find_struct(cls, name): - if name in cls._struct_pool: - return cls._struct_pool[name] + def get_signature(cls): + return cls.sdef.get_type_name() @classmethod - def get_all_structs(cls): - return list(cls._struct_pool.values()) + def get_alias_type(cls): + alias_type = cls.sdef._alias_type + if alias_type: + return alias_type + return cls @classmethod - def get_all_struct_names(cls): - return list(cls._struct_pool.keys()) + def _alloc(cls, alloc, tag, **kwargs): + if tag is None: + tag = cls.get_signature() + return alloc.alloc_struct(tag, cls) @classmethod - def dump_type(self, indent=0, num=0, base=0, name="", data=None): - istr = " " * indent - print(" @%04d %s %s {" % (base, istr, self._type_name)) - i = 0 - for f in self._fields: - offset = f.offset - size = f.size - struct_type = f.struct_type - if struct_type and not f.is_pointer: - if data: - sub_data = data[i] - else: - sub_data = None - num = struct_type.dump_type( - indent=indent + 1, num=num, base=offset, name=f.name, data=sub_data - ) - else: - if data: - data_str = "= %-10s" % str(data[i]) - else: - data_str = "" - print( - "#%04d %04d @%04d/%04x +%04d %s %s %-10s %-20s (ptr=%s, sub=%s)" - % ( - i, - num, - offset, - offset, - size, - istr, - data_str, - f.type_sig, - f.name, - f.is_pointer, - bool(struct_type), - ) - ) - num += 1 - i += 1 - total = self._total_size - off = total + base - print(" @%04d =%04d %s } %s" % (off, total, istr, name)) - return num + def _free(cls, alloc, mem_obj): + alloc.free_struct(mem_obj) # ----- instance ----- - def __init__(self, mem, addr): - self._mem = mem - self._addr = addr - self._parent = None - - def __eq__(self, other): - if type(other) is int: - return self._addr == other - elif isinstance(other, AmigaStruct): - return self._addr == other._addr - else: - return NotImplemented + def __init__(self, mem, addr, **kwargs): + super(AmigaStruct, self).__init__(mem, addr, **kwargs) + # create field instances + self.sfields = AmigaStructFields(self) + # refs to be freed automatically + self._free_refs = [] + # setup fields (if any) + self.setup(kwargs, self._alloc, self._free_refs) + + def setup(self, setup_dict, alloc=None, free_refs=None): + """setup the fields of the struct""" + assert type(setup_dict) is dict + all_refs = [] + for key, val in setup_dict.items(): + field = self.sfields.get_field_by_name_or_alias(key, self._sfdp) + if field: + field.setup(val, alloc, free_refs) + + def free(self): + for free_ref in self._free_refs: + free_ref.free_ref() + super().free() def __str__(self): return "[AStruct:%s,@%06x+%06x]" % ( - self._type_name, + self.sdef.get_type_name(), self._addr, - self._total_size, + self._byte_size, ) - def get_mem(self): - return self._mem - - def get_addr(self): - return self._addr - - def get_parent(self): - return self._parent - - def get_path_name(self): - my_name = self.get_type_name() - if self._parent: - return self._parent.get_path_name() + "." + my_name - else: - return my_name - - def get_field_path_name(self, field): - my_name = field.name - if self._parent: - addr = self._addr + field.offset - st, p_field, _ = self._parent.get_struct_field_for_addr(addr, recurse=False) - return self._parent.get_field_path_name(p_field) + "." + my_name - else: - return my_name - - def read_data(self): - cls = self._data_class - vals = [] - for idx in range(self._num_fields): - val = self.read_field_index(idx) - vals.append(val) - return cls(*vals) - - def write_data(self, data): - for idx in range(self._num_fields): - val = data[idx] - self.write_field_index(idx, val) - - def read_field_index(self, index): - field = self._fields[index] - addr = self._addr + field.offset - # pointer - if field.is_pointer: - val = self._mem.r32(addr) - if field.is_baddr: - val = BAddr(val) - return val - # embedded struct type - elif field.struct_type: - struct = self.create_struct(field) - return struct.read_data() - else: - base_type = field.base_type - width = base_type[0] - is_baddr = base_type[1] - signed = base_type[2] - # read value - if signed: - val = self._mem.reads(width, addr) - else: - val = self._mem.read(width, addr) - # auto convert baddr - if is_baddr: - val = BAddr(val) - return val - - def write_field_index(self, index, val): - field = self._fields[index] - addr = self._addr + field.offset - # pointer - if field.is_pointer: - if field.is_baddr: - if type(val) is not BAddr: - val = BAddr.from_addr(int(val)) - val = val.get_baddr() - self._mem.w32(addr, val) - # embedded struct type - elif field.struct_type: - struct = self.create_struct(field) - return struct.write_data(val) - else: - base_type = field.base_type - width = base_type[0] - is_baddr = base_type[1] - signed = base_type[2] - # convert? - if is_baddr: - if type(val) is not BAddr: - val = BAddr.from_addr(int(val)) - val = val.get_baddr() - # write value - if signed: - self._mem.writes(width, addr, val) - else: - self._mem.write(width, addr, val) - - def read_field(self, name): - struct, field = self.get_struct_field_for_name(name) - return struct.read_field_index(field.index) - - def read_field_ext(self, name): - struct, field = self.get_struct_field_for_name(name) - val = struct.read_field_index(field.index) - return struct, field, val - - def write_field(self, name, val): - struct, field = self.get_struct_field_for_name(name) - struct.write_field_index(field.index, val) - return struct, field - - def dump(self): - data = self.read_data() - self.dump_type(data=data) - - def get_field_addr(self, field): - return self._addr + field.offset - - def get_struct_field_for_addr(self, addr, recurse=True): - return self.get_struct_field_for_offset(addr - self._addr, recurse) - - def get_struct_field_for_offset(self, offset, recurse=True): - """return (struct, field, delta) or None""" - for field in self._fields: - if offset < field.end: - delta = offset - field.offset - # sub struct - if field.struct_type and not field.is_pointer and recurse: - struct = self.create_struct(field) - return struct.get_struct_field_for_offset(delta) - # no sub struct - return self, field, delta - - def get_addr_for_name(self, name): - struct, field = self.get_struct_field_for_name(name) - return struct._addr + field.offset - - def get_struct_field_for_name(self, name): - """return (struct, field)""" - parts = name.split(".") - return self._get_struct_field_for_parts(parts) - - def _get_struct_field_for_parts(self, parts): - name = parts[0] - field = self.get_field_by_name(name) - # last one in path - if len(parts) == 1: - return self, field - # more names: expect embedded struct - else: - struct = self.create_struct(field) - return struct._get_struct_field_for_parts(parts[1:]) - - def create_struct(self, field): - st = field.struct_type - if st is None: - return None - struct = st(self._mem, self._addr + field.offset) - struct._parent = self - return struct - - def __getattr__(self, name): - if name in self._name_to_field: - field = self._name_to_field[name] - if field.struct_type: - # allow to access sub struct - return self.create_struct(field) - else: - # access value - return self.read_field_index(field.index) - else: - raise AttributeError - - def __setattr__(self, name, val): - if name[0] == "_": - object.__setattr__(self, name, val) - elif name in self._name_to_field: - field = self._name_to_field[name] - self.write_field_index(field.index, val) - else: - raise AttributeError + def get(self, field_name): + """return field instance by name""" + return self.sfields.get_field_by_name_or_alias(field_name, self._sfdp) + + def __getattr__(self, field_name): + field = self.get(field_name) + if field is not None: + return field + return super().__getattr__(field_name) + + def __setattr__(self, field_name, val): + if field_name[0] != "_" and field_name != "sfields": + # check for field name and forbid access + field = self.get(field_name) + if field is not None: + raise AttributeError( + "Field {} is read-only in {}".format(field_name, self) + ) + super().__setattr__(field_name, val) diff --git a/amitools/vamos/astructs/astructdef.py b/amitools/vamos/astructs/astructdef.py index 1e6e7646..6f2bac18 100644 --- a/amitools/vamos/astructs/astructdef.py +++ b/amitools/vamos/astructs/astructdef.py @@ -1,186 +1,82 @@ -import collections -from .astruct import AmigaStruct - - -class InvalidAmigaTypeException(Exception): - def __init__(self, type_name): - self.type_name = type_name - - def __str__(self): - return self.type_name - - -Field = collections.namedtuple( - "Field", - [ - "type_sig", - "name", - "offset", - "size", - "end", - "struct_type", - "base_type", - "is_pointer", - "is_baddr", - "index", - ], +from .astruct import ( + AmigaStruct, + AmigaStructTypes, + AmigaStructFieldDefs, + APTR_SELF, + BPTR_SELF, + TypeBase, + FieldDef, ) +from .pointer import APTR, BPTR class AmigaStructDecorator(object): - def decorate(self, cls): - name = self._validate_class(cls) - cls._type_name = name - # store struct pool and class - self.cls = cls - self.types = AmigaStruct._types - self.struct_pool = AmigaStruct._struct_pool - # setup field data - self._setup_fields(cls) - # create data class - self._create_data_class(cls) + def __call__(self, cls): + # check class and store base name (without Struct postfix) + type_name = self._validate_class(cls) + # setup struct def via format + struct_def = self._setup_fields(cls, cls._format, type_name) + cls.sdef = struct_def + # any sub field aliases? + if cls._subfield_aliases: + self._setup_subfield_aliases(cls, cls._subfield_aliases) + cls._byte_size = struct_def.get_total_size() # add to pool - self.struct_pool[name] = cls + AmigaStructTypes.add_struct(cls) return cls - def _create_data_class(self, cls): - names = cls._field_names - data_name = cls._type_name + "Data" - data_cls = collections.namedtuple(data_name, names) - cls._data_class = data_cls - - def _setup_fields(self, cls): - total_size = 0 - offset = 0 - name_to_field = {} - index = 0 - fields = [] - names = [] - my_name = cls._type_name + def _setup_subfield_aliases(self, cls, aliases): + alias_map = {} + for alias, path in aliases.items(): + alias_path = path.split(".") + def_path = cls.sdef.find_sub_field_defs_by_name(*alias_path) + assert def_path + alias_map[alias] = def_path + cls._sfdp = alias_map - # run through fields - for type_sig, name in self.cls._format: - - # check type signature - pure_name = self._validate_type_signature(my_name, type_sig) - - # calc size of field - size = self._lookup_type_size(type_sig) - - # is pointer? struct type or base type? - is_pointer = self._is_pointer(type_sig) - struct_type = self._get_struct_type(pure_name) - base_type = self._get_base_type(pure_name) - is_baddr = self._is_baddr(base_type, type_sig) + def _setup_fields(self, cls, format, type_name): + struct_def = AmigaStructFieldDefs(type_name) - # its my type - if pure_name == my_name: - struct_type = cls - if not is_pointer: - raise RuntimeError("Can't embed myself in struct!") + # run through fields + for field_type, field_name in format: + + # replace self pointers + if field_type is APTR_SELF: + field_type = APTR(cls) + elif field_type is BPTR_SELF: + field_type = BPTR(cls) + + # ensure correct format + if type(field_type) is not type or not issubclass(field_type, TypeBase): + raise RuntimeError( + "invalid field: {}: {} in {}".format( + field_name, field_type, cls.__name__ + ) + ) + + field_size = field_type.get_byte_size() + if field_size is None: + raise RuntimeError( + "invalid field: {}: {} in {}".format( + field_name, field_type, cls.__name__ + ) + ) # create field - field = Field( - type_sig=type_sig, - name=name, - offset=offset, - size=size, - end=offset + size, - struct_type=struct_type, - base_type=base_type, - is_pointer=is_pointer, - is_baddr=is_baddr, + index = struct_def.get_num_field_defs() + offset = struct_def.get_total_size() + field_def = FieldDef( index=index, + offset=offset, + type=field_type, + name=field_name, + size=field_size, + struct=cls, ) - fields.append(field) - - # store name -> index mapping - name_to_field[name] = field - names.append(name) - - # add name to class directly - field_name = name + "_field" - if getattr(cls, field_name, None) is not None: - raise RuntimeError("field '%s' already a member of class!" % name) - setattr(cls, field_name, field) - - index += 1 - offset += size - total_size += size - - # store in class - cls._total_size = total_size - cls._fields = fields - cls._name_to_field = name_to_field - cls._field_names = names - cls._num_fields = index - - def _get_struct_type(self, pure_name): - if pure_name in self.struct_pool: - return self.struct_pool[pure_name] - else: - return None - - def _get_base_type(self, pure_name): - if pure_name in self.types: - return self.types[pure_name] - else: - return None - - def _lookup_type_size(self, full_type_name): - # array? - comp = full_type_name.split("|") - type_name = comp[0] - array_mult = 1 - for m in comp[1:]: - array_mult *= int(m) - - # its a pointer ;) - if self._is_pointer(type_name): - base = 4 - # look for standard type - elif type_name in self.types: - base = 2 ** self.types[type_name][0] - # look for user type - elif type_name in self.struct_pool: - t = self.struct_pool[type_name] - base = t.get_size() - else: - raise InvalidAmigaTypeException(type_name) - - return array_mult * base - - def _is_pointer(self, name): - return name.find("*") != -1 or name.find("#") != -1 - - def _is_baddr(self, base_type, name): - if base_type: - return base_type[1] - elif name.find("#") != -1: - return True - else: - return False - - def _validate_type_signature(self, my_name, type_sig): - type_name = self._get_pure_name(type_sig) - # its me - if type_name == my_name: - return type_name - # is it an internal type? - elif type_name in self.types: - return type_name - # a sub type? - elif type_name in self.struct_pool: - return type_name - else: - raise InvalidAmigaTypeException(type_name) - - def _get_pure_name(self, name): - # remove array post fixes and pointers - comp = name.split("|") - type_name = comp[0].split("*")[0] - type_name = type_name.split("#")[0] - return type_name + # add to struct + struct_def.add_field_def(field_def) + + return struct_def def _validate_class(self, cls): # make sure cls is derived from AmigaStruct @@ -198,7 +94,16 @@ def _validate_class(self, cls): return base_name -def AmigaStructDef(cls): - """a class decorator that setups up an amiga struct class""" - decorator = AmigaStructDecorator() - return decorator.decorate(cls) +AmigaStructDef = AmigaStructDecorator() + + +class AmigaClassDecorator: + def __call__(self, cls): + assert issubclass(cls, AmigaStruct) + # store as derived class + assert cls.sdef._alias_type is None + cls.sdef._alias_type = cls + return cls + + +AmigaClassDef = AmigaClassDecorator() diff --git a/amitools/vamos/astructs/baddr.py b/amitools/vamos/astructs/baddr.py deleted file mode 100644 index 268da716..00000000 --- a/amitools/vamos/astructs/baddr.py +++ /dev/null @@ -1,67 +0,0 @@ -class BAddr(object): - """represent an address in BCPL LONG notation""" - - def __init__(self, baddr=0): - self.baddr = baddr - self.addr = baddr << 2 - - def __repr__(self): - return "BAddr(%d)" % self.baddr - - def __str__(self): - return "BAddr(%08x,addr=%08x)" % (self.baddr, self.addr) - - def __eq__(self, other): - if isinstance(other, BAddr): - return other.baddr == self.baddr - elif type(other) is int: - # int compares addr - return other == self.addr - else: - return NotImplemented - - def __ne__(self, other): - if isinstance(other, BAddr): - return other.baddr != self.baddr - elif type(other) is int: - # int compares addr - return other != self.addr - else: - return NotImplemented - - def __rshift__(self, other): - # FIXME compat: BAddr() >> 2 == baddr - if other == 2: - return self.baddr - else: - return NotImplemented - - def __add__(self, other): - # FIXME compat: BAddr() + off = addr - if type(other) is int: - return self.addr + other - else: - return NotImplemented - - def __and__(self, other): - # FIXME compat: BAddr() & mask = addr - if type(other) is int: - return self.addr & other - else: - return NotImplemented - - def __int__(self): - # auto convert to addr - return self.addr - - @classmethod - def from_addr(cls, addr): - if addr & 3 != 0: - raise ValueError("BAddr needs long word aligned address!") - return cls(addr >> 2) - - def get_baddr(self): - return self.baddr - - def get_addr(self): - return self.addr diff --git a/amitools/vamos/atypes/bitfield.py b/amitools/vamos/astructs/bitfield.py similarity index 63% rename from amitools/vamos/atypes/bitfield.py rename to amitools/vamos/astructs/bitfield.py index 36a1da34..a95f99d7 100644 --- a/amitools/vamos/atypes/bitfield.py +++ b/amitools/vamos/astructs/bitfield.py @@ -1,28 +1,18 @@ import inspect +from .scalar import ScalarType -def BitFieldType(cls): - """a class decorator that generates a bit field class""" - - # collect integer vals - _name_to_val = {} - _val_to_name = {} - mem = inspect.getmembers(cls) - for name, val in mem: - if type(val) in (int, int): - # check that val is really a bit mask - if val & (val - 1) != 0: - raise ValueError("no bit mask in bit field: " % name) - _name_to_val[name] = val - _val_to_name[val] = name - cls._name_to_val = _name_to_val - cls._val_to_name = _val_to_name +class BitField: + _name_to_val = None # will be filled by decorator + _val_to_name = None # will be filled by decorator + _names = None # will be filled by decorator + @classmethod def to_strs(cls, val, check=True): res = [] - for bf_val in cls._val_to_name: + for name in cls._names: + bf_val = cls._name_to_val[name] if bf_val & val == bf_val: - name = cls._val_to_name[bf_val] res.append(name) val &= ~bf_val if val != 0: @@ -32,9 +22,11 @@ def to_strs(cls, val, check=True): res.append(str(val)) return res + @classmethod def to_str(cls, val, check=True): return "|".join(cls.to_strs(val, check)) + @classmethod def from_strs(cls, *args): val = 0 for name in args: @@ -45,9 +37,11 @@ def from_strs(cls, *args): raise ValueError("invalid bit mask name: " + name) return val + @classmethod def from_str(cls, val): return cls.from_strs(*val.split("|")) + @classmethod def _get_bit_mask(cls, val): if type(val) is str: if val in cls._name_to_val: @@ -62,69 +56,61 @@ def _get_bit_mask(cls, val): else: return val + @classmethod def is_set(cls, what, val): bmask = cls._get_bit_mask(what) return val & bmask == bmask + @classmethod def is_clr(cls, what, val): bmask = cls._get_bit_mask(what) return val & bmask == 0 - cls.to_str = classmethod(to_str) - cls.from_str = classmethod(from_str) - cls.to_strs = classmethod(to_strs) - cls.from_strs = classmethod(from_strs) - cls._get_bit_mask = classmethod(_get_bit_mask) - cls.is_set = classmethod(is_set) - cls.is_clr = classmethod(is_clr) - - def __init__(self, *values): - val = 0 - for v in values: - bmask = self._get_bit_mask(v) - val |= bmask - self.value = val - def __str__(self): - return self.to_str(self.value, False) + return self.to_str(self.get(), False) def __repr__(self): return "%s('%s')" % (self.__class__.__name__, str(self)) - def __int__(self): - return self.value - - def __eq__(self, other): - if isinstance(other, cls): - return self.value == other.value - elif type(other) is int: - return self.value == other - else: - return NotImplemented - def has_bits(self, what): bmask = self._get_bit_mask(what) - return self.value & bmask == bmask + return self.get() & bmask == bmask def set_bits(self, what): bmask = self._get_bit_mask(what) - self.value |= bmask + super().set(self.get() | bmask) def clr_bits(self, what): bmask = self._get_bit_mask(what) - self.value &= ~bmask - - def get_value(self): - return self.value - - cls.__init__ = __init__ - cls.__str__ = __str__ - cls.__repr__ = __repr__ - cls.__int__ = __int__ - cls.__eq__ = __eq__ - cls.get_value = get_value - cls.has_bits = has_bits - cls.set_bits = set_bits - cls.clr_bits = clr_bits + super().set(self.get() & ~bmask) + + def set(self, val): + bmask = self._get_bit_mask(val) + super().set(bmask) + + +def BitFieldType(cls): + """a class decorator that generates a bit field class""" + + assert issubclass(cls, BitField) + assert issubclass(cls, ScalarType) + assert not cls._signed + + # collect public integer vals + _name_to_val = {} + _val_to_name = {} + _names = [] + mem = inspect.getmembers(cls) + for name, val in mem: + if type(val) is int and name[0] != "_": + # check that val is really a bit mask + if val & (val - 1) != 0: + raise ValueError("no bit mask in bit field: " % name) + _name_to_val[name] = val + _val_to_name[val] = name + _names.append(name) + cls._name_to_val = _name_to_val + cls._val_to_name = _val_to_name + cls._names = _names return cls diff --git a/amitools/vamos/astructs/dos.py b/amitools/vamos/astructs/dos.py deleted file mode 100644 index 3fd9f41c..00000000 --- a/amitools/vamos/astructs/dos.py +++ /dev/null @@ -1,338 +0,0 @@ -from .astructdef import AmigaStructDef -from .astruct import AmigaStruct - - -@AmigaStructDef -class FileLockStruct(AmigaStruct): - _format = [ - ("FileLock#", "fl_Link"), - ("ULONG", "fl_Key"), - ("LONG", "fl_Access"), - ("void*", "fl_Task"), - ("BPTR", "fl_Volume"), - ] - - -@AmigaStructDef -class FileHandleStruct(AmigaStruct): - _format = [ - ("void*", "fh_Link"), - ("void*", "fh_Port"), - ("void*", "fh_Type"), - ("LONG", "fh_Buf"), - ("LONG", "fh_Pos"), - ("LONG", "fh_End"), - ("LONG", "fh_Funcs"), - ("LONG", "fh_Func2"), - ("LONG", "fh_Func3"), - ("LONG", "fh_Args"), - ("LONG", "fh_Arg2"), - ] - - -@AmigaStructDef -class PathListStruct(AmigaStruct): - _format = [("PathList#", "pl_Next"), ("FileLock#", "pl_Lock")] - - -@AmigaStructDef -class CLIStruct(AmigaStruct): - _format = [ - ("LONG", "cli_Result2"), - ("BSTR", "cli_SetName"), - ("PathList#", "cli_CommandDir"), - ("LONG", "cli_ReturnCode"), - ("BSTR", "cli_CommandName"), - ("LONG", "cli_FailLevel"), - ("BSTR", "cli_Prompt"), - ("FileHandle#", "cli_StandardInput"), - ("FileHandle#", "cli_CurrentInput"), - ("BSTR", "cli_CommandFile"), - ("LONG", "cli_Interactive"), - ("LONG", "cli_Background"), - ("FileHandle#", "cli_CurrentOutput"), - ("LONG", "cli_DefaultStack"), - ("FileHandle#", "cli_StandardOutput"), - ("BPTR", "cli_Module"), - ] - - -@AmigaStructDef -class ProcessStruct(AmigaStruct): - _format = [ - ("Task", "pr_Task"), - ("MsgPort", "pr_MsgPort"), - # param - ("WORD", "pr_Pad"), - ("BPTR", "pr_SegList"), - ("LONG", "pr_StackSize"), - ("APTR", "pr_GlobVec"), - ("LONG", "pr_TaskNum"), - ("BPTR", "pr_StackBase"), - ("LONG", "pr_Result2"), - ("FileLock#", "pr_CurrentDir"), - ("FileHandle#", "pr_CIS"), - ("FileHandle#", "pr_COS"), - ("APTR", "pr_ConsoleTask"), - ("APTR", "pr_FileSystemTask"), - ("CLI#", "pr_CLI"), - ("APTR", "pr_ReturnAddr"), - ("APTR", "pr_PktWait"), - ("APTR", "pr_WindowPtr"), - # 2.0 - ("FileLock#", "pr_HomeDir"), - ("LONG", "pr_Flags"), - ("VOIDFUNC", "pr_ExitCode"), - ("LONG", "pr_ExitData"), - ("UBYTE*", "pr_Arguments"), - ("MinList", "pr_LocalVars"), - ("ULONG", "pr_ShellPrivate"), - ("FileHandle#", "pr_CES"), - ] - - -@AmigaStructDef -class DateStampStruct(AmigaStruct): - _format = [("LONG", "ds_Days"), ("LONG", "ds_Minute"), ("LONG", "ds_Tick")] - - -# the union in DosList is splitted up into own types - - -@AmigaStructDef -class DosListDeviceStruct(AmigaStruct): - _format = [ - ("BPTR", "dol_Next"), - ("LONG", "dol_Type"), - ("APTR", "dol_Task"), - ("BPTR", "dol_Lock"), - ("BSTR", "dol_Handler"), - ("LONG", "dol_StackSize"), - ("LONG", "dol_Priority"), - ("LONG", "dol_Startup"), - ("BPTR", "dol_SegList"), - ("BPTR", "dol_GlobVec"), - ("BSTR", "dol_Name"), - ] - - -@AmigaStructDef -class DosListVolumeStruct(AmigaStruct): - _format = [ - ("BPTR", "dol_Next"), - ("LONG", "dol_Type"), - ("APTR", "dol_Task"), - ("BPTR", "dol_Lock"), - ("DateStamp", "dol_VolumeDate"), - ("BPTR", "dol_LockList"), - ("LONG", "dol_DiskType"), - ("LONG", "dol_Padding0"), - ("BSTR", "dol_Name"), - ] - - -@AmigaStructDef -class AssignListStruct(AmigaStruct): - _format = [("AssignList*", "al_Next"), ("BPTR", "al_Lock")] - - -@AmigaStructDef -class DosListAssignStruct(AmigaStruct): - _format = [ - ("BPTR", "dol_Next"), - ("LONG", "dol_Type"), - ("APTR", "dol_Task"), - ("BPTR", "dol_Lock"), - ("UBYTE*", "dol_AssignName"), - ("AssignList*", "dol_List"), - ("LONG|4", "dol_Padding"), - ("BSTR", "dol_Name"), - ] - - -@AmigaStructDef -class DosInfoStruct(AmigaStruct): - _format = [ - ("BPTR", "di_McName"), - ("BPTR", "di_DevInfo"), - ("BPTR", "di_Devices"), - ("BPTR", "di_Handlers"), - ("BPTR", "di_NetHand"), - ("SignalSemaphore", "di_DevLock"), - ("SignalSemaphore", "di_EntryLock"), - ("SignalSemaphore", "di_DeleteLock"), - ] - - -@AmigaStructDef -class RootNodeStruct(AmigaStruct): - _format = [ - ("BPTR", "rn_TaskArray"), - ("BPTR", "rn_ConsoleSegment"), - ("DateStamp", "rn_Time"), - ("LONG", "rn_RestartSeg"), - ("BPTR", "rn_Info"), - ("BPTR", "rn_FileHandlerSegment"), - ("MinList", "rn_CliList"), - ("MsgPort*", "rn_BootProc"), - ("BPTR", "rn_ShellSegment"), - ("LONG", "rn_Flags"), - ] - - -@AmigaStructDef -class DosLibraryStruct(AmigaStruct): - _format = [ - ("Library", "lib"), - ("RootNode*", "dl_Root"), - ("APTR", "dl_GV"), - ("LONG", "dl_A2"), - ("LONG", "dl_A5"), - ("LONG", "dl_A6"), - ("APTR", "dl_Errors"), - ("APTR", "dl_TimeReq"), - ("APTR", "dl_UtilityBase"), - ("APTR", "dl_IntuitionBase"), - ] - - -@AmigaStructDef -class FileInfoBlockStruct(AmigaStruct): - _format = [ - ("ULONG", "fib_DiskKey"), - ("LONG", "fib_DirEntryType"), - ("char|108", "fib_FileName"), - ("LONG", "fib_Protection"), - ("LONG", "fib_EntryType"), - ("ULONG", "fib_Size"), - ("LONG", "fib_NumBlocks"), - ("DateStamp", "fib_Date"), - ("char|80", "fib_Comment"), - ("UWORD", "fib_OwnerUID"), - ("UWORD", "fib_OwnerGID"), - ("char|32", "fib_Reserved"), - ] - - -@AmigaStructDef -class DosPacketStruct(AmigaStruct): - _format = [ - ("Message*", "dp_Link"), - ("MsgPort*", "dp_Port"), - ("LONG", "dp_Type"), - ("LONG", "dp_Res1"), - ("LONG", "dp_Res2"), - ("LONG", "dp_Arg1"), - ("LONG", "dp_Arg2"), - ("LONG", "dp_Arg3"), - ("LONG", "dp_Arg4"), - ("LONG", "dp_Arg5"), - ("LONG", "dp_Arg6"), - ("LONG", "dp_Arg7"), - ] - - -@AmigaStructDef -class AChainStruct(AmigaStruct): - _format = [ - ("AChain*", "an_Child"), - ("AChain*", "an_Parent"), - ("BPTR", "an_Lock"), - ("FileInfoBlock", "an_Info"), - ("BYTE", "an_Flags"), - ("UBYTE", "an_String"), - ] - - -@AmigaStructDef -class AnchorPathStruct(AmigaStruct): - _format = [ - ("AChain*", "ap_Base"), - ("AChain*", "ap_Last"), - ("LONG", "ap_BreakBits"), - ("LONG", "ap_FoundBreak"), - ("BYTE", "ap_Flags"), - ("BYTE", "ap_Reserved"), - ("WORD", "ap_Strlen"), - ("FileInfoBlock", "ap_Info"), - ("UBYTE", "ap_Buf"), - ] - - -@AmigaStructDef -class DevProcStruct(AmigaStruct): - _format = [ - ("MsgPort*", "dvp_Port"), - ("BPTR", "dvp_Lock"), - ("ULONG", "dvp_Flags"), - ("void*", "dvp_DevNode"), - ] - - -@AmigaStructDef -class CSourceStruct(AmigaStruct): - _format = [("UBYTE*", "CS_Buffer"), ("LONG", "CS_Length"), ("LONG", "CS_CurChr")] - - -@AmigaStructDef -class RDArgsStruct(AmigaStruct): - _format = [ - ("CSource", "RDA_Source"), - ("LONG", "RDA_DAList"), - ("UBYTE*", "RDA_Buffer"), - ("LONG", "RDA_BufSiz"), - ("UBYTE*", "RDA_ExtHelp"), - ("LONG", "RDA_Flags"), - ] - - -@AmigaStructDef -class DateTimeStruct(AmigaStruct): - _format = [ - ("DateStamp", "dat_Stamp"), - ("UBYTE", "dat_Format"), - ("UBYTE", "dat_Flags"), - ("UBYTE*", "dat_StrDay"), - ("UBYTE*", "dat_StrDate"), - ("UBYTE*", "dat_StrTime"), - ] - - -@AmigaStructDef -class InfoDataStruct(AmigaStruct): - _format = [ - ("LONG", "id_NumSoftErrors"), - ("LONG", "id_UnitNumber"), - ("LONG", "id_DiskState"), - ("LONG", "id_NumBlocks"), - ("LONG", "id_NumBlocksUsed"), - ("LONG", "id_BytesPerBlock"), - ("LONG", "id_DiskType"), - ("BPTR", "id_VolumeNode"), - ("LONG", "id_InUse"), - ] - - -@AmigaStructDef -class SegmentStruct(AmigaStruct): - _format = [ - ("BPTR", "seg_Next"), - ("ULONG", "seg_UC"), - ("BPTR", "seg_Seg"), - ("UBYTE", "seg_Name"), - ] - - -@AmigaStructDef -class LocalVarStruct(AmigaStruct): - _format = [ - ("Node", "lv_Node"), - ("UWORD", "lv_Flags"), - ("UBYTE*", "lv_Value"), - ("ULONG", "lv_Len"), - ] - - -@AmigaStructDef -class PathStruct(AmigaStruct): - _format = [("BPTR", "path_Next"), ("BPTR", "path_Lock")] diff --git a/amitools/vamos/astructs/dump.py b/amitools/vamos/astructs/dump.py new file mode 100644 index 00000000..26b58d77 --- /dev/null +++ b/amitools/vamos/astructs/dump.py @@ -0,0 +1,103 @@ +from .astruct import AmigaStruct + + +class TypeDumper: + def __init__(self, print_func=print): + self._print_func = print_func + + def _reset(self, base_addr=0): + self._indent = 0 + self._num = 0 + self._base_addr = 0 + self._offset = 0 + + def dump(self, type_cls, base_addr=0): + self._reset(base_addr) + if issubclass(type_cls, AmigaStruct): + self._dump_struct(type_cls) + else: + self._print_type_line(type_cls.__name__) + + def dump_fields(self, *field_defs, base_addr=0): + self._reset(base_addr) + # first print enclosing structure + field_def = field_defs[0] + struct = field_def.struct + self._print_begin_struct(struct.sdef.get_type_name()) + self._indent += 1 + self._dump_fields(field_defs, 0) + self._indent -= 1 + self._print_end_struct(struct.get_byte_size()) + + def _dump_fields(self, field_defs, pos): + field_def = field_defs[pos] + field_type = field_def.type + struct = field_def.struct + self._offset += field_def.offset + self._base_addr += field_def.offset + if issubclass(field_type, AmigaStruct): + self._print_begin_struct(field_type.sdef.get_type_name(), field_def.name) + if pos < len(field_defs) - 1: + self._indent += 1 + self._dump_fields(field_defs, pos + 1) + self._indent -= 1 + self._print_end_struct(struct.get_byte_size()) + else: + self._print_field(field_def) + + def _dump_struct(self, type_cls, struct_field_def=None): + type_name = type_cls.sdef.get_type_name() + byte_size = type_cls.get_byte_size() + field_name = struct_field_def.name if struct_field_def else None + self._print_begin_struct(type_name, field_name) + self._indent += 1 + + for field_def in type_cls.sdef.get_field_defs(): + field_type = field_def.type + if issubclass(field_type, AmigaStruct): + self._dump_struct(field_type, field_def) + else: + self._print_field(field_def) + self._num += 1 + self._offset += field_def.size + self._base_addr += field_def.size + + self._indent -= 1 + self._print_end_struct(byte_size) + + def _get_prefix(self, extra=None): + istr = " " * self._indent + if not extra: + extra = " " * 5 + return " @%04d %s %s" % (self._base_addr, extra, istr) + + def _print_type_line(self, type_name): + self._print_func("%s %s" % (self._get_prefix(), type_name)) + + def _print_begin_struct(self, type_name, field_name=None): + if field_name: + self._print_func( + "%s %s %s {" % (self._get_prefix(), type_name, field_name) + ) + else: + self._print_func("%s %s {" % (self._get_prefix(), type_name)) + + def _print_end_struct(self, total): + extra = "=%04d" % total + self._print_func("%s }" % (self._get_prefix(extra))) + + def _print_field(self, field_def): + istr = " " * self._indent + self._print_func( + "#%04d %04d @%04d/%04x +%04d %s %-20s %-20s" + % ( + field_def.index, + self._num, + self._offset, + self._offset, + field_def.type.get_byte_size(), + istr, + field_def.type.get_signature(), + field_def.name, + ) + ) diff --git a/amitools/vamos/atypes/enum.py b/amitools/vamos/astructs/enum.py similarity index 50% rename from amitools/vamos/atypes/enum.py rename to amitools/vamos/astructs/enum.py index a829dca4..de1bb63e 100644 --- a/amitools/vamos/atypes/enum.py +++ b/amitools/vamos/astructs/enum.py @@ -1,21 +1,14 @@ import inspect +from .scalar import ScalarType -def EnumType(cls): - """a class decorator that generates an enum class""" - - # collect integer vals - _name_to_val = {} - _val_to_name = {} - mem = inspect.getmembers(cls) - for name, val in mem: - if type(val) in (int, int): - _name_to_val[name] = val - _val_to_name[val] = name - - cls._name_to_val = _name_to_val - cls._val_to_name = _val_to_name +class Enum: + _name_to_val = None # will be set by decorator + _val_to_name = None # will be set by decorator + _names = None + _values = None + @classmethod def to_str(cls, val, check=True): if val in cls._val_to_name: return cls._val_to_name[val] @@ -24,48 +17,52 @@ def to_str(cls, val, check=True): else: return str(val) + @classmethod def from_str(cls, name): if name in cls._name_to_val: return cls._name_to_val[name] raise ValueError("'%s' is an unknown Enum string" % name) - cls.to_str = classmethod(to_str) - cls.from_str = classmethod(from_str) - - def __init__(self, value): - if type(value) is str: - if value in self._name_to_val: - self.value = self._name_to_val[value] - else: - raise ValueError("invalid value: " + value) - else: - self.value = value - def __str__(self): - return self.to_str(self.value, False) + return self.to_str(self.get(), False) def __repr__(self): return "%s('%s')" % (self.__class__.__name__, str(self)) - def __int__(self): - return self.value - - def __eq__(self, other): - if isinstance(other, cls): - return self.value == other.value - elif type(other) is int: - return self.value == other + def set(self, val): + # allow to set values + if val in self._values: + super().set(val) + # or names + elif val in self._names: + super().set(self.from_str(val)) else: - return NotImplemented + raise ValueError("Invalid enum value: " + val) + + +def EnumType(cls): + """a class decorator that generates an enum class""" - def get_value(self): - return self.value + assert issubclass(cls, Enum) + assert issubclass(cls, ScalarType) + assert not cls._signed - cls.__init__ = __init__ - cls.__str__ = __str__ - cls.__repr__ = __repr__ - cls.__int__ = __int__ - cls.__eq__ = __eq__ - cls.get_value = get_value + # collect integer vals + _name_to_val = {} + _val_to_name = {} + _names = [] + _values = [] + mem = inspect.getmembers(cls) + for name, val in mem: + if type(val) is int and name[0] != "_": + _name_to_val[name] = val + _val_to_name[val] = name + _names.append(name) + _values.append(val) + + cls._name_to_val = _name_to_val + cls._val_to_name = _val_to_name + cls._names = _names + cls._values = _values return cls diff --git a/amitools/vamos/astructs/exec_.py b/amitools/vamos/astructs/exec_.py deleted file mode 100644 index a5c67d05..00000000 --- a/amitools/vamos/astructs/exec_.py +++ /dev/null @@ -1,298 +0,0 @@ -from .astructdef import AmigaStructDef -from .astruct import AmigaStruct - -# Node -@AmigaStructDef -class NodeStruct(AmigaStruct): - _format = [ - ("Node*", "ln_Succ"), - ("Node*", "ln_Pred"), - ("UBYTE", "ln_Type"), - ("BYTE", "ln_Pri"), - ("char*", "ln_Name"), - ] - - -# MinNode -@AmigaStructDef -class MinNodeStruct(AmigaStruct): - _format = [("MinNode*", "mln_Succ"), ("MinNode*", "mln_Pred")] - - -# Library -@AmigaStructDef -class LibraryStruct(AmigaStruct): - _format = [ - ("Node", "lib_Node"), - ("UBYTE", "lib_Flags"), - ("UBYTE", "lib_pad"), - ("UWORD", "lib_NegSize"), - ("UWORD", "lib_PosSize"), - ("UWORD", "lib_Version"), - ("UWORD", "lib_Revision"), - ("char*", "lib_IdString"), - ("ULONG", "lib_Sum"), - ("UWORD", "lib_OpenCnt"), - ] - - -# List -@AmigaStructDef -class ListStruct(AmigaStruct): - _format = [ - ("Node*", "lh_Head"), - ("Node*", "lh_Tail"), - ("Node*", "lh_TailPred"), - ("UBYTE", "lh_Type"), - ("UBYTE", "l_pad"), - ] - - -# MinList -@AmigaStructDef -class MinListStruct(AmigaStruct): - _format = [ - ("MinNode*", "mlh_Head"), - ("MinNode*", "mlh_Tail"), - ("MinNode*", "mlh_TailPred"), - ] - - -# MsgPort -@AmigaStructDef -class MsgPortStruct(AmigaStruct): - _format = [ - ("Node", "mp_Node"), - ("UBYTE", "mp_Flags"), - ("UBYTE", "mp_SigBit"), - ("void*", "mp_SigTask"), - ("List", "mp_MsgList"), - ] - - -# Message -@AmigaStructDef -class MessageStruct(AmigaStruct): - _format = [ - ("Node", "mn_Node"), - ("MsgPort*", "mn_ReplyPort"), - ("UWORD", "mn_Length"), - ] - - -# IntVector -@AmigaStructDef -class IntVectorStruct(AmigaStruct): - _format = [("APTR", "iv_Data"), ("VOIDFUNC", "iv_Code"), ("Node*", "iv_Node")] - - -# SoftIntList -@AmigaStructDef -class SoftIntListStruct(AmigaStruct): - _format = [("List", "sh_List"), ("UWORD", "sh_Pad")] - - -# Task -@AmigaStructDef -class TaskStruct(AmigaStruct): - _format = [ - ("Node", "tc_Node"), - ("UBYTE", "tc_Flags"), - ("UBYTE", "tc_State"), - ("BYTE", "tc_IDNestCnt"), - ("BYTE", "tc_TDNestCnt"), - ("ULONG", "tc_SigAlloc"), - ("ULONG", "tc_SigWait"), - ("ULONG", "tc_SigRecvd"), - ("ULONG", "tc_SigExcept"), - ("UWORD", "tc_TrapAlloc"), - ("UWORD", "tc_TrapAble"), - ("APTR", "tc_ExceptData"), - ("APTR", "tc_ExceptCode"), - ("APTR", "tc_TrapData"), - ("APTR", "tc_TrapCode"), - ("APTR", "tc_SPReg"), - ("APTR", "tc_SPLower"), - ("APTR", "tc_SPUpper"), - ("VOIDFUNC", "tc_Switch"), - ("VOIDFUNC", "tc_Launch"), - ("List", "tc_MemEntry"), - ("APTR", "tc_UserData"), - ] - - -@AmigaStructDef -class ExecLibraryStruct(AmigaStruct): - _format = [ - ("Library", "LibNode"), - # Static System Variables - ("UWORD", "SoftVer"), - ("WORD", "LowMemChkSum"), - ("ULONG", "ChkBase"), - ("APTR", "ColdCapture"), - ("APTR", "CoolCapture"), - ("APTR", "WarmCapture"), - ("APTR", "SysStkUpper"), - ("APTR", "SysStkLower"), - ("ULONG", "MaxLocMem"), - ("APTR", "DebugEntry"), - ("APTR", "DebugData"), - ("APTR", "AlertData"), - ("APTR", "MaxExtMem"), - ("UWORD", "ChkSum"), - # Interrupt Related - ("IntVector|16", "IntVects"), - # Dynamic System Variables - ("Task*", "ThisTask"), - ("ULONG", "IdleCount"), - ("ULONG", "DispCount"), - ("UWORD", "Quantum"), - ("UWORD", "Elapsed"), - ("UWORD", "SysFlags"), - ("BYTE", "IDNestCnt"), - ("BYTE", "TDNestCnt"), - ("UWORD", "AttnFlags"), - ("UWORD", "AttnResched"), - ("APTR", "ResModules"), - ("APTR", "TaskTrapCode"), - ("APTR", "TaskExceptCode"), - ("APTR", "TaskExitCode"), - ("ULONG", "TaskSigAlloc"), - ("UWORD", "TaskTrapAlloc"), - # System Lists (private!) - ("List", "MemList"), - ("List", "ResourceList"), - ("List", "DeviceList"), - ("List", "IntrList"), - ("List", "LibList"), - ("List", "PortList"), - ("List", "TaskReady"), - ("List", "TaskWait"), - ("SoftIntList|5", "SoftIntList"), - # Other Globals - ("LONG|4", "LastAlert"), - ("UBYTE", "VBlankFrequency"), - ("UBYTE", "PowerSupplyFrequency"), - ("List", "SemaphoreList"), - ("APTR", "KickMemPtr"), - ("APTR", "KickTagPtr"), - ("APTR", "KickCheckSum"), - # V36 Additions - ("ULONG", "ex_Pad0"), - ("ULONG", "ex_LaunchPoint"), - ("APTR", "ex_RamLibPrivate"), - ("ULONG", "ex_EClockFrequency"), - ("ULONG", "ex_CacheControl"), - ("ULONG", "ex_TaskID"), - ("ULONG|5", "ex_Reserved1"), - ("APTR", "ex_MMULock"), - ("ULONG|3", "ex_Reserved2"), - # V39 Additions - ("MinList", "ex_MemHandlers"), - ("APTR", "ex_MemHandler"), - ] - - -# StackSwap -@AmigaStructDef -class StackSwapStruct(AmigaStruct): - _format = [("APTR", "stk_Lower"), ("ULONG", "stk_Upper"), ("APTR", "stk_Pointer")] - - -# Semaphores -@AmigaStructDef -class SemaphoreRequestStruct(AmigaStruct): - _format = [("MinNode", "sr_Link"), ("Task*", "sr_Waiter")] - - -@AmigaStructDef -class SignalSemaphoreStruct(AmigaStruct): - _format = [ - ("Node", "ss_Link"), - ("WORD", "ss_NestCount"), - ("MinList", "ss_WaitQueue"), - ("SemaphoreRequest", "ss_MultipleLink"), - ("Task*", "ss_Owner"), - ("WORD", "ss_QueueCount"), - ] - - -# Device -@AmigaStructDef -class DeviceStruct(AmigaStruct): - _format = [("Library", "dd_Library")] - - -# Unit -@AmigaStructDef -class UnitStruct(AmigaStruct): - _format = [ - ("MsgPort", "unit_MsgPort"), - ("UBYTE", "unit_flags"), - ("UBYTE", "unit_pad"), - ("UWORD", "unit_OpenCnt"), - ] - - -# IORequests -@AmigaStructDef -class IORequestStruct(AmigaStruct): - _format = [ - ("Message", "io_Message"), - ("Device*", "io_Device"), - ("Unit", "io_Unit"), - ("UWORD", "io_Command"), - ("UBYTE", "io_Flags"), - ("BYTE", "io_Error"), - ("ULONG", "io_Actual"), - ("ULONG", "io_Length"), - ("ULONG", "io_Data"), - ("ULONG", "io_Offset"), - ] - - -# MemChunk -@AmigaStructDef -class MemChunkStruct(AmigaStruct): - _format = [("MemChunk*", "mc_Next"), ("ULONG", "mc_Bytes")] - - -# MemHeader -@AmigaStructDef -class MemHeaderStruct(AmigaStruct): - _format = [ - ("Node", "mh_Node"), - ("UWORD", "mh_Attributes"), - ("MemChunk*", "mh_First"), - ("APTR", "mh_Lower"), - ("APTR", "mh_Upper"), - ("ULONG", "mh_Free"), - ] - - -# Resident -@AmigaStructDef -class ResidentStruct(AmigaStruct): - _format = [ - ("UWORD", "rt_MatchWord"), - ("APTR", "rt_MatchTag"), - ("APTR", "rt_EndSkip"), - ("UBYTE", "rt_Flags"), - ("UBYTE", "rt_Version"), - ("UBYTE", "rt_Type"), - ("BYTE", "rt_Pri"), - ("char*", "rt_Name"), - ("char*", "rt_IdString"), - ("APTR", "rt_Init"), - ] - - -# AutoInit used in Residents -@AmigaStructDef -class AutoInitStruct(AmigaStruct): - _format = [ - ("ULONG", "ai_PosSize"), - ("APTR", "ai_Functions"), - ("APTR", "ai_InitStruct"), - ("APTR", "ai_InitFunc"), - ] diff --git a/amitools/vamos/astructs/pointer.py b/amitools/vamos/astructs/pointer.py new file mode 100644 index 00000000..d07797d8 --- /dev/null +++ b/amitools/vamos/astructs/pointer.py @@ -0,0 +1,214 @@ +from .typebase import TypeBase + + +class VOID(TypeBase): + """A VOID instance only holds the address where the void exists.""" + + def __repr__(self): + return "VOID(addr={})".format(self._addr) + + +class PointerType(TypeBase): + _byte_size = 4 + _ref_type = None + + @classmethod + def get_ref_type(cls): + return cls._ref_type + + @classmethod + def get_signature(cls): + return "{}*".format(cls._ref_type.get_signature()) + + def __init__(self, mem=None, addr=None, cpu=None, reg=None, **kwargs): + """create pointer type with referenced object""" + super(PointerType, self).__init__(mem, addr, cpu, reg, **kwargs) + self._ref = None + self._ref_addr = None + + def setup(self, val, alloc=None, free_refs=None): + if val is None: + self.set_ref(None) + elif type(val) is int: + self.set_ref_addr(val) + elif isinstance(val, self._ref_type): + self.set_ref(val) + else: + if alloc: + ref = self.alloc_ref(alloc) + free_refs.append(ref) + ref.setup(val, alloc, free_refs) + else: + raise ValueError("No alloc:" + val) + + def alloc_ref(self, alloc, *alloc_args, tag=None, **kwargs): + # make sure nothing is allocated yet + assert self._ref is None + new_ref = self._ref_type.alloc(alloc, tag=tag, *alloc_args) + self.set_ref(new_ref) + return self.get_ref() + + def free_ref(self): + assert self._ref + self._ref.free() + self.set_ref(None) + + def get_ref(self): + """return the referenced type instance""" + ref_addr = self._read_pointer() + if ref_addr != self._ref_addr: + self._ref = self._create_ref_at(ref_addr) + self._ref_addr = ref_addr + return self._ref + + def set_ref(self, ref): + """set a new type instance""" + self._ref = ref + if not ref: + self._ref_addr = 0 + else: + assert isinstance(ref, self._ref_type) + self._ref_addr = ref.get_addr() + self._write_pointer(self._ref_addr) + + def get_ref_addr(self): + return self._read_pointer() + + def set_ref_addr(self, ref_addr): + self._ref = None + self._ref_addr = None + self._write_pointer(ref_addr) + + def get(self): + return self.get_ref_addr() + + def set(self, ref_addr): + self.set_ref_addr(ref_addr) + + def _read_pointer(self): + if self._addr is not None: + store_addr = self._mem.r32(self._addr) + else: + store_addr = self._cpu.r_reg(self._reg) + ref_addr = self._store_to_ref_addr(store_addr) + return ref_addr + + def _write_pointer(self, ref_addr): + store_addr = self._ref_to_store_addr(ref_addr) + if self._addr is not None: + self._mem.w32(self._addr, store_addr) + else: + self._cpu.w_reg(self._reg, store_addr) + + def __repr__(self): + return "{}(ref={}, addr={})".format( + self.__class__.__name__, self._ref, self._addr + ) + + def __str__(self): + return str(self.get_ref()) + + def __int__(self): + return self.get_ref_addr() + + def _store_to_ref_addr(self, addr): + return addr + + def _ref_to_store_addr(self, addr): + return addr + + def _create_ref_at(self, ref_addr): + # null pointer + if ref_addr == 0: + return None + cls_type = self._ref_type.get_alias_type() + return cls_type(mem=self._mem, addr=ref_addr) + + def __getattr__(self, key): + if key == "aptr": + return self.get_ref_addr() + elif key == "ref": + return self.get_ref() + else: + return super(PointerType, self).__getattr__(key) + + def __setattr__(self, key, val): + if key == "aptr": + self.set_ref_addr(val) + elif key == "ref": + self.set_ref(val) + else: + super(PointerType, self).__setattr__(key, val) + + +class BCPLPointerType(PointerType): + @classmethod + def get_signature(cls): + return "{}#".format(cls._ref_type.get_signature()) + + def _store_to_ref_addr(self, addr): + return addr << 2 + + def _ref_to_store_addr(self, addr): + return addr >> 2 + + def get_ref_baddr(self): + return self.get_ref_addr() >> 2 + + def set_ref_baddr(self, baddr): + self.set_ref_addr(baddr << 2) + + def get(self): + return self.get_ref_baddr() + + def set(self, val): + self.set_ref_baddr(val) + + def __int__(self): + return self.get_ref_baddr() + + def __getattr__(self, key): + if key == "bptr": + return self.get_ref_baddr() + else: + return super(BCPLPointerType, self).__getattr__(key) + + def __setattr__(self, key, val): + if key == "bptr": + self.set_ref_baddr(val) + else: + super(BCPLPointerType, self).__setattr__(key, val) + + +# pointer type cache +APTRTypes = {} + + +def APTR(ref_type): + """create a new pointer type class the references the given type""" + name = "APTR_" + ref_type.__name__ + if name in APTRTypes: + return APTRTypes[name] + else: + new_type = type(name, (PointerType,), dict(_ref_type=ref_type)) + APTRTypes[name] = new_type + return new_type + + +# pointer type cache +BPTRTypes = {} + + +def BPTR(ref_type): + """create a new pointer type class the references the given type""" + name = "BPTR_" + ref_type.__name__ + if name in BPTRTypes: + return BPTRTypes[name] + else: + new_type = type(name, (BCPLPointerType,), dict(_ref_type=ref_type)) + BPTRTypes[name] = new_type + return new_type + + +APTR_VOID = APTR(VOID) +BPTR_VOID = BPTR(VOID) diff --git a/amitools/vamos/astructs/scalar.py b/amitools/vamos/astructs/scalar.py new file mode 100644 index 00000000..836e19f4 --- /dev/null +++ b/amitools/vamos/astructs/scalar.py @@ -0,0 +1,132 @@ +from .typebase import TypeBase + + +class ScalarType(TypeBase): + _signed = None + _mem_width = None + + @classmethod + def is_signed(cls): + """is scalar type signed?""" + return cls._signed + + @classmethod + def get_mem_width(cls): + """return width of type in 2**n bytes. + + Non scalar types will raise an error. + See WIDTH_* constants. + """ + return cls._mem_width + + def __init__(self, mem=None, addr=None, cpu=None, reg=None, **kwargs): + super(ScalarType, self).__init__(mem, addr, cpu, reg, **kwargs) + + def set(self, val): + if self._addr is not None: + self._write_mem(val) + else: + self._write_reg(val) + + def get(self): + if self._addr is not None: + return self._read_mem() + else: + return self._read_reg() + + def setup(self, val, alloc=None, free_refs=None): + self.set(val) + + def _read_reg(self): + raise RuntimeError + + def _write_reg(self): + raise RuntimeError + + def _read_mem(self): + raise RuntimeError + + def _write_mem(self): + raise RuntimeError + + def __repr__(self): + return "{}(value={}, addr={})".format( + self.__class__.__name__, self.get(), self._addr + ) + + def __int__(self): + return self.get() + + def __getattr__(self, key): + if key == "val": + return self.get() + else: + return super(ScalarType, self).__getattr__(key) + + def __setattr__(self, key, val): + if key == "val": + self.set(val) + else: + super(ScalarType, self).__setattr__(key, val) + + +class SignedType(ScalarType): + _signed = True + + def _read_reg(self): + return self._cpu.rs_reg(self._reg) + + def _write_reg(self, val): + self._cpu.ws_reg(self._reg, val) + + def _read_mem(self): + return self._mem.reads(self._mem_width, self._addr) + + def _write_mem(self, val): + self._mem.writes(self._mem_width, self._addr, val) + + +class UnsignedType(ScalarType): + _signed = False + + def _read_reg(self): + return self._cpu.r_reg(self._reg) + + def _write_reg(self, val): + self._cpu.w_reg(self._reg, val) + + def _read_mem(self): + return self._mem.read(self._mem_width, self._addr) + + def _write_mem(self, val): + self._mem.write(self._mem_width, self._addr, val) + + +class LONG(SignedType): + _byte_size = 4 + _mem_width = 2 + + +class ULONG(UnsignedType): + _byte_size = 4 + _mem_width = 2 + + +class WORD(SignedType): + _byte_size = 2 + _mem_width = 1 + + +class UWORD(UnsignedType): + _byte_size = 2 + _mem_width = 1 + + +class BYTE(SignedType): + _byte_size = 1 + _mem_width = 0 + + +class UBYTE(UnsignedType): + _byte_size = 1 + _mem_width = 0 diff --git a/amitools/vamos/astructs/string.py b/amitools/vamos/astructs/string.py new file mode 100644 index 00000000..19553217 --- /dev/null +++ b/amitools/vamos/astructs/string.py @@ -0,0 +1,196 @@ +from .typebase import TypeBase +from .pointer import APTR, BPTR + + +class CStringType(TypeBase): + def __init__(self, mem, addr, **kwargs): + super(CStringType, self).__init__(mem, addr, **kwargs) + + def get(self): + if self._addr == 0: + return None + else: + return self._mem.r_cstr(self._addr) + + def set(self, val): + if self._addr == 0: + raise ValueError("Can't set NULL string!") + else: + self._mem.w_cstr(self._addr, val) + + def __getattr__(self, key): + if key == "str": + return self.get() + else: + return super(CStringType, self).__getattr__(key) + + def __setattr__(self, key, val): + if key == "str": + self.set(val) + else: + super(CStringType, self).__setattr__(key, val) + + def __eq__(self, other): + # compare against other string + if type(other) is str: + return self.get() == other + elif other is None: + return self.get() is None + else: + return super(CStringType, self).__eq__(other) + + def __str__(self): + return str(self.get()) + + @classmethod + def _alloc(cls, alloc, tag, txt): + if tag is None: + tag = "CString('%s')" % txt + return alloc.alloc_cstr(tag, txt) + + @classmethod + def _free(cls, alloc, mem_obj): + alloc.free_cstr(mem_obj) + + +class BStringType(TypeBase): + def __init__(self, mem, addr, **kwargs): + super(BStringType, self).__init__(mem, addr, **kwargs) + + def get(self): + if self._addr == 0: + return None + else: + return self._mem.r_bstr(self._addr) + + def set(self, val): + if self._addr == 0: + raise ValueError("Can't set BNULL string!") + else: + self._mem.w_bstr(self._addr, val) + + def __getattr__(self, key): + if key == "str": + return self.get() + else: + return super(BStringType, self).__getattr__(key) + + def __setattr__(self, key, val): + if key == "str": + self.set(val) + else: + super(BStringType, self).__setattr__(key, val) + + def __eq__(self, other): + # compare against other string + if type(other) is str: + return self.get() == other + elif other is None: + return self.get() is None + else: + super(BStringType, self).__eq__(other) + + def __str__(self): + return str(self.get()) + + @classmethod + def _alloc(cls, alloc, tag, txt): + if tag is None: + tag = "BString('%s')" % txt + return alloc.alloc_bstr(tag, txt) + + @classmethod + def _free(cls, alloc, mem_obj): + alloc.free_bstr(mem_obj) + + +class CSTR(APTR(CStringType)): + @classmethod + def get_signature(cls): + return "CSTR" + + def get_str(self): + if self.aptr == 0: + return None + else: + return self.ref.get() + + def set_str(self, val): + if self.aptr == 0: + raise ValueError("Can't set NULL string!") + else: + self.ref.set(val) + + def __getattr__(self, key): + if key == "str": + return self.get_str() + else: + return super(CSTR, self).__getattr__(key) + + def __setattr__(self, key, val): + if key == "str": + self.set_str(val) + else: + super(CSTR, self).__setattr__(key, val) + + def alloc_str(self, alloc, txt): + return self.alloc_ref(alloc, txt) + + def free_str(self): + self.free_ref() + + def setup(self, val, alloc=None, free_refs=None): + if type(val) is str: + if alloc: + self.alloc_str(alloc, val) + free_refs.append(self) + else: + raise ValueError("no alloc for str!") + else: + return super().setup(val, alloc, free_refs) + + +class BSTR(BPTR(BStringType)): + @classmethod + def get_signature(cls): + return "BSTR" + + def get_str(self): + if self.bptr == 0: + return None + else: + return self.ref.get() + + def set_str(self, val): + if self.bptr == 0: + raise ValueError("Can't set BNULL string!") + else: + self.ref.set(val) + + def __getattr__(self, key): + if key == "str": + return self.get_str() + else: + return super(BSTR, self).__getattr__(key) + + def __setattr__(self, key, val): + if key == "str": + self.set_str(val) + else: + super(BSTR, self).__setattr__(key, val) + + def alloc_str(self, alloc, txt): + return self.alloc_ref(alloc, txt) + + def free_str(self): + self.free_ref() + + def setup(self, val, alloc=None, free_refs=None): + if type(val) is str: + if alloc: + self.alloc_str(alloc, val) + free_refs.append(self) + else: + raise ValueError("no alloc for str!") + else: + return super().setup(val, alloc, free_refs) diff --git a/amitools/vamos/astructs/typebase.py b/amitools/vamos/astructs/typebase.py new file mode 100644 index 00000000..7fc6196c --- /dev/null +++ b/amitools/vamos/astructs/typebase.py @@ -0,0 +1,150 @@ +class TypeBase: + """Base class for all Amiga types + + Namely scalars, pointers, arrays, and structs. Some type instances + (pointer, scalars) can be read from/written to CPU regs. All type + instances can be bound to a memory location and read from/written to + there. + """ + + # type parameters (will be overwritten in derived classes) + _byte_size = None + + @classmethod + def get_byte_size(cls): + """return the memory footprint in bytes""" + return cls._byte_size + + @classmethod + def get_size(cls): + """return the memory footprint in bytes""" + return cls._byte_size + + @classmethod + def get_signature(cls): + """return the type signature""" + return cls.__name__ + + @classmethod + def get_alias_type(cls): + """return the type alias (amiga class instead of struct) or the struct""" + return cls + + # --- instance --- + + def __init__( + self, + mem=None, + addr=None, + cpu=None, + reg=None, + offset=0, + base_offset=0, + alloc=None, + mem_obj=None, + **kwargs, + ): + """create instance of a type. + + If you pass mem and addr than the type is bound to a memory location. + If you pass a register then the value is bound to the CPU register. + """ + self._mem = mem + self._addr = addr + self._reg = reg + self._cpu = cpu + self._offset = offset + self._base_offset = base_offset + # optional allocation + self._mem_obj = mem_obj + self._alloc = alloc + # both addr and reg are not allowed + assert not (reg and addr) + if reg: + assert cpu + + def __eq__(self, other): + if type(other) is int: + return self._addr == other + elif isinstance(other, self.__class__): + return self._addr == other._addr + else: + return NotImplemented + + def get_addr(self): + """if type is bound to memory then return address otherwise None.""" + return self._addr + + def get_reg(self): + """if type is bound to CPU register then return register otherwise None.""" + + def get_mem(self): + """return associated mem object.""" + return self._mem + + def get_cpu(self): + """if type is bound to CPU register return CPU instance otherwise None.""" + return self._cpu + + def get_offset(self): + """if type is embedded in a structure then return its byte offset""" + return self._offset + + def get_base_offset(self): + """if type is embedded in a sub structure then return its global offset""" + return self._base_offset + + def __getattr__(self, key): + if key == "addr": + return self._addr + elif key == "mem": + return self._mem + elif key == "cpu": + return self._cpu + elif key == "reg": + return self._reg + elif key == "offset": + return self._offset + elif key == "base_offset": + return self._base_offset + else: + raise AttributeError("Invalid get key '{}' in {}".format(key, repr(self))) + + def __setattr__(self, key, val): + # check for invalid keys + if key in ("val", "aptr", "bptr", "ref"): + raise AttributeError("Invalid set key '{}' in {}".format(key, repr(self))) + super().__setattr__(key, val) + + # allocation + + @classmethod + def alloc(cls, alloc, *alloc_args, tag=None, **kwargs): + if not tag: + tag = cls.get_signature() + mem_obj = cls._alloc(alloc, tag, *alloc_args) + if not mem_obj: + return None + # create instance of this or alias type + cls_type = cls.get_alias_type() + return cls_type( + mem=alloc.get_mem(), + addr=mem_obj.addr, + alloc=alloc, + mem_obj=mem_obj, + **kwargs, + ) + + @classmethod + def _alloc(cls, alloc, tag): + return alloc.alloc_memory(tag, cls._byte_size) + + @classmethod + def _free(cls, alloc, mem_obj): + alloc.free_memory(mem_obj) + + def free(self): + assert self._alloc and self._mem_obj + self._free(self._alloc, self._mem_obj) + self._alloc = None + self._mem_obj = None diff --git a/amitools/vamos/astructs/util.py b/amitools/vamos/astructs/util.py deleted file mode 100644 index 90c61953..00000000 --- a/amitools/vamos/astructs/util.py +++ /dev/null @@ -1,21 +0,0 @@ -from .astructdef import AmigaStructDef -from .astruct import AmigaStruct - -# TagItem -@AmigaStructDef -class TagItemStruct(AmigaStruct): - _format = [("ULONG", "ti_Tag"), ("ULONG", "ti_Data")] - - -# ClockData -@AmigaStructDef -class ClockDataStruct(AmigaStruct): - _format = [ - ("UWORD", "sec"), - ("UWORD", "min"), - ("UWORD", "hour"), - ("UWORD", "mday"), - ("UWORD", "month"), - ("UWORD", "year"), - ("UWORD", "wday"), - ] diff --git a/amitools/vamos/atypes/__init__.py b/amitools/vamos/atypes/__init__.py deleted file mode 100644 index 8b8269fa..00000000 --- a/amitools/vamos/atypes/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ -from .enum import EnumType -from .bitfield import BitFieldType -from .cstring import CString -from .bstring import BString - -from .atype import AmigaType, AmigaTypeWithName -from .atypedef import AmigaTypeDef - -# exec -from .node import Node, NodeType, MinNode -from .list_ import List, MinList -from .library import Library, LibFlags -from .resident import Resident, ResidentFlags, AutoInit -from .execlib import ExecLibrary -from .task import Task, TaskFlags, TaskState -from .msg import MsgPort, Message, MsgPortFlags - -# dos -from .lock import FileLock, FileHandle -from .process import CLI, Process, PathList diff --git a/amitools/vamos/atypes/atype.py b/amitools/vamos/atypes/atype.py deleted file mode 100644 index e9668d45..00000000 --- a/amitools/vamos/atypes/atype.py +++ /dev/null @@ -1,142 +0,0 @@ -from .cstring import CString - - -class AmigaType(object): - - # will be set by decorator - _type_pool = {} - _struct_def = None - _type_name = None - - @classmethod - def find_type(cls, name): - if name in cls._type_pool: - return cls._type_pool[name] - - @classmethod - def get_type_name(cls): - return cls._type_name - - @classmethod - def get_type_size(cls): - return cls._struct_def.get_size() - - @classmethod - def get_type_struct(cls): - return cls._struct_def - - def __init__(self, mem, addr): - assert type(addr) is int - self._mem = mem - self._addr = addr - self._struct = self._struct_def(mem, addr) - # allocation extra info - self._alloc = None - self._mem_obj = None - self._size = None - - def __eq__(self, other): - if type(other) is int: - return self._addr == other - elif isinstance(other, AmigaType): - return self._addr == other._addr - else: - return NotImplemented - - def get_mem(self): - return self._mem - - def get_addr(self): - return self._addr - - def read_data(self): - return self._struct.read_data() - - def get_size(self): - if self._size: - return self._size - else: - return self.get_type_size() - - def write_data(self): - return self._struct.write_data() - - @classmethod - def init_from(cls, other): - if isinstance(other, AmigaType): - return cls(other._mem, other._addr) - else: - raise ValueError("no AType!") - - @classmethod - def alloc(cls, alloc, tag=None, size=None, add_label=True): - return cls._alloc(alloc, tag=tag, size=size, add_label=add_label) - - @classmethod - def _alloc(cls, alloc, tag=None, size=None, add_label=True): - if tag is None: - tag = cls._type_name - if size is None: - size = cls.get_type_size() - struct = cls.get_type_struct() - mem_obj = alloc.alloc_struct(tag, struct, size, add_label=add_label) - mem = alloc.get_mem() - obj = cls(mem, mem_obj.addr) - obj._alloc = alloc - obj._mem_obj = mem_obj - obj._size = size - return obj - - def free(self): - if self._alloc: - self._alloc.free_struct(self._mem_obj) - self._alloc = None - self._mem_obj = None - self._addr = 0 - else: - raise RuntimeError("can't free") - - def __getattr__(self, name): - # find associated getter - if name.startswith("get_") or name.startswith("set_"): - raise AttributeError - func_name = "get_" + name - func = getattr(self, func_name, None) - if func: - return func() - else: - raise AttributeError - - def __setattr__(self, name, val): - if name[0] == "_": - object.__setattr__(self, name, val) - else: - func_name = "set_" + name - func = getattr(self, func_name, None) - if func: - func(val) - else: - raise AttributeError - - -class AmigaTypeWithName(AmigaType): - def __init__(self, mem, addr, alloc=None): - AmigaType.__init__(self, mem, addr) - # extra alloc info - self._name_cstr = None - - def set_name(self, name): - pass - - @classmethod - def alloc(cls, alloc, name=None): - obj = cls._alloc(alloc, name) - if name: - obj._name_cstr = CString.alloc(alloc, name) - obj.set_name(obj._name_cstr) - return obj - - def free(self): - AmigaType.free(self) - if self._name_cstr: - self._name_cstr.free() diff --git a/amitools/vamos/atypes/atypedef.py b/amitools/vamos/atypes/atypedef.py deleted file mode 100644 index 5ccd11c4..00000000 --- a/amitools/vamos/atypes/atypedef.py +++ /dev/null @@ -1,236 +0,0 @@ -import re -from .atype import AmigaType, AmigaTypeWithName -from .cstring import CString - - -class AmigaTypeDecorator(object): - def __init__(self, struct_def, wrap, funcs, allow_struct): - if wrap is None: - wrap = {} - self.struct_def = struct_def - self.wrap = wrap - self.funcs = funcs - self.allow_struct = allow_struct - - def decorate(self, cls): - name = self._validate_class(cls) - cls._type_name = name - # store struct def - cls._struct_def = self.struct_def - # add to pool - cls._type_pool[name] = cls - # finally create methods - self._gen_field_methods(cls) - # add custom functions - if self.funcs: - self._add_custom_funcs(cls, self.funcs) - return cls - - def _add_custom_funcs(self, cls, funcs): - for name in funcs: - func = funcs[name] - setattr(cls, name, func) - - def _validate_class(self, cls): - # make sure cls is derived from AmigaStruct - if cls.__bases__ != (AmigaType,) and cls.__bases__ != (AmigaTypeWithName,): - raise RuntimeError("cls must dervive from AmigaType") - # get name of type - name = self.struct_def.get_type_name() - return name - - def _gen_field_methods(self, cls): - # add get/set methods - for field in self.struct_def.get_fields(): - # make a lowercase/underscore name without prefix - # e.g. lh_TailPred -> tail_pred - base_name = self._name_convert(field.name) - - # c_str handling - if field.type_sig == "char*": - self._gen_cstr_get_set(base_name, cls, field) - # struct types - elif field.struct_type: - if field.is_pointer: - self._gen_struct_ptr_get_set(base_name, cls, field) - else: - self._gen_struct_get(base_name, cls, field) - # base types - else: - wrap_funcs = self._is_wrapped(field, base_name) - if wrap_funcs: - self._gen_wrap_get_set(base_name, cls, field, wrap_funcs) - else: - self._gen_default_get_set(base_name, cls, field) - - # common field method - self._gen_common_field_methods(base_name, cls, field) - - def _get_gen_type(self, cls, field): - # what type to generate for a struct field? - gen_type = field.struct_type - # its me -> use my type class - if cls._struct_def == gen_type: - return cls - # find other type - name = gen_type.get_type_name() - t_type = cls.find_type(name) - if t_type is None: - # no type class found. can we use struct type? - if not self.allow_struct: - raise RuntimeError("can't find type for ptr: " + name) - else: - gen_type = t_type - return gen_type - - def _gen_struct_ptr_get_set(self, base_name, cls, field): - """access a struct pointer. return associated struct_type""" - index = field.index - gen_type = self._get_gen_type(cls, field) - - def get_struct_ptr(self, ptr=False): - addr = int(self._struct.read_field_index(index)) - if ptr: - return addr - if addr == 0: - return None - return gen_type(self.mem, addr) - - def set_struct_ptr(self, val): - if not type(val) is int: - if val is None: - val = 0 - elif isinstance(val, gen_type): - # make sure its the correct type - val = val.addr - else: - raise ValueError( - "invalid type assign: want=%s, got=%s" % (gen_type, type(val)) - ) - self._struct.write_field_index(index, val) - - self._setup_get_set(base_name, cls, get_struct_ptr, set_struct_ptr) - - def _gen_struct_get(self, base_name, cls, field): - """generate a getter for embedded structs""" - gen_type = self._get_gen_type(cls, field) - - def get_struct(self): - addr = self.addr + field.offset - return gen_type(self.mem, addr) - - setattr(cls, "get_" + base_name, get_struct) - - def _is_wrapped(self, field, base_name): - # allow field name - if field.name in self.wrap: - return self.wrap[field.name] - # and converted base name - elif base_name in self.wrap: - return self.wrap[base_name] - - def _gen_common_field_methods(self, base_name, cls, field): - def get_field_addr(self): - """return the address of the field itself""" - return self.addr + field.offset - - setattr(cls, "get_" + base_name + "_addr", get_field_addr) - - def _gen_cstr_get_set(self, base_name, cls, field): - index = field.index - - def get_cstr(self, ptr=False): - """return the c_str or "" if ptr==0 - or the addr of the pointer (addr=True)""" - addr = self._struct.read_field_index(index) - if ptr: - return addr - return CString(self.mem, addr) - - def set_cstr(self, val): - """set a c_str either by address or with a CString object""" - if type(val) is int: - ptr = val - elif isinstance(val, CString): - ptr = val.get_addr() - else: - raise ValueError("set cstring: wrong value: %s" % val) - self._struct.write_field_index(index, ptr) - - self._setup_get_set(base_name, cls, get_cstr, set_cstr) - - def _setup_get_set(self, base_name, cls, get_func, set_func): - setattr(cls, "get_" + base_name, get_func) - setattr(cls, "set_" + base_name, set_func) - - def _gen_default_get_set(self, base_name, cls, field): - index = field.index - - def get_func(self): - return self._struct.read_field_index(index) - - def set_func(self, val): - self._struct.write_field_index(index, val) - - self._setup_get_set(base_name, cls, get_func, set_func) - - def _gen_wrap_get_set(self, base_name, cls, field, wrap_funcs): - if type(wrap_funcs) in (list, tuple): - get_wrap = wrap_funcs[0] - set_wrap = wrap_funcs[1] - else: - get_wrap = wrap_funcs - set_wrap = None - - # default conversion is integer conversion - if set_wrap is None: - set_wrap = int - - index = field.index - if get_wrap: - - def get_func(self, raw=False): - val = self._struct.read_field_index(index) - if raw: - return val - return get_wrap(val) - - else: - - def get_func(self): - return self._struct.read_field_index(index) - - if set_wrap: - - def set_func(self, val, raw=False): - if not raw: - val = set_wrap(val) - self._struct.write_field_index(index, val) - - else: - - def set_func(self, val): - self._struct.write_field_index(index, val) - - self._setup_get_set(base_name, cls, get_func, set_func) - - def _name_convert(self, name): - """convert camel case names to underscore""" - # strip leading prefix - pos = name.find("_") - if pos > 0: - name = name[pos + 1 :] - # to underscore - s1 = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", name) - return re.sub("([a-z0-9])([A-Z])", r"\1_\2", s1).lower() - - -def AmigaTypeDef(struct_def, wrap=None, funcs=None, allow_struct=False): - """a class decorator that automatically adds get/set methods - for AmigaStruct fields""" - decorator = AmigaTypeDecorator(struct_def, wrap, funcs, allow_struct) - - def deco_func(cls): - return decorator.decorate(cls) - - return deco_func diff --git a/amitools/vamos/atypes/bstring.py b/amitools/vamos/atypes/bstring.py deleted file mode 100644 index 51e053ef..00000000 --- a/amitools/vamos/atypes/bstring.py +++ /dev/null @@ -1,97 +0,0 @@ -class BString(object): - """Manage BCPL-Strings in Amiga memory. - - The string is immutable as changing strings typically requires - re-allocating memory for it. - """ - - def __init__(self, mem, addr, alloc=None, mem_obj=None, max_size=None): - self.mem = mem - self.addr = addr - self.alloc = alloc - self.mem_obj = mem_obj - self.baddr = addr >> 2 - self.max_size = max_size - assert self.addr & 3 == 0 - - def __str__(self): - return self.get_string() - - def __int__(self): - return self.addr - - def __repr__(self): - return "BString(@%06x:%s)" % (self.addr, self.get_string()) - - def __eq__(self, other): - if type(other) is int: - return self.addr == other - elif type(other) is str: - return self.get_string() == other - elif isinstance(other, BString): - return self.addr == other.addr - else: - return NotImplemented - - def get_mem(self): - return self.mem - - def get_addr(self): - return self.addr - - def get_baddr(self): - return self.baddr - - def get_max_size(self): - return self.max_size - - def get_size(self): - return len(self.get_string()) - - def get_string(self): - if self.addr == 0: - return None - else: - return self.mem.r_bstr(self.addr) - - def set_string(self, txt): - if self.max_size: - if len(txt) > self.max_size: - raise ValueError("new string too long!") - if self.addr == 0: - raise ValueError("bstr is NULL") - else: - return self.mem.w_bstr(self.addr, txt) - - def free(self): - if self.alloc: - self.alloc.free_bstr(self.mem_obj) - self.mem_obj = None - self.alloc = None - self.addr = 0 - self.max_size = 0 - - @staticmethod - def alloc(alloc, txt, tag=None): - """allocate memory for the given txt with the allocator - - Returns a BString object with allocation info. - You can free() the object later on - """ - if type(txt) is BString: - return txt - if tag is None: - tag = "BString('%s')" % txt - mem = alloc.get_mem() - # NULL ptr - if txt is None: - mem_obj = None - alloc = None - addr = 0 - n = 0 - # valid string - else: - mem_obj = alloc.alloc_bstr(tag, txt) - addr = mem_obj.addr - n = len(txt) - return BString(mem, addr, alloc, mem_obj, n) diff --git a/amitools/vamos/atypes/cstring.py b/amitools/vamos/atypes/cstring.py deleted file mode 100644 index db91e2f1..00000000 --- a/amitools/vamos/atypes/cstring.py +++ /dev/null @@ -1,92 +0,0 @@ -class CString(object): - """Manage C-Strings (with zero termination) in Amiga memory. - - The string is immutable as changing strings typically requires - re-allocating memory for it. - """ - - def __init__(self, mem, addr, alloc=None, mem_obj=None, max_size=None): - self.mem = mem - self.addr = addr - self.alloc = alloc - self.mem_obj = mem_obj - self.max_size = max_size - - def __str__(self): - return self.get_string() - - def __int__(self): - return self.addr - - def __repr__(self): - return "CString(@%06x:%s)" % (self.addr, self.get_string()) - - def __eq__(self, other): - if type(other) is int: - return self.addr == other - elif type(other) is str: - return self.get_string() == other - elif isinstance(other, CString): - return self.addr == other.addr - else: - return NotImplemented - - def get_mem(self): - return self.mem - - def get_addr(self): - return self.addr - - def get_string(self): - if self.addr == 0: - return None - else: - return self.mem.r_cstr(self.addr) - - def get_max_size(self): - return self.max_size - - def get_size(self): - return len(self.get_string()) - - def set_string(self, txt): - if self.max_size: - if len(txt) > self.max_size: - raise ValueError("new string too long!") - if self.addr == 0: - raise ValueError("cstr is NULL") - else: - self.mem.w_cstr(self.addr, txt) - - def free(self): - if self.alloc: - self.alloc.free_cstr(self.mem_obj) - self.mem_obj = None - self.alloc = None - self.addr = 0 - self.max_size = 0 - - @staticmethod - def alloc(alloc, txt, tag=None): - """allocate memory for the given txt with the allocator - - Returns a CString object with allocation info. - You can free() the object later on - """ - if type(txt) is CString: - return txt - if tag is None: - tag = "CString('%s')" % txt - mem = alloc.get_mem() - # NULL ptr - if txt is None: - mem_obj = None - alloc = None - addr = 0 - n = 0 - # valid string - else: - mem_obj = alloc.alloc_cstr(tag, txt) - addr = mem_obj.addr - n = len(txt) - return CString(mem, addr, alloc, mem_obj, n) diff --git a/amitools/vamos/atypes/execlib.py b/amitools/vamos/atypes/execlib.py deleted file mode 100644 index 52281da2..00000000 --- a/amitools/vamos/atypes/execlib.py +++ /dev/null @@ -1,71 +0,0 @@ -from amitools.vamos.astructs import ( - ExecLibraryStruct, - SoftIntListStruct, - IntVectorStruct, -) -from .library import Library -from .atype import AmigaType -from .atypedef import AmigaTypeDef -from .bitfield import BitFieldType -from .node import NodeType -from .task import Task - - -@BitFieldType -class AttnFlags: - AFF_68010 = 1 << 0 - AFF_68020 = 1 << 1 - AFF_68030 = 1 << 2 - AFF_68040 = 1 << 3 - AFF_68881 = 1 << 4 - AFF_68882 = 1 << 5 - AFF_FPU40 = 1 << 6 - AFF_68060 = 1 << 7 - - -@AmigaTypeDef(SoftIntListStruct) -class SoftIntList(AmigaType): - pass - - -@AmigaTypeDef(IntVectorStruct) -class IntVector(AmigaType): - pass - - -@AmigaTypeDef(ExecLibraryStruct, wrap={"attn_flags": AttnFlags}) -class ExecLibrary(AmigaType): - def __init__(self, mem, addr): - AmigaType.__init__(self, mem, addr) - # extra alloc info - self._lib = None - - def setup(self, version=0, revision=0, attn_flags=0, max_loc_mem=0): - self.lib_node.setup(version, revision) - self.attn_flags = attn_flags - self.max_loc_mem = max_loc_mem - # init lists - self.mem_list.new_list(NodeType.NT_MEMORY) - self.resource_list.new_list(NodeType.NT_RESOURCE) - self.device_list.new_list(NodeType.NT_DEVICE) - self.intr_list.new_list(NodeType.NT_INTERRUPT) - self.lib_list.new_list(NodeType.NT_LIBRARY) - self.port_list.new_list(NodeType.NT_MSGPORT) - self.task_ready.new_list(NodeType.NT_TASK) - self.task_wait.new_list(NodeType.NT_TASK) - self.semaphore_list.new_list(NodeType.NT_SEMAPHORE) - self.mem_handlers.new_list() - - def fill_funcs(self, opcode=None, param=None): - self.lib_node.fill_funcs(opcode, param) - - @classmethod - def alloc(cls, alloc, name, id_str, neg_size, pos_size=None): - pos_size = cls.get_type_size() - lib = Library.alloc(alloc, name, id_str, neg_size, pos_size) - exec_lib = ExecLibrary.init_from(lib) - exec_lib._lib = lib - return exec_lib - - def free(self): - self._lib.free() diff --git a/amitools/vamos/atypes/library.py b/amitools/vamos/atypes/library.py deleted file mode 100644 index 11e5cf57..00000000 --- a/amitools/vamos/atypes/library.py +++ /dev/null @@ -1,155 +0,0 @@ -from amitools.vamos.astructs import LibraryStruct -from amitools.vamos.label import LabelLib -from amitools.vamos.machine.opcodes import op_rts -from .atype import AmigaType -from .atypedef import AmigaTypeDef -from .node import NodeType -from .bitfield import BitFieldType -from .cstring import CString - - -@BitFieldType -class LibFlags(object): - LIBF_SUMMING = 1 << 0 - LIBF_CHANGED = 1 << 1 - LIBF_SUMUSED = 1 << 2 - LIBF_DELEXP = 1 << 3 - - -@AmigaTypeDef(LibraryStruct, wrap={"flags": LibFlags}) -class Library(AmigaType): - def __init__(self, mem, addr, alloc=None): - AmigaType.__init__(self, mem, addr) - # extra alloc info - self._name_cstr = None - self._id_str_cstr = None - self._label = None - self._alloc = alloc - self._mem_obj = None - - def set_name(self, val): - self.get_node().set_name(val) - - def get_name(self, ptr=False): - return self.get_node().get_name(ptr) - - def setup(self, version=0, revision=0, type=NodeType.NT_LIBRARY, pri=0, flags=0): - """set all lib values but name, id_str, pos_size, neg_size.""" - node = self.get_node() - node.set_succ(0) - node.set_pred(0) - node.set_type(type) - node.set_pri(pri) - - self.set_flags(flags) - self.set_pad(0) - self.set_version(version) - self.set_revision(revision) - self.set_sum(0) - self.set_open_cnt(0) - - def fill_funcs(self, opcode=None, param=None): - """quickly fill the function table of a library with an opcode and param""" - if opcode is None: - opcode = op_rts - neg_size = self.neg_size - off = 6 - while off < neg_size: - addr = self._addr - off - self._mem.w16(addr, opcode) - if param: - self._mem.w32(addr + 2, param) - off += 6 - - @classmethod - def alloc(cls, alloc, name, id_str, neg_size, pos_size=None, fd=None): - """alocate library and optional name and id_str CStrings""" - # calc size - if pos_size is None: - pos_size = cls.get_type_size() - # round neg_size to multiple of four - neg_size = (neg_size + 3) & ~3 - total_size = pos_size + neg_size - # allocate lib - mem_obj = alloc.alloc_memory(None, total_size, add_label=False) - addr_base = mem_obj.addr + neg_size - lib = cls(alloc.get_mem(), addr_base) - lib._mem_obj = mem_obj - lib._alloc = alloc - lib._size = total_size - # set pos/neg size - lib.set_pos_size(pos_size) - lib.set_neg_size(neg_size) - # add label? - label_mgr = alloc.get_label_mgr() - if label_mgr: - struct = lib.get_type_struct() - lib._label = LabelLib(name, addr_base, neg_size, pos_size, struct, fd) - label_mgr.add_label(lib._label) - # set name and id_str - if name: - lib._name_cstr = CString.alloc(alloc, name) - lib.set_name(lib._name_cstr) - if id_str: - lib._id_str_cstr = CString.alloc(alloc, id_str) - lib.set_id_string(lib._id_str_cstr) - return lib - - def free(self): - if not self._alloc: - raise RuntimeError("can't free") - mem_obj = self._mem_obj - if mem_obj is None: - addr = self._addr - self.neg_size - mem_obj = self._alloc.get_memory(addr) - self._alloc.free_memory(mem_obj) - # cleanup name - if self._name_cstr: - self._name_cstr.free() - self._name_cstr = None - # cleanup id str - if self._id_str_cstr: - self._id_str_cstr.free() - self._id_str_cstr = None - # cleanup label - if self._label: - self._alloc.get_label_mgr().remove_label(self._label) - self._label = None - # clear state - self._alloc = None - self._mem_obj = None - self._addr = 0 - - def calc_sum(self): - """calc the lib sum and return it""" - neg_size = self.get_neg_size() - addr = self._addr - neg_size - lib_sum = 0 - while addr < self._addr: - val = self._mem.r32(addr) - lib_sum += val - addr += 4 - lib_sum &= 0xFFFFFFFF - return lib_sum - - def update_sum(self): - """calc new lib sum and store it""" - lib_sum = self.calc_sum() - self.set_sum(lib_sum) - return lib_sum - - def check_sum(self): - """calc and compare lib sum with stored value""" - lib_sum = self.calc_sum() - got_sum = self.get_sum() - return lib_sum == got_sum - - def inc_open_cnt(self): - cnt = self.get_open_cnt() - cnt += 1 - self.set_open_cnt(cnt) - - def dec_open_cnt(self): - cnt = self.get_open_cnt() - cnt -= 1 - self.set_open_cnt(cnt) diff --git a/amitools/vamos/atypes/list_.py b/amitools/vamos/atypes/list_.py deleted file mode 100644 index eb341b21..00000000 --- a/amitools/vamos/atypes/list_.py +++ /dev/null @@ -1,179 +0,0 @@ -from amitools.vamos.astructs import ListStruct, MinListStruct -from .node import Node, NodeType, MinNode -from .atype import AmigaType -from .atypedef import AmigaTypeDef - - -class ListIter(object): - def __init__(self, alist, start_node=None): - self.alist = alist - self.mem = self.alist._mem - if start_node is None: - self.node = alist._head.get_succ() - else: - self.node = start_node - - def __iter__(self): - return self - - def __next__(self): - succ = self.node.get_succ() - if succ is None: - raise StopIteration() - res = self.node - self.node = succ - return res - - -# common list funcs - - -def iter_func(self): - return ListIter(self) - - -def len_func(self): - l = 0 - node = self._head.get_succ() - while True: - node = node.get_succ() - if node is None: - break - l += 1 - return l - - -def iter_at(self, node): - return ListIter(self, node) - - -def add_head(self, node): - n = self._head.get_succ() - node.set_pred(self._head) - node.set_succ(n) - self._head.set_succ(node) - n.set_pred(node) - - -def add_tail(self, node): - tp = self.get_tail_pred() - node.set_succ(self._tail) - self._tail.set_pred(node) - node.set_pred(tp) - tp.set_succ(node) - - -def rem_head(self): - node = self._head.get_succ() - if node is None: - return None - node.remove() - return node - - -def rem_tail(self): - node = self.get_tail_pred() - if node is None: - return None - node.remove() - return node - - -def insert(self, node, pred): - if pred is not None and pred != self._head: - pred_succ = pred.get_succ() - if pred_succ: - # normal node - node.set_succ(pred_succ) - node.set_pred(pred) - pred_succ.set_pred(node) - pred.set_succ(node) - else: - # last node - self.add_tail(node) - else: - # first node - self.add_head(node) - - -funcs = { - "__iter__": iter_func, - "__len__": len_func, - "iter_at": iter_at, - "add_head": add_head, - "add_tail": add_tail, - "rem_head": rem_head, - "rem_tail": rem_tail, - "insert": insert, -} - - -@AmigaTypeDef(MinListStruct, funcs=funcs) -class MinList(AmigaType): - def __init__(self, mem, addr): - AmigaType.__init__(self, mem, addr) - self._head = MinNode(mem, self.addr) - self._tail = MinNode(mem, self.addr + 4) - - def __str__(self): - return "[MinList:@%06x,h=%06x,t=%06x,tp=%06x]" % ( - self.addr, - self.get_head(True), - self.get_tail(True), - self.get_tail_pred(True), - ) - - def new_list(self): - self.set_head(self._tail) - self.set_tail(0) - self.set_tail_pred(self._head) - - -@AmigaTypeDef(ListStruct, wrap={"type": NodeType}, funcs=funcs) -class List(AmigaType): - def __init__(self, mem, addr): - AmigaType.__init__(self, mem, addr) - self._head = Node(mem, self.addr) - self._tail = Node(mem, self.addr + 4) - - def __str__(self): - return "[List:@%06x,h=%06x,t=%06x,tp=%06x,%s]" % ( - self.addr, - self.get_head(True), - self.get_tail(True), - self.get_tail_pred(True), - self.get_type(), - ) - - # ----- list ops ----- - - def new_list(self, lt): - self.set_type(lt) - self.set_head(self._tail) - self.set_tail(0) - self.set_tail_pred(self._head) - - def enqueue(self, node): - pred = None - pri = node.get_pri() - for ln in self: - ln_pri = ln.get_pri() - if ln_pri < pri: - self.insert(node, pred) - return - pred = ln - self.add_tail(node) - - def find_names(self, name): - """this method is a generator delivering all matches""" - for node in self: - node_name = node.get_name() - if node_name == name: - yield node - - def find_name(self, name): - """this method is a function returning the first match""" - for node in self: - node_name = node.get_name() - if node_name == name: - return node diff --git a/amitools/vamos/atypes/lock.py b/amitools/vamos/atypes/lock.py deleted file mode 100644 index e25908f3..00000000 --- a/amitools/vamos/atypes/lock.py +++ /dev/null @@ -1,13 +0,0 @@ -from amitools.vamos.astructs import FileLockStruct, FileHandleStruct -from .atype import AmigaType -from .atypedef import AmigaTypeDef - - -@AmigaTypeDef(FileLockStruct) -class FileLock(AmigaType): - pass - - -@AmigaTypeDef(FileHandleStruct) -class FileHandle(AmigaType): - pass diff --git a/amitools/vamos/atypes/msg.py b/amitools/vamos/atypes/msg.py deleted file mode 100644 index 2f717849..00000000 --- a/amitools/vamos/atypes/msg.py +++ /dev/null @@ -1,44 +0,0 @@ -from amitools.vamos.astructs import MessageStruct, MsgPortStruct -from .atype import AmigaTypeWithName -from .atypedef import AmigaTypeDef -from .enum import EnumType -from .node import NodeType - - -@EnumType -class MsgPortFlags(object): - PA_SIGNAL = 0 - PA_SOFTINT = 1 - PA_IGNORE = 2 - - -@AmigaTypeDef(MsgPortStruct, wrap={"Flags": MsgPortFlags}) -class MsgPort(AmigaTypeWithName): - def set_name(self, val): - self.get_node().set_name(val) - - def get_name(self, ptr=False): - return self.get_node().get_name(ptr) - - def setup(self, pri=0, flags=0, sig_bit=0, sig_task=0, nt=NodeType.NT_MSGPORT): - self.node.set_pri(pri) - self.node.set_type(nt) - self.set_flags(flags) - self.set_sig_bit(sig_bit) - self.set_sig_task(sig_task) - self.msg_list.new_list(NodeType.NT_MESSAGE) - - -@AmigaTypeDef(MessageStruct) -class Message(AmigaTypeWithName): - def set_name(self, val): - self.get_node().set_name(val) - - def get_name(self, ptr=False): - return self.get_node().get_name(ptr) - - def setup(self, pri=0, reply_port=0, length=0, nt=NodeType.NT_MESSAGE): - self.node.set_pri(pri) - self.node.set_type(nt) - self.set_reply_port(reply_port) - self.set_length(length) diff --git a/amitools/vamos/atypes/node.py b/amitools/vamos/atypes/node.py deleted file mode 100644 index dabf9724..00000000 --- a/amitools/vamos/atypes/node.py +++ /dev/null @@ -1,105 +0,0 @@ -from amitools.vamos.astructs import NodeStruct, MinNodeStruct -from .atype import AmigaType, AmigaTypeWithName -from .atypedef import AmigaTypeDef -from .enum import EnumType -from .cstring import CString - - -@EnumType -class NodeType(object): - """manage valid node type constants and conversions""" - - NT_UNKNOWN = 0 - NT_TASK = 1 - NT_INTERRUPT = 2 - NT_DEVICE = 3 - NT_MSGPORT = 4 - NT_MESSAGE = 5 - NT_FREEMSG = 6 - NT_REPLYMSG = 7 - NT_RESOURCE = 8 - NT_LIBRARY = 9 - NT_MEMORY = 10 - NT_SOFTINT = 11 - NT_FONT = 12 - NT_PROCESS = 13 - NT_SEMAPHORE = 14 - NT_SIGNALSEM = 15 - NT_BOOTNODE = 16 - NT_KICKMEM = 17 - NT_GRAPHICS = 18 - NT_DEATHMESSAGE = 19 - - NT_USER = 254 - NT_EXTENDED = 255 - - -# common funcs for nodes - - -def remove(self, clear=True): - succ = self.get_succ() - pred = self.get_pred() - if succ is None or pred is None: - raise ValueError("remove node without succ/pred!") - succ.set_pred(pred) - pred.set_succ(succ) - if clear: - self.set_succ(None) - self.set_pred(None) - - -funcs = {"remove": remove} - - -@AmigaTypeDef(MinNodeStruct, funcs=funcs) -class MinNode(AmigaType): - """wrap an Exec MinNode in memory an allow to operate on its values. - """ - - def __str__(self): - return "[MinNode:@%06x,p=%06x,s=%06x]" % ( - self.addr, - self.get_pred(True), - self.get_succ(True), - ) - - def setup(self, succ, pred): - self.set_succ(succ) - self.set_pred(pred) - - -@AmigaTypeDef(NodeStruct, wrap={"type": NodeType}, funcs=funcs) -class Node(AmigaTypeWithName): - """wrap an Exec Node in memory an allow to operate on its values. - """ - - def __str__(self): - return "[Node:@%06x,p=%06x,s=%06x,%s,%d,'%s']" % ( - self.addr, - self.get_pred(True), - self.get_succ(True), - self.get_type(), - self.get_pri(), - self.get_name(), - ) - - def setup(self, succ, pred, nt, pri, name=None): - self.set_succ(succ) - self.set_pred(pred) - self.set_type(nt) - self.set_pri(pri) - if name: - self.set_name(name) - - # ----- node ops ----- - - def find_name(self, name): - """find name after this node""" - succ = self.get_succ() - if succ is None: - return None - succ_name = succ.get_name() - if succ_name == name: - return succ - return succ.find_name(name) diff --git a/amitools/vamos/atypes/process.py b/amitools/vamos/atypes/process.py deleted file mode 100644 index a9150382..00000000 --- a/amitools/vamos/atypes/process.py +++ /dev/null @@ -1,28 +0,0 @@ -from amitools.vamos.astructs import ProcessStruct, CLIStruct, PathListStruct -from .atype import AmigaTypeWithName, AmigaType -from .atypedef import AmigaTypeDef -from .node import NodeType - - -@AmigaTypeDef(PathListStruct) -class PathList(AmigaType): - pass - - -@AmigaTypeDef(CLIStruct) -class CLI(AmigaType): - pass - - -@AmigaTypeDef(ProcessStruct) -class Process(AmigaTypeWithName): - def setup(self): - self.task.setup(nt=NodeType.NT_PROCESS) - self.msg_port.setup() - self.local_vars.new_list() - - def set_name(self, val): - self.task.node.name = val - - def get_name(self, ptr=False): - return self.task.node.get_name(ptr) diff --git a/amitools/vamos/atypes/resident.py b/amitools/vamos/atypes/resident.py deleted file mode 100644 index fbeff8ea..00000000 --- a/amitools/vamos/atypes/resident.py +++ /dev/null @@ -1,134 +0,0 @@ -from amitools.vamos.astructs import ResidentStruct, AutoInitStruct, LibraryStruct -from amitools.vamos.mem import MemoryCache -from .bitfield import BitFieldType -from .node import NodeType -from .atype import AmigaType -from .atypedef import AmigaTypeDef -from .cstring import CString - - -@BitFieldType -class ResidentFlags: - RTF_AUTOINIT = 1 << 7 - RTF_AFTERDOS = 1 << 2 - RTF_SINGLETASK = 1 << 1 - RTF_COLDSTART = 1 << 0 - - -@AmigaTypeDef(AutoInitStruct) -class AutoInit(AmigaType): - def setup(self, pos_size=0, functions=0, init_struct=0, init_func=0): - if pos_size == 0: - pos_size = LibraryStruct.get_size() - self.pos_size = pos_size - self.functions = functions - self.init_struct = init_struct - self.init_func = init_func - - -@AmigaTypeDef(ResidentStruct, wrap={"flags": ResidentFlags, "type": NodeType}) -class Resident(AmigaType): - - RTC_MATCHWORD = 0x4AFC - - def __init__(self, mem, addr): - AmigaType.__init__(self, mem, addr) - # extra alloc info - self._name_obj = None - self._id_str_obj = None - - @classmethod - def find(cls, mem, addr, size, only_first=True, mem_cache=True): - """scan a memory region for resident structures and return the residents. - if 'only_first' is set return a single instance or None. - otherwise a list of Resident objects. - """ - # use a memory cache to speed up search - if mem_cache: - memc = MemoryCache(addr, size) - memc.read_cache(mem) - mem = memc - # start search - end_addr = addr + size - finds = [] - while addr < end_addr: - # look for match word - mw = mem.r16(addr) - if mw == cls.RTC_MATCHWORD: - # check pointer - ptr = mem.r32(addr + 2) - if ptr == addr: - # yes its a resident... - if only_first: - return cls(mem, addr) - finds.append(cls(mem, addr)) - # read end skip - addr = mem.r32(addr + 6) - addr += 2 - # nothing found for single match: - if only_first: - return None - return finds - - @classmethod - def alloc(cls, alloc, name, id_str): - """alocate resident and optional name and id_str CStrings""" - # handle name - if type(name) is CString: - name_obj = None - elif type(name) is str: - tag = "ResName(%s)" % name - name_obj = CString.alloc(alloc, name, tag) - name = name_obj - else: - raise ValueError("name must be str or CString") - # handle id_str - if type(id_str) is CString: - id_str_obj = None - elif type(id_str) is str: - tag = "ResIdStr(%s)" % name - id_str_obj = CString.alloc(alloc, id_str, tag) - id_str = id_str_obj - else: - raise ValueError("id_str must be str or CString") - # allocate resident - res = cls._alloc(alloc) - res._name_obj = name_obj - res._id_str_obj = id_str_obj - res.set_name(name) - res.set_id_string(id_str) - return res - - def free(self): - if not self.alloc: - raise RuntimeError("can't free") - AmigaType.free(self) - # cleanup name - if self._name_obj: - self._name_obj.free() - self._name_obj = None - # cleanup id str - if self._id_str_obj: - self._id_str_obj.free() - self._id_str_obj = None - - def is_valid(self): - if self.match_word != self.RTC_MATCHWORD: - return False - return self.match_tag == self.get_addr() - - def setup(self, flags=0, version=0, type=NodeType.NT_LIBRARY, pri=0, init=0): - self.set_match_word(self.RTC_MATCHWORD) - self.set_match_tag(self.addr) - self.set_end_skip(self.addr + self.get_type_size()) - self.set_flags(flags) - self.set_version(version) - self.set_type(type) - self.set_pri(pri) - self.set_init(init) - - def get_auto_init(self): - return AutoInit(self._mem, self.get_init()) - - def set_auto_init(self, val): - self.set_init(val._addr) diff --git a/amitools/vamos/atypes/task.py b/amitools/vamos/atypes/task.py deleted file mode 100644 index 5286ae40..00000000 --- a/amitools/vamos/atypes/task.py +++ /dev/null @@ -1,46 +0,0 @@ -from amitools.vamos.astructs import TaskStruct -from .atype import AmigaTypeWithName -from .atypedef import AmigaTypeDef -from .bitfield import BitFieldType -from .enum import EnumType -from .node import NodeType -from .cstring import CString - - -@BitFieldType -class TaskFlags(object): - TF_PROCTIME = 1 << 0 - TF_ETASK = 1 << 3 - TF_STACKCHK = 1 << 4 - TF_EXCEPT = 1 << 5 - TF_SWITCH = 1 << 6 - TF_LAUNCH = 1 << 7 - - -@EnumType -class TaskState(object): - TS_INVALID = 0 - TS_ADDED = 1 - TS_RUN = 2 - TS_READY = 3 - TS_WAIT = 4 - TS_EXCEPT = 5 - TS_REMOVED = 6 - - -@AmigaTypeDef(TaskStruct, wrap={"Flags": TaskFlags, "State": TaskState}) -class Task(AmigaTypeWithName): - def setup(self, pri=0, flags=0, nt=NodeType.NT_TASK): - node = self.get_node() - node.set_type(nt) - node.set_pri(pri) - - self.set_flags(flags) - self.set_state(TaskState.TS_INVALID) - self.mem_entry.new_list(NodeType.NT_MEMORY) - - def set_name(self, val): - self.get_node().set_name(val) - - def get_name(self, ptr=False): - return self.get_node().get_name(ptr) diff --git a/amitools/vamos/cfgcore/main.py b/amitools/vamos/cfgcore/main.py index 540573dc..29c23240 100644 --- a/amitools/vamos/cfgcore/main.py +++ b/amitools/vamos/cfgcore/main.py @@ -49,15 +49,15 @@ def get_args(self): def parse(self, paths=None, args=None, cfg_dict=None): """convenience function that combines all other calls. - add file and skip args. - pre-parse args. - either load given config. - if no skip args: - try given paths for configs. first match wins. - finally parse args. - - return True if parsing was without errors else False - """ + add file and skip args. + pre-parse args. + either load given config. + if no skip args: + try given paths for configs. first match wins. + finally parse args. + + return True if parsing was without errors else False + """ self.add_file_arg() self.add_skip_arg() self.add_config_debug() @@ -169,8 +169,8 @@ def _ensure_arg_group(self): def pre_parse_args(self, args=None): """parse args to get the values for file and skip arg. - Returns (cfg_file, skip_cfg) or (None, False) - """ + Returns (cfg_file, skip_cfg) or (None, False) + """ self.args = self.ap.parse_args(args) if self.ap_error: log_cfg.error("args: %s", self.ap_error) diff --git a/amitools/vamos/lib/DosLibrary.py b/amitools/vamos/lib/DosLibrary.py index fba58f4a..53bba6aa 100644 --- a/amitools/vamos/lib/DosLibrary.py +++ b/amitools/vamos/lib/DosLibrary.py @@ -5,10 +5,28 @@ from amitools.vamos.machine.regs import * from amitools.vamos.libcore import LibImpl -from amitools.vamos.astructs import * +from amitools.vamos.astructs import AccessStruct +from amitools.vamos.libstructs import ( + DosLibraryStruct, + DosInfoStruct, + RootNodeStruct, + DateStampStruct, + DateTimeStruct, + LocalVarStruct, + NodeStruct, + SegmentStruct, + FileHandleStruct, + FileInfoBlockStruct, + InfoDataStruct, + DevProcStruct, + AnchorPathStruct, + RDArgsStruct, + CLIStruct, + DosPacketStruct, + PathStruct, +) from amitools.vamos.error import * from amitools.vamos.log import log_dos -from amitools.vamos.path import PathManager, AssignManager from .dos.Args import * from .dos.Error import * from .dos.AmiTime import * @@ -17,7 +35,6 @@ from .dos.DosTags import DosTags from .dos.PatternMatch import Pattern, pattern_parse, pattern_match from .dos.MatchFirstNext import MatchFirstNext -from amitools.vamos.label import LabelStruct from .dos.CommandLine import CommandLine from .dos.Process import Process from .dos.DosList import DosList @@ -411,6 +428,7 @@ def SetVar(self, ctx): size = ctx.cpu.r_reg(REG_D3) flags = ctx.cpu.r_reg(REG_D4) name = ctx.mem.r_cstr(name_ptr) + vtype = flags & 0xFF if buff_ptr == 0: if not flags & self.GVF_GLOBAL_ONLY: node = self.find_var(ctx, name, flags & 0xff) @@ -467,7 +485,7 @@ def FindSegment(self, ctx): log_dos.info("FindSegment(%s)" % needle) while seg_addr != 0: segment = AccessStruct(ctx.mem, SegmentStruct, seg_addr) - name_addr = seg_addr + SegmentStruct.get_field_offset_by_name("seg_Name") + name_addr = seg_addr + SegmentStruct.sdef.seg_Name.offset name = ctx.mem.r_bstr(name_addr) if name.lower() == needle.lower(): if (system and segment.r_s("seg_UC") < 0) or ( @@ -485,7 +503,7 @@ def AddSegment(self, ctx): system = ctx.cpu.r_reg(REG_D3) name = ctx.mem.r_cstr(name_ptr) seg_addr = self._alloc_mem("Segment", SegmentStruct.get_size() + len(name) + 1) - name_addr = seg_addr + SegmentStruct.get_field_offset_by_name("seg_Name") + name_addr = seg_addr + SegmentStruct.sdef.seg_Name.offset segment = ctx.alloc.map_struct("Segment", seg_addr, SegmentStruct) head_addr = self.dos_info.access.r_s("di_NetHand") segment.access.w_s("seg_Next", head_addr) diff --git a/amitools/vamos/lib/ExecLibrary.py b/amitools/vamos/lib/ExecLibrary.py index 6066217c..868d1e5f 100644 --- a/amitools/vamos/lib/ExecLibrary.py +++ b/amitools/vamos/lib/ExecLibrary.py @@ -1,11 +1,20 @@ from amitools.vamos.machine.regs import * from amitools.vamos.libnative import MakeFuncs, InitStruct, MakeLib, LibFuncs, InitRes from amitools.vamos.libcore import LibImpl -from amitools.vamos.astructs import * -from amitools.vamos.atypes import ExecLibrary as ExecLibraryType -from amitools.vamos.atypes import NodeType, List +from amitools.vamos.astructs import AccessStruct +from amitools.vamos.libstructs import ( + ExecLibraryStruct, + StackSwapStruct, + IORequestStruct, + ListStruct, + NodeStruct, + NodeType, + SignalSemaphoreStruct, +) +from amitools.vamos.libtypes import ExecLibrary as ExecLibraryType +from amitools.vamos.libtypes import List from amitools.vamos.log import log_exec -from amitools.vamos.error import * +from amitools.vamos.error import VamosInternalError, UnsupportedFeatureError from .lexec.PortManager import PortManager from .lexec.SemaphoreManager import SemaphoreManager from .lexec.Pool import Pool @@ -28,22 +37,22 @@ def setup_lib(self, ctx, base_addr): self.exec_lib.lib_list.new_list(NodeType.NT_LIBRARY) self.exec_lib.device_list.new_list(NodeType.NT_DEVICE) # set some system contants + attn_flags = 0 if ctx.cpu_name == "68030(fake)": - self.exec_lib.attn_flags = 7 + attn_flags = 7 elif ctx.cpu_name == "68020": - self.exec_lib.attn_flags = 3 + attn_flags = 3 elif ctx.cpu_name == "68040": - self.exec_lib.attn_flags = 127 - else: - self.exec_lib.attn_flags = 0 - self.exec_lib.max_loc_mem = ctx.ram_size + attn_flags = 127 + self.exec_lib.attn_flags.val = attn_flags + self.exec_lib.max_loc_mem.val = ctx.ram_size # create the port manager self.port_mgr = PortManager(ctx.alloc) self.semaphore_mgr = SemaphoreManager(ctx.alloc, ctx.mem) self.mem = ctx.mem def set_this_task(self, process): - self.exec_lib.this_task = process.this_task.addr + self.exec_lib.this_task.aptr = process.this_task.addr self.stk_lower = process.get_stack().get_lower() self.stk_upper = process.get_stack().get_upper() @@ -71,7 +80,7 @@ def Permit(self, ctx): def FindTask(self, ctx): task_ptr = ctx.cpu.r_reg(REG_A1) if task_ptr == 0: - addr = self.exec_lib.this_task.get_addr() + addr = self.exec_lib.this_task.aptr log_exec.info("FindTask: me=%06x" % addr) return addr else: diff --git a/amitools/vamos/lib/LibList.py b/amitools/vamos/lib/LibList.py index daade9fb..98b1c222 100644 --- a/amitools/vamos/lib/LibList.py +++ b/amitools/vamos/lib/LibList.py @@ -17,7 +17,7 @@ "dos.library": DosLibrary, "exec.library": ExecLibrary, "intuition.library": IntuitionLibrary, - "locale.library" : LocaleLibrary, + "locale.library": LocaleLibrary, "mathffp.library": MathFFPLibrary, "mathieeedoubbas.library": MathIEEEDoubBasLibrary, "mathieeedoubtrans.library": MathIEEEDoubTransLibrary, diff --git a/amitools/vamos/lib/LocaleLibrary.py b/amitools/vamos/lib/LocaleLibrary.py index a134d98d..15657bdf 100644 --- a/amitools/vamos/lib/LocaleLibrary.py +++ b/amitools/vamos/lib/LocaleLibrary.py @@ -1,61 +1,138 @@ +from amitools.vamos.machine.regs import * from amitools.vamos.libcore import LibImpl +from amitools.vamos.lib.util.TagList import * +from amitools.vamos.lib.util.AmiDate import * +from amitools.vamos.log import * + import string -from amitools.vamos.machine.regs import REG_A0, REG_D0 -class LocaleLibrary(LibImpl): +class LocaleLibrary(LibImpl): def OpenLocale(self, ctx): - self.name = ctx.cpu.r_reg(REG_A0) + name = ctx.cpu.r_reg(REG_A0) # dummy return 0 def CloseLocale(self, ctx): - self.locale = ctx.cpu.r_reg(REG_A0) + locale = ctx.cpu.r_reg(REG_A0) # dummy - accept all def IsUpper(self, ctx): - self.locale = ctx.cpu.r_reg(REG_A0) + locale = ctx.cpu.r_reg(REG_A0) character = ctx.cpu.r_reg(REG_D0) - return chr(character) in string.ascii_uppercase + return chr(character) in string.uppercase def IsLower(self, ctx): - self.locale = ctx.cpu.r_reg(REG_A0) + locale = ctx.cpu.r_reg(REG_A0) character = ctx.cpu.r_reg(REG_D0) - return chr(character) in string.ascii_lowercase + return chr(character) in string.lowercase def IsDigit(self, ctx): - self.locale = ctx.cpu.r_reg(REG_A0) + locale = ctx.cpu.r_reg(REG_A0) character = ctx.cpu.r_reg(REG_D0) return chr(character) in string.digits def IsPrint(self, ctx): - self.locale = ctx.cpu.r_reg(REG_A0) + locale = ctx.cpu.r_reg(REG_A0) character = ctx.cpu.r_reg(REG_D0) return chr(character) in string.printable def IsCntrl(self, ctx): - self.locale = ctx.cpu.r_reg(REG_A0) + locale = ctx.cpu.r_reg(REG_A0) character = ctx.cpu.r_reg(REG_D0) # maybe not ok... return character < 32 and not chr(character) in string.printable def IsXDigit(self, ctx): - self.locale = ctx.cpu.r_reg(REG_A0) + locale = ctx.cpu.r_reg(REG_A0) character = ctx.cpu.r_reg(REG_D0) return chr(character) in string.hexdigits def IsSpace(self, ctx): - self.locale = ctx.cpu.r_reg(REG_A0) + locale = ctx.cpu.r_reg(REG_A0) character = ctx.cpu.r_reg(REG_D0) return chr(character) in string.whitespace def IsPunct(self, ctx): - self.locale = ctx.cpu.r_reg(REG_A0) + locale = ctx.cpu.r_reg(REG_A0) character = ctx.cpu.r_reg(REG_D0) return chr(character) in string.punctuation def IsGraph(self, ctx): - self.locale = ctx.cpu.r_reg(REG_A0) + locale = ctx.cpu.r_reg(REG_A0) character = ctx.cpu.r_reg(REG_D0) - return chr(character) in string.printable and not chr(character) in string.whitespace + return ( + chr(character) in string.printable + and not chr(character) in string.whitespace + ) + + +""" + def CloseCatalog(self, ctx): + catalog = ctx.cpu.r_reg(REG_A0) + print "not implemented: CloseCatalog" + + def ConvToLower(self, ctx): + locale = ctx.cpu.r_reg(REG_A0) + character = ctx.cpu.r_reg(REG_D0) + print "not implemented: ConvToLower" + + def ConvToUpper(self, ctx): + locale = ctx.cpu.r_reg(REG_A0) + character = ctx.cpu.r_reg(REG_D0) + print "not implemented: ConvToUpper" + + def FormatDate(self, ctx): + locale = ctx.cpu.r_reg(REG_A0) + fmtTemplate = ctx.cpu.r_reg(REG_A1) + date = ctx.cpu.r_reg(REG_A2) + putCharFunc = ctx.cpu.r_reg(REG_A3) + print "not implemented: FormatDate" + + def FormatString(self, ctx): + locale = ctx.cpu.r_reg(REG_A0) + fmtTemplate = ctx.cpu.r_reg(REG_A1) + dataStream = ctx.cpu.r_reg(REG_A2) + putCharFunc = ctx.cpu.r_reg(REG_A3) + print "not implemented: FormatString" + + def GetCatalogStr(self, ctx): + catalog = ctx.cpu.r_reg(REG_A0) + defaultString = ctx.cpu.r_reg(REG_A1) + stringNum = ctx.cpu.r_reg(REG_D0) + print "not implemented: GetCatalogStr" + + def GetLocaleStr(self, ctx): + locale = ctx.cpu.r_reg(REG_A0) + stringNum = ctx.cpu.r_reg(REG_D0) + print "not implemented: GetLocaleStr" + + def OpenCatalogA(self, ctx): + locale = ctx.cpu.r_reg(REG_A0) + name = ctx.cpu.r_reg(REG_A1) + tagList = ctx.cpu.r_reg(REG_A2) + print "not implemented: OpenCatalogA" + + def ParseDate(self, ctx): + locale = ctx.cpu.r_reg(REG_A0) + date = ctx.cpu.r_reg(REG_A1) + fmtTemplate = ctx.cpu.r_reg(REG_A2) + putCharFunc = ctx.cpu.r_reg(REG_A3) + print "not implemented: ParseDate" + + def StrConvert(self, ctx): + locale = ctx.cpu.r_reg(REG_A0) + string = ctx.cpu.r_reg(REG_A1) + buffer = ctx.cpu.r_reg(REG_A2) + bufferSize = ctx.cpu.r_reg(REG_D0) + type = ctx.cpu.r_reg(REG_D1) + print "not implemented: StrConvert" + def StrnCmp(self, ctx): + locale = ctx.cpu.r_reg(REG_A0) + string1 = ctx.cpu.r_reg(REG_A1) + string2 = ctx.cpu.r_reg(REG_A2) + length = ctx.cpu.r_reg(REG_D0) + type = ctx.cpu.r_reg(REG_D1) + print "not implemented: StrnCmp" +""" diff --git a/amitools/vamos/lib/TimerDevice.py b/amitools/vamos/lib/TimerDevice.py index fbfd97d3..24a8d562 100644 --- a/amitools/vamos/lib/TimerDevice.py +++ b/amitools/vamos/lib/TimerDevice.py @@ -1,19 +1,20 @@ from amitools.vamos.libcore import LibImpl -from amitools.vamos.machine.regs import * -from amitools.vamos.astructs import * +from amitools.vamos.machine.regs import REG_A0 +from amitools.vamos.astructs import AccessStruct +from amitools.vamos.libstructs import DateStampStruct from datetime import datetime + class TimerDevice(LibImpl): + def ReadEClock(self, ctx): + eclockval = ctx.cpu.r_reg(REG_A0) + + dt = datetime.now() - def ReadEClock(self, ctx): - eclockval = ctx.cpu.r_reg(REG_A0) - - dt = datetime.now() - # abuse DateStampStruct - tv = AccessStruct(ctx.mem, DateStampStruct, struct_addr = eclockval) - tv.ds_Days = dt.microsecond / 1000000 - tv.ds_Minute = dt.microsecond % 1000000 - - return 50 + tv = AccessStruct(ctx.mem, DateStampStruct, struct_addr=eclockval) + tv.ds_Days = dt.microsecond / 1000000 + tv.ds_Minute = dt.microsecond % 1000000 + + return 50 diff --git a/amitools/vamos/lib/VamosTestLibrary.py b/amitools/vamos/lib/VamosTestLibrary.py index 0dd79cb5..2f169b88 100644 --- a/amitools/vamos/lib/VamosTestLibrary.py +++ b/amitools/vamos/lib/VamosTestLibrary.py @@ -1,6 +1,10 @@ +import code +import os.path + from amitools.vamos.machine.regs import * from amitools.vamos.libcore import LibImpl from amitools.vamos.error import * +from amitools.vamos.astructs import CSTR class VamosTestLibrary(LibImpl): @@ -34,25 +38,20 @@ def PrintHello(self, ctx): print("VamosTest: PrintHello()") return 0 - def PrintString(self, ctx): - str_addr = ctx.cpu.r_reg(REG_A0) - txt = ctx.mem.r_cstr(str_addr) - print("VamosTest: PrintString(%s)", txt) + def PrintString(self, ctx, txt: CSTR): + print("VamosTest: PrintString('%s')" % txt.str) return 0 - def Add(self, ctx): - a = ctx.cpu.r_reg(REG_D0) - b = ctx.cpu.r_reg(REG_D1) + def Add(self, ctx, a, b): + """define input values directly as function arguments""" return a + b - def Swap(self, ctx): - a = ctx.cpu.r_reg(REG_D0) - b = ctx.cpu.r_reg(REG_D1) + def Swap(self, ctx, a, b): + """define input values directly as function arguments""" return b, a - def RaiseError(self, ctx): - str_addr = ctx.cpu.r_reg(REG_A0) - txt = ctx.mem.r_cstr(str_addr) + def RaiseError(self, ctx, txt_ptr: CSTR): + txt = txt_ptr.str if txt == "RuntimeError": e = RuntimeError("VamosTest") elif txt == "VamosInternalError": @@ -64,3 +63,66 @@ def RaiseError(self, ctx): return print("VamosTest: raise", e.__class__.__name__) raise e + + def _ExecutePyUsage(self): + print( + """ExecutePy Usage: +-e '' # return value in d0 +-x '' # return value in 'rc' var +-f '' # return value in 'rc' var +-c '' ' # call function 'func(ctx)' and return value +""" + ) + + def ExecutePy(self, ctx, argc, argv): + """execute python code in the current context""" + # read args + args = [] + for i in range(argc): + ptr = ctx.mem.r32(argv) + txt = ctx.mem.r_cstr(ptr) + args.append(txt) + argv += 4 + # local and global variables + loc = {"rc": 0, "ctx": ctx} + glob = globals() + # mode of operation + if argc == 0: + # nor args - run interactive + code.interact(banner="vamos REPL", exitmsg="back to vamos", local=loc) + rc = loc["rc"] + elif argc < 2: + # invalid usage + self._ExecutePyUsage() + rc = 2 + else: + op = args[0] + val = args[1] + if op == "-e": + # eval string + rc = eval(val, glob, loc) + elif op == "-x": + # exec string + exec(val, glob, loc) + rc = loc["rc"] + elif op == "-f": + # exec script file + with open(val) as fh: + exec(fh.read(), glob, loc) + rc = loc["rc"] + elif op == "-c" and argc > 2: + # exec function(ctx) in file + func_name = args[2] + with open(val) as fh: + exec(fh.read(), glob, loc) + func = loc[func_name] + rc = func(ctx) + else: + self._ExecutePyUsage() + rc = 2 + # check return value + if type(rc) is not int: + print("ExecutePy: invalid return value:", rc) + rc = 3 + # fetch return code + return rc diff --git a/amitools/vamos/lib/dos/Args.py b/amitools/vamos/lib/dos/Args.py index 13b26a7e..1a342657 100644 --- a/amitools/vamos/lib/dos/Args.py +++ b/amitools/vamos/lib/dos/Args.py @@ -117,7 +117,7 @@ def parse_string(template): class ParseResultList: """the class holds the parsing results. each template arg gets assigned - a single item (or not)""" + a single item (or not)""" def __init__(self, targ_list): """create with a template arg list""" @@ -141,11 +141,11 @@ def get_results(self): def calc_extra_result_size(self): """return size of extra result in bytes. - we count the longs and chars that do not fit into the - result long array passed into ReadArgs() + we count the longs and chars that do not fit into the + result long array passed into ReadArgs() - return size in bytes, number of longs - """ + return size in bytes, number of longs + """ num = self.len num_longs = 0 num_chars = 0 @@ -253,8 +253,8 @@ def __init__(self, targ_list): def parse(self, csrc, maxbuf=256): """input is read from csrc - return NO_ERROR or ERROR_* - """ + return NO_ERROR or ERROR_* + """ result_list = ParseResultList(self.targ_list) self.result_list = result_list item_parser = ItemParser(csrc, eol_unget_bug=False) @@ -430,9 +430,9 @@ def __init__(self, csrc): def want_help(self): """check if a line has a '?' as a help request. - if yes return True and consume the whole line from csrc. - otherwise return False and rewind the whole line from csrc. - """ + if yes return True and consume the whole line from csrc. + otherwise return False and rewind the whole line from csrc. + """ escaped = False quoted = False seen_space = True diff --git a/amitools/vamos/lib/dos/CSource.py b/amitools/vamos/lib/dos/CSource.py index 0fcb9612..ccc5ec7e 100644 --- a/amitools/vamos/lib/dos/CSource.py +++ b/amitools/vamos/lib/dos/CSource.py @@ -1,4 +1,4 @@ -from amitools.vamos.astructs import CSourceStruct +from amitools.vamos.libstructs import CSourceStruct class CSource: diff --git a/amitools/vamos/lib/dos/DosList.py b/amitools/vamos/lib/dos/DosList.py index 1136ca11..b62b646d 100644 --- a/amitools/vamos/lib/dos/DosList.py +++ b/amitools/vamos/lib/dos/DosList.py @@ -1,8 +1,7 @@ import logging from amitools.vamos.log import log_doslist -from amitools.vamos.astructs import ( - AccessStruct, - DosListDeviceStruct, +from amitools.vamos.astructs import AccessStruct +from amitools.vamos.libstructs import ( DosListVolumeStruct, DosListAssignStruct, AssignListStruct, diff --git a/amitools/vamos/lib/dos/FileHandle.py b/amitools/vamos/lib/dos/FileHandle.py index de269c83..c321aa56 100644 --- a/amitools/vamos/lib/dos/FileHandle.py +++ b/amitools/vamos/lib/dos/FileHandle.py @@ -1,6 +1,6 @@ import os import sys -from amitools.vamos.astructs import FileHandleStruct +from amitools.vamos.libstructs import FileHandleStruct class FileHandle: diff --git a/amitools/vamos/lib/dos/FileManager.py b/amitools/vamos/lib/dos/FileManager.py index 2cab7af8..0a40dbd9 100644 --- a/amitools/vamos/lib/dos/FileManager.py +++ b/amitools/vamos/lib/dos/FileManager.py @@ -6,7 +6,9 @@ import stat from amitools.vamos.log import log_file -from amitools.vamos.astructs import AccessStruct, MessageStruct, DosPacketStruct +from amitools.vamos.error import UnsupportedFeatureError +from amitools.vamos.astructs import AccessStruct +from amitools.vamos.libstructs import MessageStruct, DosPacketStruct from .Error import * from .DosProtection import DosProtection from .FileHandle import FileHandle @@ -58,7 +60,7 @@ def _create_stdout_fh(self): fileno = fobj.fileno() # create unbuffered raw stream if its a tty if os.isatty(fileno): - fobj = open(fileno, mode, buffering=0) + fobj = open(fileno, "wb", buffering=0) log_file.debug( "open no buffering: fileno=%s -> %s, fileno=%s", fileno, diff --git a/amitools/vamos/lib/dos/Lock.py b/amitools/vamos/lib/dos/Lock.py index e7c6f1fa..e73458d6 100644 --- a/amitools/vamos/lib/dos/Lock.py +++ b/amitools/vamos/lib/dos/Lock.py @@ -4,7 +4,8 @@ from amitools.vamos.log import log_lock -from amitools.vamos.astructs import AccessStruct, FileLockStruct, DateStampStruct +from amitools.vamos.astructs import AccessStruct +from amitools.vamos.libstructs import FileLockStruct, DateStampStruct from .DosProtection import DosProtection from .AmiTime import * from .Error import * @@ -95,12 +96,14 @@ def examine_file(self, fib_mem, name, sys_path): if os.path.isfile(sys_path): size = os.path.getsize(sys_path) # limit to 32bit - if size > 0xffffffff: - size = 0xffffffff + if size > 0xFFFFFFFF: + size = 0xFFFFFFFF fib_mem.w_s("fib_Size", size) blocks = (size + 511) // 512 fib_mem.w_s("fib_NumBlocks", blocks) - log_lock.debug("examine lock: '%s' size=%d, blocks=%d", sys_path, size, blocks) + log_lock.debug( + "examine lock: '%s' size=%d, blocks=%d", sys_path, size, blocks + ) else: fib_mem.w_s("fib_NumBlocks", 1) log_lock.debug("examine lock: '%s' no file", sys_path) diff --git a/amitools/vamos/lib/dos/LockManager.py b/amitools/vamos/lib/dos/LockManager.py index e0968084..6cd95c62 100644 --- a/amitools/vamos/lib/dos/LockManager.py +++ b/amitools/vamos/lib/dos/LockManager.py @@ -2,9 +2,9 @@ import logging from amitools.vamos.log import log_lock -from amitools.vamos.label import LabelRange -from amitools.vamos.error import * -from amitools.vamos.astructs import AccessStruct, DosListVolumeStruct +from amitools.vamos.error import UnsupportedFeatureError, VamosInternalError +from amitools.vamos.astructs import AccessStruct +from amitools.vamos.libstructs import DosListVolumeStruct from .Error import * from .Lock import Lock diff --git a/amitools/vamos/lib/dos/MatchFirstNext.py b/amitools/vamos/lib/dos/MatchFirstNext.py index 8a0cd21e..3bf84a6a 100644 --- a/amitools/vamos/lib/dos/MatchFirstNext.py +++ b/amitools/vamos/lib/dos/MatchFirstNext.py @@ -1,5 +1,5 @@ -from amitools.vamos.astructs import ( - AccessStruct, +from amitools.vamos.astructs import AccessStruct +from amitools.vamos.libstructs import ( AnchorPathStruct, AChainStruct, FileInfoBlockStruct, diff --git a/amitools/vamos/lib/dos/Process.py b/amitools/vamos/lib/dos/Process.py index 795d62b1..8250a987 100644 --- a/amitools/vamos/lib/dos/Process.py +++ b/amitools/vamos/lib/dos/Process.py @@ -1,8 +1,16 @@ -from amitools.vamos.astructs.dos import CLIStruct, DosPacketStruct, ProcessStruct -from amitools.vamos.astructs.exec_ import MessageStruct, MinListStruct +from amitools.vamos.libstructs.dos import CLIStruct, DosPacketStruct, ProcessStruct +from amitools.vamos.libstructs.exec_ import MessageStruct, MinListStruct from amitools.vamos.lib.dos import CommandLine from amitools.vamos.log import log_proc -from amitools.vamos.machine.regs import REG_D0, REG_D1, REG_D2, REG_A0, REG_A2, REG_A5, REG_A6 +from amitools.vamos.machine.regs import ( + REG_D0, + REG_D1, + REG_D2, + REG_A0, + REG_A2, + REG_A5, + REG_A6, +) from amitools.vamos.schedule import Stack, Task import os diff --git a/amitools/vamos/lib/dos/SysArgs.py b/amitools/vamos/lib/dos/SysArgs.py index acc95acd..98ca0500 100644 --- a/amitools/vamos/lib/dos/SysArgs.py +++ b/amitools/vamos/lib/dos/SysArgs.py @@ -3,8 +3,8 @@ def ami_quote_str(chars): """perform Amiga-like shell quoting with surrounding "..." quotes - an special chars quoted with asterisk * - """ + an special chars quoted with asterisk * + """ if chars == "": return '""' res = ['"'] @@ -41,11 +41,11 @@ def sys_arg_to_ami_arg(sys_arg): def sys_args_to_ami_arg_str(sys_args): """convert a regular argv[] array of your host system to a single - string used as an Amiga argument + string used as an Amiga argument - this function handles automatic quoting if necessary and - also appends a final newline - """ + this function handles automatic quoting if necessary and + also appends a final newline + """ ami_args = [] for sys_arg in sys_args: ami_arg = sys_arg_to_ami_arg(sys_arg) diff --git a/amitools/vamos/lib/lexec/Alloc.py b/amitools/vamos/lib/lexec/Alloc.py index efba83ef..384ea33b 100644 --- a/amitools/vamos/lib/lexec/Alloc.py +++ b/amitools/vamos/lib/lexec/Alloc.py @@ -1,7 +1,8 @@ # helper for Allocate()/Deallocate() from amitools.vamos.log import log_exec -from amitools.vamos.astructs import AccessStruct, MemHeaderStruct, MemChunkStruct +from amitools.vamos.astructs import AccessStruct +from amitools.vamos.libstructs import MemHeaderStruct, MemChunkStruct class MemChunk: @@ -73,12 +74,31 @@ def write(self, ctx): mh.w_s("mh_Free", self.free) def read_first(self, ctx): - mc = MemChunk() - mc.read(ctx, self.first) - return mc + if self.first == 0: + return None + else: + mc = MemChunk() + mc.read(ctx, self.first) + return mc + + +def validate(ctx, mh): + log_exec.debug("Header: %s", mh) + sum_free = 0 + mc = mh.read_first(ctx) + num = 0 + while mc: + log_exec.debug("#%d: chunk: %s", num, mc) + sum_free += mc.bytes + mc = mc.read_next(ctx) + num += 1 + # check if free size in header matches sum of chunks + if sum_free != mh.free: + log_exec.error("sum_free=%d != mh.free=%d", sum_free, mh.free) + raise RuntimeError("MH Validation!") -def allocate(ctx, mh_addr, num_bytes): +def allocate(ctx, mh_addr, num_bytes, check=False): # nothing to allocate if num_bytes == 0: return 0 @@ -88,6 +108,8 @@ def allocate(ctx, mh_addr, num_bytes): if ex != 0: num_bytes += 8 - ex + log_exec.debug("ALLOC: mh_addr=%06x, num_bytes=%d", mh_addr, num_bytes) + # read mem header mh = MemHdr() mh.read(ctx, mh_addr) @@ -99,6 +121,9 @@ def allocate(ctx, mh_addr, num_bytes): if mh.first == 0: return 0 + if check: + validate(ctx, mh) + # find chunk with enough free bytes mc_last = None mc = mh.read_first(ctx) @@ -107,7 +132,7 @@ def allocate(ctx, mh_addr, num_bytes): mc_next = mc.read_next(ctx) log_exec.debug("read: %s", mc_next) if mc_next is None: -# log_exec.warning("invalid mem chunk list!") + # no memory found. chunks are too fragmented return 0 mc_last = mc mc = mc_next @@ -142,7 +167,7 @@ def allocate(ctx, mh_addr, num_bytes): return res_addr -def deallocate(ctx, mh_addr, blk_addr, num_bytes): +def deallocate(ctx, mh_addr, blk_addr, num_bytes, check=False): # nothing to allocate if num_bytes == 0 or blk_addr == 0: return 0 @@ -151,6 +176,13 @@ def deallocate(ctx, mh_addr, blk_addr, num_bytes): if ex != 0: num_bytes += 8 - ex + log_exec.debug( + "DEALLOC: mh_addr=%06x, blk_addr=%06x, num_bytes=%d", + mh_addr, + blk_addr, + num_bytes, + ) + # read mem header mh = MemHdr() mh.read(ctx, mh_addr) @@ -160,6 +192,9 @@ def deallocate(ctx, mh_addr, blk_addr, num_bytes): if blk_addr < mh.lower or (blk_addr + num_bytes - 1) > mh.upper: log_exec.error("deallocate: block outside of mem header!") + if check: + validate(ctx, mh) + # no mem chunks? if mh.first == 0: # sanity check @@ -175,12 +210,12 @@ def deallocate(ctx, mh_addr, blk_addr, num_bytes): # find chunk right before/after the returned block mc_last = None mc = mh.read_first(ctx) - log_exec.debug("read: %s", mc) + log_exec.debug("read first: %s", mc) while mc and mc.addr < blk_addr: mc_last = mc mc = mc.read_next(ctx) if mc is not None: - log_exec.debug("read: %s", mc) + log_exec.debug("read next: %s", mc) # now we have either a mc_last and/or mc chunk # check if we can merge with one or both @@ -212,6 +247,9 @@ def deallocate(ctx, mh_addr, blk_addr, num_bytes): if mc_last: mc_last.next = blk_addr mc_last.write(ctx) + else: + # update header + mh.first = mc.addr log_exec.debug("grow cur: %s", mc) # no merging possible -> create a new chunk between last and cur else: @@ -231,3 +269,6 @@ def deallocate(ctx, mh_addr, blk_addr, num_bytes): mh.free += num_bytes mh.write(ctx) log_exec.debug("done: %s", mh) + + if check: + validate(ctx, mh) diff --git a/amitools/vamos/lib/lexec/ExecLibCtx.py b/amitools/vamos/lib/lexec/ExecLibCtx.py index 92da890e..ef6045df 100644 --- a/amitools/vamos/lib/lexec/ExecLibCtx.py +++ b/amitools/vamos/lib/lexec/ExecLibCtx.py @@ -2,7 +2,7 @@ class ExecLibCtx(LibCtx): - def __init__(self, machine, alloc, seg_loader, path_mgr): + def __init__(self, machine, alloc, seg_loader, path_mgr, lib_mgr): LibCtx.__init__(self, machine) self.machine = machine self.traps = machine.get_traps() @@ -13,6 +13,7 @@ def __init__(self, machine, alloc, seg_loader, path_mgr): self.alloc = alloc self.seg_loader = seg_loader self.path_mgr = path_mgr + self.lib_mgr = lib_mgr self.process = None def set_process(self, process): diff --git a/amitools/vamos/lib/lexec/PortManager.py b/amitools/vamos/lib/lexec/PortManager.py index d1420008..3c0f14cf 100644 --- a/amitools/vamos/lib/lexec/PortManager.py +++ b/amitools/vamos/lib/lexec/PortManager.py @@ -1,4 +1,4 @@ -from amitools.vamos.astructs import MsgPortStruct +from amitools.vamos.libstructs import MsgPortStruct from amitools.vamos.error import * diff --git a/amitools/vamos/lib/lexec/SemaphoreManager.py b/amitools/vamos/lib/lexec/SemaphoreManager.py index d1e1e5ef..2635bdbd 100644 --- a/amitools/vamos/lib/lexec/SemaphoreManager.py +++ b/amitools/vamos/lib/lexec/SemaphoreManager.py @@ -1,4 +1,5 @@ -from amitools.vamos.astructs import AccessStruct, SignalSemaphoreStruct +from amitools.vamos.astructs import AccessStruct +from amitools.vamos.libstructs import SignalSemaphoreStruct from amitools.vamos.error import * diff --git a/amitools/vamos/lib/util/AmiDate.py b/amitools/vamos/lib/util/AmiDate.py index 90fb05e1..aae02df8 100644 --- a/amitools/vamos/lib/util/AmiDate.py +++ b/amitools/vamos/lib/util/AmiDate.py @@ -1,6 +1,7 @@ import datetime -from amitools.vamos.astructs import AccessStruct, ClockDataStruct +from amitools.vamos.astructs import AccessStruct +from amitools.vamos.libstructs import ClockDataStruct # when Amiga time began... @@ -22,8 +23,8 @@ def write_clock_data(dt, mem, data_ptr): def read_clock_data(mem, data_ptr): """read Amiga ClockData and return corresponding Python datetime - return None if data is invalid - """ + return None if data is invalid + """ data = AccessStruct(mem, ClockDataStruct, struct_addr=data_ptr) # read date struct sec = data.r_s("sec") diff --git a/amitools/vamos/lib/util/TagList.py b/amitools/vamos/lib/util/TagList.py index 4a57d216..3985987d 100644 --- a/amitools/vamos/lib/util/TagList.py +++ b/amitools/vamos/lib/util/TagList.py @@ -1,4 +1,5 @@ -from amitools.vamos.astructs import AccessStruct, TagItemStruct +from amitools.vamos.astructs import AccessStruct +from amitools.vamos.libstructs import TagItemStruct TAG_DONE = 0 TAG_IGNORE = 1 diff --git a/amitools/vamos/libcore/__init__.py b/amitools/vamos/libcore/__init__.py index 5aeea539..69f84ada 100644 --- a/amitools/vamos/libcore/__init__.py +++ b/amitools/vamos/libcore/__init__.py @@ -1,7 +1,8 @@ -from .impl import LibImpl, LibImplScanner, LibImplScan +from .impl import LibImpl, LibImplScanner, LibImplScan, LibImplFunc, LibImplFuncArg from .registry import LibRegistry -from .ctx import LibCtx, LibCtxMap +from .ctx import LibCtx from .stub import LibStub, LibStubGen +from .proxy import LibProxy, LibProxyGen from .profile import LibFuncProfileData, LibProfileData, LibProfiler from .jumptab import LibJumpTable, NoJumpTableEntryError from .patch import LibPatcherMultiTrap diff --git a/amitools/vamos/libcore/create.py b/amitools/vamos/libcore/create.py index 8fe4c1d5..5ef3872f 100644 --- a/amitools/vamos/libcore/create.py +++ b/amitools/vamos/libcore/create.py @@ -1,5 +1,5 @@ -from amitools.vamos.astructs import LibraryStruct -from amitools.vamos.atypes import Library, NodeType +from amitools.vamos.libstructs import LibraryStruct, NodeType +from amitools.vamos.libtypes import Library from amitools.fd import read_lib_fd, generate_fd from .vlib import VLib from .stub import LibStubGen @@ -35,10 +35,17 @@ def _create_library(self, info, is_dev, fd): id_str = info.get_id_string() neg_size = info.get_neg_size() pos_size = info.get_pos_size() - library = Library.alloc(self.alloc, name, id_str, neg_size, pos_size, fd) + library = Library.alloc( + self.alloc, + name=name, + id_string=id_str, + neg_size=neg_size, + pos_size=pos_size, + fd=fd, + ) version = info.get_version() revision = info.get_revision() - library.setup(version=version, revision=revision, type=ltype) + library.new_lib(version=version, revision=revision, type=ltype) return library def _generate_fake_fd(self, name, lib_cfg): @@ -75,10 +82,10 @@ def create_lib(self, info, ctx, impl=None, lib_cfg=None, check=False): if self.profiler: # get some impl information if scan: - func_tags = scan.get_func_tags() + impl_funcs = scan.get_all_funcs() else: - func_tags = None - profile = self.profiler.create_profile(name, fd, func_tags) + impl_funcs = None + profile = self.profiler.create_profile(name, fd, impl_funcs) else: profile = None # create stub diff --git a/amitools/vamos/libcore/ctx.py b/amitools/vamos/libcore/ctx.py index eeeca9c3..030d84ac 100644 --- a/amitools/vamos/libcore/ctx.py +++ b/amitools/vamos/libcore/ctx.py @@ -7,24 +7,6 @@ def __init__(self, machine): self.mem = machine.get_mem() # will be set on creation self.vlib = None - self.lib_mgr = None def __str__(self): return "[LibCtx:cpu=%s,mem=%s]" % (self.cpu, self.mem) - - -class LibCtxMap(object): - """register non-default context objects for libraries""" - - def __init__(self, machine): - self.ctx_map = {} - self.machine = machine - - def add_ctx(self, name, ctx): - self.ctx_map[name] = ctx - - def get_ctx(self, name): - if name in self.ctx_map: - return self.ctx_map[name] - # return a new default context - return LibCtx(self.machine) diff --git a/amitools/vamos/libcore/impl.py b/amitools/vamos/libcore/impl.py index 1bf3d4a0..7dcf5aac 100644 --- a/amitools/vamos/libcore/impl.py +++ b/amitools/vamos/libcore/impl.py @@ -1,6 +1,8 @@ import inspect -from amitools.vamos.astructs import LibraryStruct +import collections +from amitools.vamos.libstructs import LibraryStruct from amitools.vamos.error import VamosInternalError +from amitools.vamos.machine import str_to_reg_map class LibImpl(object): @@ -26,10 +28,19 @@ def close_lib(self, ctx, open_cnt): pass +LibImplFunc = collections.namedtuple( + "LibImplFunc", + ("name", "fd_func", "tag", "method", "extra_args"), +) + + +LibImplFuncArg = collections.namedtuple("LibImplFuncArg", ("name", "reg", "type")) + + class LibImplScan(object): """scan result of a vamos library implementation - it contains extracted function lists""" + it contains extracted function lists""" TAG_VALID = "valid" TAG_ERROR = "error" @@ -40,11 +51,11 @@ def __init__(self, name, impl, fd): self.name = name self.impl = impl self.fd = fd + self.all_funcs = {} self.valid_funcs = {} self.missing_funcs = {} self.invalid_funcs = {} self.error_funcs = {} - self.func_tags = {} def get_name(self): return self.name @@ -56,21 +67,25 @@ def get_fd(self): return self.fd def get_valid_funcs(self): - """return map: name -> (fd_func, method)""" + """return map: name -> LibImplFunc""" return self.valid_funcs def get_missing_funcs(self): - """return map: name -> fd_func""" + """return map: name -> LibImplFunc""" return self.missing_funcs def get_invalid_funcs(self): - """return map: name -> method""" + """return map: name -> LibImplFunc""" return self.invalid_funcs def get_error_funcs(self): - """return map: name -> (fd_func, method)""" + """return map: name -> LibImplFunc""" return self.error_funcs + def get_all_funcs(self): + """return map: name -> LibImplFunc""" + return self.all_funcs + def get_valid_func_names(self): return sorted(self.valid_funcs.keys()) @@ -95,50 +110,58 @@ def get_num_invalid_funcs(self): def get_num_error_funcs(self): return len(self.error_funcs) - def get_func_tags(self): - """return a map of name -> tag""" - return self.func_tags + def get_func_by_name(self, name): + return self.all_funcs[name] class LibImplScanner(object): """scan an implementation of a library for functions""" - def scan(self, name, impl, fd, inc_std_funcs=False): - """scan a library implementation with a functable""" - res = LibImplScan(name, impl, fd) + def scan(self, lib_name, impl, fd_lib, inc_std_funcs=False): + """scan a library implementation and check if functions match the FD""" + res = LibImplScan(lib_name, impl, fd_lib) found_names = [] + # scan methods of the impl class members = inspect.getmembers(impl, predicate=inspect.ismethod) for name, method in members: tag = None # is a func in the fd? - if fd.has_func(name): - func = fd.get_func_by_name(name) - if self._check_argspec(method): - res.valid_funcs[name] = (func, method) - tag = LibImplScan.TAG_VALID - else: - res.error_funcs[name] = (func, method) - tag = LibImplScan.TAG_ERROR + if fd_lib.has_func(name): + fd_func = fd_lib.get_func_by_name(name) + impl_func = self._gen_impl_func(fd_func, method) + res.all_funcs[name] = impl_func found_names.append(name) + if impl_func.tag == LibImplScan.TAG_VALID: + res.valid_funcs[name] = impl_func + else: + res.error_funcs[name] = impl_func # not a func name else: # if name is camel case then it is invalid if name[0].isupper(): - res.invalid_funcs[name] = method - tag = LibImplScan.TAG_INVALID - # store func tag - if tag: - res.func_tags[name] = tag + impl_func = LibImplFunc( + name, + None, + LibImplScan.TAG_INVALID, + method, + None, + ) + res.invalid_funcs[name] = impl_func + res.all_funcs[name] = impl_func # now check for missing functions - funcs = fd.get_funcs() + funcs = fd_lib.get_funcs() if len(funcs) != len(found_names): - for func in funcs: + for fd_func in funcs: # skip std functions - if inc_std_funcs or not func.is_std(): - name = func.get_name() + if inc_std_funcs or not fd_func.is_std(): + name = fd_func.get_name() if name not in found_names: - res.missing_funcs[name] = func - res.func_tags[name] = LibImplScan.TAG_MISSING + impl_func = LibImplFunc( + name, fd_func, LibImplScan.TAG_MISSING, None, None + ) + res.missing_funcs[name] = impl_func + res.all_funcs[name] = impl_func + # return scan result return res @@ -160,12 +183,60 @@ def scan_checked(self, name, impl, fd, inc_std_funcs=False, ignore_invalid=False "'%s' impl has %d error funcs: %s" % (name, num_error, txt) ) - def _check_argspec(self, method): + def _gen_extra_args(self, more_args, fd_func, anno): + """extract more args of func that are suitable to be mapped to regs""" + extra_args = [] + fd_args = fd_func.get_args() + num_fd_args = len(fd_args) + if len(more_args) != num_fd_args: + return None + # make sure arg names match + for i in range(num_fd_args): + fd_arg_name = fd_args[i][0] + impl_arg_name = more_args[i] + # impl arg must have the fd arg as a prefix + # if not impl_arg_name.startswith(fd_arg_name): + # return None + # find CPU register + reg_str = "REG_" + fd_args[i][1].upper() + reg = str_to_reg_map[reg_str] + # find type + py_type = int + if impl_arg_name in anno: + py_type = anno[impl_arg_name] + arg = LibImplFuncArg(fd_arg_name, reg, py_type) + extra_args.append(arg) + return extra_args + + def _gen_impl_func(self, fd_func, method): + """describe the impl func""" + # prepare impl_func + func_name = fd_func.get_name() + tag = LibImplScan.TAG_ERROR + impl_func = LibImplFunc(func_name, fd_func, tag, method, None) + # inspect method fas = inspect.getfullargspec(method) if fas.varargs is not None: - return False + return impl_func if fas.varkw is not None: - return False + return impl_func if fas.defaults is not None: - return False - return fas.args == ["self", "ctx"] + return impl_func + # args must begin with "self" and "ctx" + num_args = len(fas.args) + if num_args < 2: + return impl_func + if fas.args[:2] != ["self", "ctx"]: + return impl_func + # extra args? must be function arguments + if num_args > 2: + anno = fas.annotations + extra_args = self._gen_extra_args(fas.args[2:], fd_func, anno) + if not extra_args: + return impl_func + else: + extra_args = None + # impl_func is valid + return LibImplFunc( + func_name, fd_func, LibImplScan.TAG_VALID, method, extra_args + ) diff --git a/amitools/vamos/libcore/mgr.py b/amitools/vamos/libcore/mgr.py index d36e855f..b3c9ab94 100644 --- a/amitools/vamos/libcore/mgr.py +++ b/amitools/vamos/libcore/mgr.py @@ -4,11 +4,11 @@ from amitools.vamos.libcore import ( LibCreator, LibInfo, + LibCtx, LibRegistry, - LibCtxMap, LibProfiler, ) -from amitools.vamos.atypes import ExecLibrary +from amitools.vamos.libtypes import ExecLibrary class VLibManager(object): @@ -21,7 +21,7 @@ def __init__( self.mem = machine.get_mem() self.alloc = alloc self.lib_reg = LibRegistry() - self.ctx_map = LibCtxMap(machine) + self.ctx_map = {} self.lib_profiler = LibProfiler(prof_names, prof_calls) if main_profiler: main_profiler.add_profiler(self.lib_profiler) @@ -52,7 +52,7 @@ def add_impl_cls(self, name, impl_cls): self.lib_reg.add_lib_impl(name, impl_cls) def add_ctx(self, name, ctx): - self.ctx_map.add_ctx(name, ctx) + self.ctx_map[name] = ctx def bootstrap_exec(self, exec_info=None, version=0, revision=0): """setup exec library""" @@ -92,8 +92,8 @@ def get_profiler(self): def shutdown(self): """cleanup libs - try to expunge all libs and report still open ones - """ + try to expunge all libs and report still open ones + """ log_libmgr.info("[vamos] +shutdown") # dec exec's open cnt self.exec_lib.lib_node.dec_open_cnt() @@ -108,15 +108,15 @@ def shutdown(self): def expunge_libs(self): """expunge all unused vlibs - return number of libs _not_ expunged - """ + return number of libs _not_ expunged + """ return self._expunge_list(self.exec_lib.lib_list) def expunge_devs(self): """expunge all unused vlibs - return number of libs _not_ expunged - """ + return number of libs _not_ expunged + """ return self._expunge_list(self.exec_lib.device_list) def _expunge_list(self, node_list): @@ -129,7 +129,9 @@ def _expunge_list(self, node_list): if not self.expunge_lib(vlib): lib = vlib.get_library() log_libmgr.warning( - "can't expunge: '%s' with open count %d", lib.name, lib.open_cnt + "can't expunge: '%s' with open count %d", + lib.name, + lib.open_cnt.val, ) left_libs += 1 return left_libs @@ -138,7 +140,7 @@ def expunge_lib(self, vlib): """expunge a vlib""" lib = vlib.get_library() # still open? - if lib.open_cnt > 0: + if lib.open_cnt.val > 0: return False # vlib? self._rem_vlib(vlib) @@ -178,7 +180,11 @@ def close_lib(self, vlib): def _create_vlib(self, lib_info, fake, lib_cfg=None): # get lib ctx name = lib_info.get_name() - ctx = self.ctx_map.get_ctx(name) + ctx = self.ctx_map.get(name, None) + # create new context? + if not ctx: + ctx = LibCtx(self.machine) + self.ctx_map[name] = ctx # get impl if fake: impl = None diff --git a/amitools/vamos/libcore/profile.py b/amitools/vamos/libcore/profile.py index 8b26f467..c8f67e93 100644 --- a/amitools/vamos/libcore/profile.py +++ b/amitools/vamos/libcore/profile.py @@ -91,16 +91,13 @@ def get_tag(self): return self.tag def __repr__(self): - return ( - "LibProfileFuncData(func_id=%r,add_samples=%r):num=%r,sum=%r,deltas=%r,tag=%r" - % ( - self.func_id, - self.add_samples, - self.num, - self.sum, - self.deltas, - self.tag, - ) + return "LibProfileFuncData(func_id=%r,add_samples=%r):num=%r,sum=%r,deltas=%r,tag=%r" % ( + self.func_id, + self.add_samples, + self.num, + self.sum, + self.deltas, + self.tag, ) def dump(self, name): @@ -164,7 +161,7 @@ def get_data(self): funcs[name] = data return res - def setup_func_table(self, fd, func_tags=None): + def setup_func_table(self, fd, impl_funcs=None): self.fd = fd num_func = fd.get_num_indices() self.func_table = [] @@ -178,8 +175,8 @@ def setup_func_table(self, fd, func_tags=None): func = LibFuncProfileData(idx, self.add_samples) self.func_map[name] = func # add tag? - if func_tags and name in func_tags: - tag = func_tags[name] + if impl_funcs and name in impl_funcs: + tag = impl_funcs[name].tag func.set_tag(tag) else: func = None @@ -300,7 +297,7 @@ def shutdown(self): if num_libs == 0: log_prof.warning("profiling enabled but no lib profiles found!") - def create_profile(self, lib_name, fd, func_tags=None): + def create_profile(self, lib_name, fd, impl_funcs=None): """get or create a new profile for a library""" # profiling disabled if not self.enabled: @@ -310,7 +307,7 @@ def create_profile(self, lib_name, fd, func_tags=None): if lib_name in self.lib_profiles: log_prof.debug("libs: create '%s' -> reuse", lib_name) prof = self.lib_profiles[lib_name] - prof.setup_func_table(fd, func_tags) + prof.setup_func_table(fd, impl_funcs) return prof elif self.add_all or lib_name in self.names: # shall we create a profile for this lib? diff --git a/amitools/vamos/libcore/proxy.py b/amitools/vamos/libcore/proxy.py new file mode 100644 index 00000000..846c8888 --- /dev/null +++ b/amitools/vamos/libcore/proxy.py @@ -0,0 +1,115 @@ +from amitools.vamos.machine.regs import REG_D0, REG_D1 + + +class LibProxy: + """A lib proxy offers the functions of a library as descibed in + a fd file. + + With a proxy you can call library functions directly via Python. + A native library is called via CPU emulation while a Python library + is called directly. + """ + + def __init__(self, ctx, base_addr=None, run_sp=None): + self.ctx = ctx + self.base_addr = base_addr + self.run_sp = run_sp + + +class LibProxyGen: + """Generate a new type derived from LibProxy holding all functions""" + + def _gen_arg_regs(self, func_def): + arg_regs = [] + fd_args = func_def.get_args() + if fd_args: + for arg_name, arg_reg in fd_args: + reg_num = int(arg_reg[1]) + if arg_reg[0] == "a": + reg_num += 8 + arg_regs.append(reg_num) + return arg_regs + + def _gen_stub_call(self, arg_regs, stub_method): + def stub_call(self, *args, **kwargs): + """proxy function to call lib stub directly""" + # fill registers with arg values + for reg, val in zip(arg_regs, args): + self.ctx.cpu.w_reg(reg, val) + + # shall we return d1 as well? + ret_d1 = kwargs.pop("ret_d1", False) + + # perform call at stub + stub_method(**kwargs) + + # prepare return value + d0 = self.ctx.cpu.r_reg(REG_D0) + if ret_d1: + d1 = self.ctx.cpu.r_reg(REG_D1) + return (d0, d1) + else: + return d0 + + return stub_call + + def _gen_lib_call(self, arg_regs, bias, name=None): + def lib_call(self, *args, **kwargs): + reg_map = {} + for reg, val in zip(arg_regs, args): + reg_map[reg] = val + + ret_regs = [REG_D0] + # shall we return d1 as well? + ret_d1 = kwargs.pop("ret_d1", False) + if ret_d1: + ret_regs.append(REG_D1) + + jump_addr = self.base_addr - bias + + # perform native run + res = self.ctx.machine.run( + jump_addr, + sp=self.run_sp, + set_regs=reg_map, + get_regs=ret_regs, + name=name, + ) + + if ret_d1: + return res.regs[REG_D0], res.regs[REG_D1] + else: + return res.regs[REG_D0] + + return lib_call + + def gen_proxy_for_stub(self, proxy_name, lib_fd, stub): + method_dict = {} + for func_def in lib_fd.get_funcs(): + # prepare reg arg list + arg_regs = self._gen_arg_regs(func_def) + func_name = func_def.get_name() + + # lookup func in stub + stub_method = getattr(stub, func_name, None) + if stub_method: + proxy_call = self._gen_stub_call(arg_regs, stub_method) + method_dict[func_name] = proxy_call + + # create new type + return type(proxy_name, (LibProxy,), method_dict) + + def gen_proxy_for_libcall(self, proxy_name, lib_fd): + method_dict = {} + for func_def in lib_fd.get_funcs(): + # prepare reg arg list + arg_regs = self._gen_arg_regs(func_def) + func_name = func_def.get_name() + func_bias = func_def.get_bias() + + # lookup func in stub + lib_call = self._gen_lib_call(arg_regs, func_bias, name=func_name) + method_dict[func_name] = lib_call + + # create new type + return type(proxy_name, (LibProxy,), method_dict) diff --git a/amitools/vamos/libcore/stub.py b/amitools/vamos/libcore/stub.py index 47e50443..a734e612 100644 --- a/amitools/vamos/libcore/stub.py +++ b/amitools/vamos/libcore/stub.py @@ -7,17 +7,17 @@ class LibStub(object): """a lib stub is a frontend object instance that wraps all calls into - a library implementation. + a library implementation. - It is suitable for binding these functions to traps of the machine - emulation. + It is suitable for binding these functions to traps of the machine + emulation. - A wrapped call return value processing or optional profiling features. - """ + A wrapped call return value processing or optional profiling features. + """ def __init__(self, name, fd, impl=None, profile=None): """create a stub for a given implementation and - associated fd description""" + associated fd description""" self.name = name self.impl = impl self.fd = fd @@ -58,7 +58,7 @@ def _handle_exc(self): class LibStubGen(object): """the lib stub generator scans a lib impl and creates stubs for all - methods found there""" + methods found there""" def __init__(self, log_missing=None, log_valid=None, ignore_invalid=True): self.log_missing = log_missing @@ -67,8 +67,8 @@ def __init__(self, log_missing=None, log_valid=None, ignore_invalid=True): def gen_fake_stub(self, name, fd, ctx, profile=None): """a fake stub exists without an implementation and only contains - "missing" functions - """ + "missing" functions + """ # create stub object stub = LibStub(name, fd, profile=profile) @@ -88,13 +88,15 @@ def gen_stub(self, impl_scan, ctx, profile=None): # generate valid funcs valid_funcs = list(impl_scan.get_valid_funcs().values()) - for fd_func, impl_method in valid_funcs: - stub_func = self.wrap_func(fd_func, impl_method, ctx, profile) + for impl_func in valid_funcs: + fd_func = impl_func.fd_func + stub_func = self.wrap_func(impl_func, ctx, profile) self._set_method(fd_func, stub, stub_func) # generate missing funcs missing_funcs = list(impl_scan.get_missing_funcs().values()) - for fd_func in missing_funcs: + for impl_func in missing_funcs: + fd_func = impl_func.fd_func stub_func = self.wrap_missing_func(fd_func, ctx, profile) self._set_method(fd_func, stub, stub_func) @@ -112,8 +114,8 @@ def _set_method(self, fd_func, stub, stub_func): def wrap_missing_func(self, fd_func, ctx, profile): """create a stub func for a missing function in impl - returns an unbound method for the stub instance - """ + returns an unbound method for the stub instance + """ log = self.log_missing if log is None: # without tracing @@ -154,15 +156,13 @@ def profile_func(this, *args, **kwargs): # return created func return func - def wrap_func(self, fd_func, impl_method, ctx, profile): - """create a stub func for a valid impl func - returns an unbound method for the stub instaance - """ + def _gen_base_func(self, method, ctx): + """generate a function that calls the method with the ctx.""" def base_func(this, *args, **kwargs): """the base function to call the impl, - set return vals, and catch exceptions""" - res = impl_method(ctx) + set return vals, and catch exceptions""" + res = method(ctx) if res is not None: if type(res) in (list, tuple): ctx.cpu.w_reg(REG_D0, res[0] & 0xFFFFFFFF) @@ -171,52 +171,96 @@ def base_func(this, *args, **kwargs): ctx.cpu.w_reg(REG_D0, res & 0xFFFFFFFF) return res - func = base_func + return base_func - # wrap around logging method? - log = self.log_valid - if log: - name = fd_func.get_name() - func_args = fd_func.get_args() - bias = fd_func.get_bias() + def _gen_base_extra_args_func(self, method, ctx, extra_args): + """generate a function that fills arguments from registers.""" - def log_func(this, *args, **kwargs): - callee_pc = this._get_callee_pc(ctx) - call_info = "%4d %s( %s ) from PC=%06x" % ( - bias, - name, - this._gen_arg_dump(func_args, ctx), - callee_pc, - ) - log.info("{ CALL: %s" % call_info) - res = base_func(this, *args, **kwargs) - if res is not None: - if type(res) in (list, tuple): - res_str = "d0=%08x, d1=%08x" % tuple(map(int, res)) - else: - res_str = "d0=%08x" % int(res) + def base_func(this, *args, **kwargs): + """the base function to call the impl, + set return vals, and catch exceptions""" + args = [] + for arg in extra_args: + arg_val = ctx.cpu.r_reg(arg.reg) + arg_type = arg.type + # int: keep value + if arg_type is not int: + # bind to type + arg_val = arg_type(cpu=ctx.cpu, reg=arg.reg, mem=ctx.mem) + args.append(arg_val) + res = method(ctx, *args) + if res is not None: + if type(res) in (list, tuple): + ctx.cpu.w_reg(REG_D0, res[0] & 0xFFFFFFFF) + ctx.cpu.w_reg(REG_D1, res[1] & 0xFFFFFFFF) else: - res_str = "n/a" - log.info("} CALL: -> %s" % res_str) - - func = log_func + ctx.cpu.w_reg(REG_D0, res & 0xFFFFFFFF) + return res - # wrap profiling? - if profile: - index = fd_func.get_index() - prof = profile.get_func_by_index(index) - if log: - call = log_func + return base_func + + def _gen_log_func(selgf, fd_func, base_func, ctx, log): + """wrap the base function with logging.""" + name = fd_func.get_name() + func_args = fd_func.get_args() + bias = fd_func.get_bias() + + def log_func(this, *args, **kwargs): + callee_pc = this._get_callee_pc(ctx) + call_info = "%4d %s( %s ) from PC=%06x" % ( + bias, + name, + this._gen_arg_dump(func_args, ctx), + callee_pc, + ) + log.info("{ CALL: %s" % call_info) + res = base_func(this, *args, **kwargs) + if res is not None: + if type(res) in (list, tuple): + res_str = "d0=%08x, d1=%08x" % tuple(map(int, res)) + else: + res_str = "d0=%08x" % int(res) else: - call = base_func + res_str = "n/a" + log.info("} CALL: -> %s" % res_str) - def profile_func(this, *args, **kwargs): - start = time.perf_counter() - call(this, *args, **kwargs) - end = time.perf_counter() - delta = end - start - prof.count(delta) + return log_func - func = profile_func + def _gen_profile_func(self, fd_func, profile, func): + """wrap profiling around func""" + index = fd_func.get_index() + prof = profile.get_func_by_index(index) + + def profile_func(this, *args, **kwargs): + start = time.perf_counter() + func(this, *args, **kwargs) + end = time.perf_counter() + delta = end - start + prof.count(delta) + + return profile_func + + def wrap_func(self, impl_func, ctx, profile): + """create a stub func for a valid impl func + returns an unbound method for the stub instaance + """ + fd_func = impl_func.fd_func + + # do we need to read some registers into extra args? + method = impl_func.method + extra_args = impl_func.extra_args + if extra_args: + func = self._gen_base_extra_args_func(method, ctx, extra_args) + else: + func = self._gen_base_func(method, ctx) + + # wrap around logging method? + log = self.log_valid + if log: + func = self._gen_log_func(fd_func, func, ctx, log) + + # wrap profiling? + if profile: + func = self._gen_profile_func(fd_func, profile, func) return func diff --git a/amitools/vamos/libcore/vlib.py b/amitools/vamos/libcore/vlib.py index 7a25badf..c83bd619 100644 --- a/amitools/vamos/libcore/vlib.py +++ b/amitools/vamos/libcore/vlib.py @@ -1,8 +1,8 @@ class VLib(object): """a vamos interal lib. - A vamos internal lib has a stub, a impl and allocator and patcher - """ + A vamos internal lib has a stub, a impl and allocator and patcher + """ def __init__( self, @@ -51,7 +51,7 @@ def get_ctx(self): return self.ctx def get_patcher(self): - return self.neg_size + return self.patcher def get_profile(self): return self.profile @@ -71,7 +71,7 @@ def _setup(self): def free(self): # check open cnt - oc = self.library.open_cnt + oc = self.library.open_cnt.val if oc > 0: raise RuntimeError("vlib.free(): has open_cnt: %d" % oc) # call cleanup func in impl @@ -92,14 +92,14 @@ def open(self): lib.inc_open_cnt() # report open to impl if self.impl: - self.impl.open_lib(self.ctx, lib.open_cnt) + self.impl.open_lib(self.ctx, lib.open_cnt.val) def close(self): lib = self.library lib.dec_open_cnt() - oc = lib.open_cnt + oc = lib.open_cnt.val if oc < 0: raise ValueError("vlib.close(): open_cnt < 0: %d" % oc) # report close to impl if self.impl: - self.impl.close_lib(self.ctx, lib.open_cnt) + self.impl.close_lib(self.ctx, lib.open_cnt.val) diff --git a/amitools/vamos/libmgr/__init__.py b/amitools/vamos/libmgr/__init__.py index d4ba38bf..0320974a 100644 --- a/amitools/vamos/libmgr/__init__.py +++ b/amitools/vamos/libmgr/__init__.py @@ -1,3 +1,4 @@ from .mgr import LibManager from .cfg import LibCfg, LibMgrCfg from .setup import SetupLibManager +from .proxy import LibProxyManager diff --git a/amitools/vamos/libmgr/mgr.py b/amitools/vamos/libmgr/mgr.py index f7a7a355..3d35e48b 100644 --- a/amitools/vamos/libmgr/mgr.py +++ b/amitools/vamos/libmgr/mgr.py @@ -1,7 +1,5 @@ -import datetime -import logging from amitools.vamos.log import log_libmgr -from amitools.vamos.atypes import Library +from amitools.vamos.libtypes import Library from amitools.vamos.libcore import VLibManager from amitools.vamos.libnative import ALibManager, LibLoader from .cfg import LibCfg @@ -13,13 +11,13 @@ class LibManager(object): def __init__(self, machine, alloc, segloader, cfg, main_profiler=None): self.mem = machine.get_mem() self.cfg = cfg + self.machine = machine self.vlib_mgr = VLibManager(machine, alloc, main_profiler=main_profiler) self.alib_mgr = ALibManager(machine, alloc, segloader) cfg.dump(log_libmgr.info) def add_ctx(self, name, ctx): """allow to add vlib contexts""" - ctx.lib_mgr = self self.vlib_mgr.add_ctx(name, ctx) def add_impl_cls(self, name, impl_cls): @@ -34,6 +32,10 @@ def get_vlib_by_name(self, name): """return associated vlib for a name""" return self.vlib_mgr.get_vlib_by_name(name) + def get_alib_info_by_addr(self, addr): + """return associated alib info for a base lib address""" + return self.alib_mgr.is_base_addr(addr) + def bootstrap_exec(self, exec_info=None): """setup exec vlib as first and essential lib""" version = 0 @@ -47,8 +49,8 @@ def bootstrap_exec(self, exec_info=None): def shutdown(self, run_sp=None): """cleanup libs - try to expunge all libs and report still open ones - """ + try to expunge all libs and report still open ones + """ log_libmgr.info("+shutdown") aleft = self.alib_mgr.shutdown(run_sp) if aleft > 0: @@ -63,8 +65,8 @@ def shutdown(self, run_sp=None): def expunge_libs(self, run_sp=None): """expunge all unused libs - return number of libs _not_ expunged - """ + return number of libs _not_ expunged + """ log_libmgr.info("+expunge_libs") aleft = self.alib_mgr.expunge_libs(run_sp) vleft = self.vlib_mgr.expunge_libs() @@ -74,8 +76,8 @@ def expunge_libs(self, run_sp=None): def expunge_devs(self, run_sp=None): """expunge all unused devs - return number of libs _not_ expunged - """ + return number of libs _not_ expunged + """ log_libmgr.info("+expunge_devs") aleft = 0 # TBD vleft = self.vlib_mgr.expunge_devs() @@ -85,8 +87,8 @@ def expunge_devs(self, run_sp=None): def expunge_lib(self, addr, run_sp=None): """expunge a library given by base address - return True if lib was expunged - """ + return True if lib was expunged + """ log_libmgr.info("expunge_lib: @%06x", addr) vlib = self.vlib_mgr.get_vlib_by_addr(addr) if vlib: @@ -100,8 +102,8 @@ def expunge_lib(self, addr, run_sp=None): def close_lib(self, addr, run_sp=None): """close a library - return True if lib was expunged, too - """ + return True if lib was expunged, too + """ log_libmgr.info("close_lib: @%06x", addr) vlib = self.vlib_mgr.get_vlib_by_addr(addr) if vlib: @@ -117,8 +119,8 @@ def open_lib( ): """open a library - return lib_base addr or 0 - """ + return lib_base addr or 0 + """ # get base name base_name = LibLoader.get_lib_base_name(full_name) log_libmgr.info( @@ -196,7 +198,7 @@ def _map_mode(self, mode): def _check_version(self, name, addr, open_ver): # check version lib = Library(self.mem, addr) - lib_ver = lib.version + lib_ver = lib.version.val if lib_ver < open_ver: log_libmgr.warning( "lib '%s' has too low version: %d < %d", name, lib_ver, open_ver diff --git a/amitools/vamos/libmgr/proxy.py b/amitools/vamos/libmgr/proxy.py new file mode 100644 index 00000000..41ee2013 --- /dev/null +++ b/amitools/vamos/libmgr/proxy.py @@ -0,0 +1,86 @@ +from amitools.vamos.log import log_libmgr +from amitools.vamos.libcore import LibProxyGen, LibCtx +from amitools.vamos.error import VamosInternalError + + +class LibProxyManager: + def __init__(self, lib_mgr): + self.lib_mgr = lib_mgr + self.proxy_cache = {} + self.proxy_gen = LibProxyGen() + + def open_lib_proxy(self, full_name, version=0, run_sp=None): + """Try to open library (vlib or alib) and return a python call proxy""" + # try to open lib + base_addr = self.lib_mgr.open_lib(full_name, version=version, run_sp=run_sp) + if base_addr == 0: + log_libmgr.warning("proxy: open_lib '%s' failed!", full_name) + return None + # is a vlib? + vlib = self.lib_mgr.get_vlib_by_addr(base_addr) + if vlib: + return self._setup_stub_proxy(vlib, base_addr) + else: + # is an alib? + ainfo = self.lib_mgr.get_alib_info_by_addr(base_addr) + if ainfo: + fd = ainfo.get_lib_fd() + name = ainfo.get_name() + # if an fd is available then we could create a proxy + if fd: + return self._setup_libcall_proxy(name, fd, base_addr, run_sp) + else: + log_libmgr.warning("proxy: no FD for '%s'", full_name) + return None + else: + raise VamosInternalError("Neither vlib nor alib?!") + + def close_lib_proxy(self, proxy, run_sp=None): + """Close the library assoicated with proxy and invalidate it.""" + base_addr = proxy.base_addr + self.lib_mgr.close_lib(base_addr, run_sp=run_sp) + proxy.ctx = None + proxy.base_addr = None + + def _setup_stub_proxy(self, vlib, base_addr): + name = vlib.info.name + type_name = self._get_proxy_type_name(name) + # cached proxy? + proxy_type = self.proxy_cache.get(type_name, None) + if not proxy_type: + log_libmgr.info("proxy: create stub type '%s'", type_name) + # no, create it + proxy_type = self.proxy_gen.gen_proxy_for_stub( + type_name, vlib.fd, vlib.stub + ) + self.proxy_cache[type_name] = proxy_type + # create instance + log_libmgr.info("proxy: create stub proxy %s@%08x", type_name, base_addr) + return proxy_type(vlib.ctx, base_addr) + + def _setup_libcall_proxy(self, name, fd, base_addr, run_sp): + type_name = self._get_proxy_type_name(name) + # cached proxy? + proxy_type = self.proxy_cache.get(type_name, None) + if not proxy_type: + log_libmgr.info("proxy: create libcall type '%s'", type_name) + # no, create it + proxy_type = self.proxy_gen.gen_proxy_for_libcall(type_name, fd) + self.proxy_cache[type_name] = proxy_type + # create instance + ctx = LibCtx(self.lib_mgr.machine) + log_libmgr.info("proxy: create libcall proxy %s@%08x", type_name, base_addr) + return proxy_type(ctx, base_addr, run_sp=run_sp) + + def _get_proxy_type_name(self, lib_name): + """Normalize library name and strip path and extension""" + pos = lib_name.find(":") + if pos != -1: + lib_name = lib_name[pos:] + pos = lib_name.find("/") + if pos != -1: + lib_name = lib_name[pos:] + pos = lib_name.find(".") + if pos != -1: + lib_name = lib_name[:pos] + return lib_name diff --git a/amitools/vamos/libmgr/setup.py b/amitools/vamos/libmgr/setup.py index c1b11e78..3aaf28d6 100644 --- a/amitools/vamos/libmgr/setup.py +++ b/amitools/vamos/libmgr/setup.py @@ -38,8 +38,17 @@ def setup(self): self.seg_loader = SegmentLoader(self.alloc, self.path_mgr) # setup contexts odg_base = self.mem_map.get_old_dos_guard_base() + # create lib mgr + self.lib_mgr = LibManager( + self.machine, + self.alloc, + self.seg_loader, + self.lib_mgr_cfg, + main_profiler=self.main_profiler, + ) + # setup special lib contexts for exec and dos self.exec_ctx = ExecLibCtx( - self.machine, self.alloc, self.seg_loader, self.path_mgr + self.machine, self.alloc, self.seg_loader, self.path_mgr, self.lib_mgr ) self.dos_ctx = DosLibCtx( self.machine, @@ -49,14 +58,6 @@ def setup(self): self.scheduler, odg_base, ) - # create lib mgr - self.lib_mgr = LibManager( - self.machine, - self.alloc, - self.seg_loader, - self.lib_mgr_cfg, - main_profiler=self.main_profiler, - ) self.lib_mgr.add_ctx("exec.library", self.exec_ctx) self.lib_mgr.add_ctx("dos.library", self.dos_ctx) # add all vamos libs diff --git a/amitools/vamos/libnative/initresident.py b/amitools/vamos/libnative/initresident.py index c97e4ca0..e6c4098a 100644 --- a/amitools/vamos/libnative/initresident.py +++ b/amitools/vamos/libnative/initresident.py @@ -1,4 +1,5 @@ -from amitools.vamos.atypes import Resident, ResidentFlags, Library, LibFlags, NodeType +from amitools.vamos.libtypes import Resident, Library +from amitools.vamos.libstructs import ResidentFlags, LibFlags, NodeType from .makelib import MakeLib from .libfuncs import LibFuncs @@ -13,8 +14,8 @@ def init_resident( self, resident_addr, seg_list_baddr, label_name=None, run_sp=None, exec_lib=None ): """Implement Exec's InitResident - Returns lib_base, mem_obj or lib_base, 0 or 0, None - """ + Returns lib_base, mem_obj or lib_base, 0 or 0, None + """ res = Resident(self.mem, resident_addr) if not res.is_valid(): return 0, None @@ -24,15 +25,15 @@ def init_resident( auto_init = res.flags.has_bits(ResidentFlags.RTF_AUTOINIT) if auto_init: - ai = res.get_auto_init() + ai = res.init.ref # create lib without calling init ml = MakeLib(self.machine, self.alloc) lib_base, mem_obj = ml.make_library( - ai.functions, - ai.init_struct, + ai.functions.aptr, + ai.init_struct.aptr, 0, - ai.pos_size, + ai.pos_size.val, seg_list_baddr, label_name=label_name, run_sp=run_sp, @@ -41,22 +42,22 @@ def init_resident( # fill lib lib = Library(self.mem, lib_base) flags = LibFlags.LIBF_CHANGED | LibFlags.LIBF_SUMUSED - lib.setup(version=res.version, type=res.type, flags=flags) - lib.name = res.name - lib.id_string = res.id_string + lib.new_lib(version=res.version, type=res.type.val, flags=flags) + lib.name.aptr = res.name.aptr + lib.id_string.aptr = res.id_string.aptr # now call init - if ai.init_func != 0: - run_name = "InitResident:%s" % res.name + if ai.init_func.aptr != 0: + run_name = "InitResident:%s" % res.name.str lib_base = ml.run_init( - ai.init_func, lib_base, seg_list_baddr, run_name, run_sp + ai.init_func.aptr, lib_base, seg_list_baddr, run_name, run_sp ) if lib_base == 0: return 0, None # add lib to exec list - rtype = res.type + rtype = res.type.val if rtype == NodeType.NT_LIBRARY: lf = LibFuncs(self.machine, self.alloc) lf.add_library(lib_base, exec_lib) @@ -66,13 +67,15 @@ def init_resident( elif rtype == NodeType.NT_RESOURCE: # TODO raise NotImplementedError("InitResident(NT_RESOURCE)") + else: + raise ValueError("InitResident: invalid type!") else: # no auto init, lib_base, or mem_obj lib_base = 0 mem_obj = None # call init func - init_func = res.init + init_func = res.init.aptr if init_func != 0: ml = MakeLib(self.machine, self.alloc) lib_base = ml.run_init( diff --git a/amitools/vamos/libnative/libfuncs.py b/amitools/vamos/libnative/libfuncs.py index 0e6fd2d0..9eab7608 100644 --- a/amitools/vamos/libnative/libfuncs.py +++ b/amitools/vamos/libnative/libfuncs.py @@ -1,4 +1,5 @@ -from amitools.vamos.atypes import Library, LibFlags, NodeType, ExecLibrary +from amitools.vamos.libtypes import Library, ExecLibrary +from amitools.vamos.libstructs import LibFlags, NodeType from amitools.vamos.loader import SegList from amitools.vamos.machine.regs import * from amitools.vamos.machine.opcodes import op_jmp @@ -28,7 +29,7 @@ def find_library(self, lib_name, exec_lib=None): def add_library(self, lib_base, exec_lib=None): lib = Library(self.mem, lib_base) - lib.node.type = NodeType.NT_LIBRARY + lib.node.type.val = NodeType.NT_LIBRARY self.sum_library(lib_base) if exec_lib is None: exec_addr = self.mem.r32(4) @@ -58,7 +59,7 @@ def close_library(self, lib_base, seg_loader, run_sp=None): def set_function(self, lib_base, lvo, new_func_addr): """return old func addr or None if patch failed""" lib = Library(self.mem, lib_base) - neg_size = lib.neg_size + neg_size = lib.neg_size.val if lvo < 0: lvo = -lvo # check lvo range diff --git a/amitools/vamos/libnative/loader.py b/amitools/vamos/libnative/loader.py index 16f98884..afdb8d76 100644 --- a/amitools/vamos/libnative/loader.py +++ b/amitools/vamos/libnative/loader.py @@ -1,6 +1,6 @@ from amitools.vamos.log import log_libmgr from .initresident import InitRes -from amitools.vamos.atypes import Resident +from amitools.vamos.libtypes import Resident from amitools.vamos.loader import SegmentLoader, SegList @@ -14,7 +14,7 @@ def __init__(self, machine, alloc, segloader): def load_sys_lib(self, sys_bin_file, run_sp=None): """try to load native lib from sys path - return (lib_base_addr, seglist_baddr) or (0,0)""" + return (lib_base_addr, seglist_baddr) or (0,0)""" # load seglist seglist_baddr = self.segloader.load_sys_seglist(sys_bin_file) if seglist_baddr: @@ -45,7 +45,7 @@ def _load_common(self, lib_name, seglist_baddr, run_sp): def load_ami_lib(self, lib_name, cwd_lock=None, run_sp=None, progdir_lock=None): """try to load native lib from ami path - return (lib_base_addr, seglist_baddr) or (0,0)""" + return (lib_base_addr, seglist_baddr) or (0,0)""" search_paths = self.get_lib_search_paths(lib_name) # now try search paths prog_dir = "PROGDIR:" diff --git a/amitools/vamos/libnative/makefuncs.py b/amitools/vamos/libnative/makefuncs.py index 6b640a63..b7cddf93 100644 --- a/amitools/vamos/libnative/makefuncs.py +++ b/amitools/vamos/libnative/makefuncs.py @@ -8,8 +8,8 @@ def __init__(self, mem): def make_functions(self, lib_base_ptr, func_array_ptr, disp_base_ptr=0): """Exec's MakeFunctions() call - returns table size in bytes - """ + returns table size in bytes + """ size = 0 src_ptr = func_array_ptr dst_ptr = lib_base_ptr - 6 diff --git a/amitools/vamos/libnative/makelib.py b/amitools/vamos/libnative/makelib.py index 4ea7ea90..7086c8fe 100644 --- a/amitools/vamos/libnative/makelib.py +++ b/amitools/vamos/libnative/makelib.py @@ -1,6 +1,6 @@ from .makefuncs import MakeFuncs from .initstruct import InitStruct -from amitools.vamos.atypes import Library +from amitools.vamos.libtypes import Library from amitools.vamos.machine.regs import * @@ -22,8 +22,8 @@ def make_library( ): """Exec's MakeLibrary - return lib_base, mem_obj or 0, None - """ + return lib_base, mem_obj or 0, None + """ neg_size, offsets = self._calc_neg_size(vectors_addr) neg_size = self._round_long(neg_size) pos_size = self._round_long(pos_size) @@ -44,9 +44,7 @@ def make_library( mf.make_functions(lib_base, vectors_addr) # lib object and set neg/pos size - lib = Library(self.mem, lib_base) - lib.set_neg_size(neg_size) - lib.set_pos_size(pos_size) + lib = Library(self.mem, addr, neg_size=neg_size, pos_size=pos_size) # init struct? if init_struct_addr != 0: diff --git a/amitools/vamos/libnative/mgr.py b/amitools/vamos/libnative/mgr.py index fba24dd7..95027ddb 100644 --- a/amitools/vamos/libnative/mgr.py +++ b/amitools/vamos/libnative/mgr.py @@ -1,14 +1,15 @@ from amitools.vamos.log import log_libmgr -from amitools.vamos.atypes import Library +from amitools.fd import read_lib_fd from .loader import LibLoader from .libfuncs import LibFuncs class ALibInfo(object): - def __init__(self, name, load_addr, seglist_baddr): + def __init__(self, name, load_addr, seglist_baddr, lib_fd=None): self.name = name self.load_addr = load_addr self.seglist_baddr = seglist_baddr + self.lib_fd = lib_fd self.base_addrs = {} def __str__(self): @@ -33,6 +34,9 @@ def get_load_addr(self): def get_seglist_baddr(self): return self.seglist_baddr + def get_lib_fd(self): + return self.lib_fd + def add_base_addr(self, base_addr): if base_addr in self.base_addrs: self.base_addrs[base_addr] += 1 @@ -68,16 +72,16 @@ def __init__(self, machine, alloc, segloader): def is_base_addr(self, addr): """check if a given addr is the lib base of a native lib - return info if found or None - """ + return info if found or None + """ for info in self.lib_infos: if info.is_base_addr(addr): return info def is_load_addr(self, addr): """check if a given addr is the lib load addr - return info if found or None - """ + return info if found or None + """ for info in self.lib_infos: if info.is_load_addr(addr): return info @@ -150,8 +154,11 @@ def open_lib(self, lib_name, cwd_lock=None, run_sp=None, progdir_lock=None): log_libmgr.info("loaded: @%06x seglist: @%06x", load_addr, seglist_baddr) info = self.segloader.get_info(seglist_baddr) log_libmgr.info("loaded: %s", info) + # try to load associated fd (if available) + fd = read_lib_fd(base_name, add_std_calls=False) + log_libmgr.info("loaded FD: '%s' -> %r", base_name, fd is not None) # store original load addr and name in info - lib_info = self._add_info(base_name, load_addr, seglist_baddr) + lib_info = self._add_info(base_name, load_addr, seglist_baddr, fd) loaded = True else: load_addr = lib_info.get_load_addr() @@ -179,8 +186,8 @@ def _find_info(self, base_name): if info.get_name() == base_name: return info - def _add_info(self, base_name, load_addr, seglist_baddr): - lib_info = ALibInfo(base_name, load_addr, seglist_baddr) + def _add_info(self, base_name, load_addr, seglist_baddr, fd): + lib_info = ALibInfo(base_name, load_addr, seglist_baddr, fd) self.lib_infos.append(lib_info) return lib_info diff --git a/amitools/vamos/libstructs/__init__.py b/amitools/vamos/libstructs/__init__.py new file mode 100644 index 00000000..d5832e0b --- /dev/null +++ b/amitools/vamos/libstructs/__init__.py @@ -0,0 +1,3 @@ +from .exec_ import * +from .dos import * +from .util import * diff --git a/amitools/vamos/libstructs/dos.py b/amitools/vamos/libstructs/dos.py new file mode 100644 index 00000000..08eaf183 --- /dev/null +++ b/amitools/vamos/libstructs/dos.py @@ -0,0 +1,369 @@ +from amitools.vamos.astructs import ( + AmigaStruct, + AmigaStructDef, + BPTR, + BPTR_SELF, + BPTR_VOID, + APTR, + APTR_VOID, + APTR_SELF, + UBYTE, + BYTE, + UWORD, + WORD, + ULONG, + LONG, + BSTR, + CSTR, + ARRAY, +) +from .exec_ import ( + TaskStruct, + MsgPortStruct, + MinListStruct, + NodeStruct, + SignalSemaphoreStruct, + LibraryStruct, + MessageStruct, +) + + +@AmigaStructDef +class FileLockStruct(AmigaStruct): + _format = [ + (BPTR_SELF, "fl_Link"), + (ULONG, "fl_Key"), + (LONG, "fl_Access"), + (APTR_VOID, "fl_Task"), + (BPTR_VOID, "fl_Volume"), + ] + + +@AmigaStructDef +class FileHandleStruct(AmigaStruct): + _format = [ + (APTR_VOID, "fh_Link"), + (APTR_VOID, "fh_Port"), + (APTR_VOID, "fh_Type"), + (LONG, "fh_Buf"), + (LONG, "fh_Pos"), + (LONG, "fh_End"), + (LONG, "fh_Funcs"), + (LONG, "fh_Func2"), + (LONG, "fh_Func3"), + (LONG, "fh_Args"), + (LONG, "fh_Arg2"), + ] + + +@AmigaStructDef +class PathListStruct(AmigaStruct): + _format = [(BPTR_SELF, "pl_Next"), (BPTR(FileLockStruct), "pl_Lock")] + + +@AmigaStructDef +class CLIStruct(AmigaStruct): + _format = [ + (LONG, "cli_Result2"), + (BSTR, "cli_SetName"), + (BPTR(PathListStruct), "cli_CommandDir"), + (LONG, "cli_ReturnCode"), + (BSTR, "cli_CommandName"), + (LONG, "cli_FailLevel"), + (BSTR, "cli_Prompt"), + (BPTR(FileHandleStruct), "cli_StandardInput"), + (BPTR(FileHandleStruct), "cli_CurrentInput"), + (BSTR, "cli_CommandFile"), + (LONG, "cli_Interactive"), + (LONG, "cli_Background"), + (BPTR(FileHandleStruct), "cli_CurrentOutput"), + (LONG, "cli_DefaultStack"), + (BPTR(FileHandleStruct), "cli_StandardOutput"), + (BPTR_VOID, "cli_Module"), + ] + + +@AmigaStructDef +class ProcessStruct(AmigaStruct): + _format = [ + (TaskStruct, "pr_Task"), + (MsgPortStruct, "pr_MsgPort"), + # param + (WORD, "pr_Pad"), + (BPTR_VOID, "pr_SegList"), + (LONG, "pr_StackSize"), + (APTR_VOID, "pr_GlobVec"), + (LONG, "pr_TaskNum"), + (BPTR_VOID, "pr_StackBase"), + (LONG, "pr_Result2"), + (BPTR(FileLockStruct), "pr_CurrentDir"), + (BPTR(FileHandleStruct), "pr_CIS"), + (BPTR(FileHandleStruct), "pr_COS"), + (APTR_VOID, "pr_ConsoleTask"), + (APTR_VOID, "pr_FileSystemTask"), + (BPTR(CLIStruct), "pr_CLI"), + (APTR_VOID, "pr_ReturnAddr"), + (APTR_VOID, "pr_PktWait"), + (APTR_VOID, "pr_WindowPtr"), + # 2.0 + (BPTR(FileLockStruct), "pr_HomeDir"), + (LONG, "pr_Flags"), + (APTR_VOID, "pr_ExitCode"), + (LONG, "pr_ExitData"), + (CSTR, "pr_Arguments"), + (MinListStruct, "pr_LocalVars"), + (ULONG, "pr_ShellPrivate"), + (BPTR(FileHandleStruct), "pr_CES"), + ] + _subfield_aliases = { + "name": "pr_Task.tc_Node.ln_Name", + "type": "pr_Task.tc_Node.ln_Type", + "pri": "pr_Task.tc_Node.ln_Pri", + } + + +@AmigaStructDef +class DateStampStruct(AmigaStruct): + _format = [(LONG, "ds_Days"), (LONG, "ds_Minute"), (LONG, "ds_Tick")] + + +# the union in DosList is splitted up into own types + + +@AmigaStructDef +class DosListDeviceStruct(AmigaStruct): + _format = [ + (BPTR_VOID, "dol_Next"), + (LONG, "dol_Type"), + (APTR_VOID, "dol_Task"), + (BPTR_VOID, "dol_Lock"), + (BSTR, "dol_Handler"), + (LONG, "dol_StackSize"), + (LONG, "dol_Priority"), + (LONG, "dol_Startup"), + (BPTR_VOID, "dol_SegList"), + (BPTR_VOID, "dol_GlobVec"), + (BSTR, "dol_Name"), + ] + + +@AmigaStructDef +class DosListVolumeStruct(AmigaStruct): + _format = [ + (BPTR_VOID, "dol_Next"), + (LONG, "dol_Type"), + (APTR_VOID, "dol_Task"), + (BPTR_VOID, "dol_Lock"), + (DateStampStruct, "dol_VolumeDate"), + (BPTR_VOID, "dol_LockList"), + (LONG, "dol_DiskType"), + (LONG, "dol_Padding0"), + (BSTR, "dol_Name"), + ] + + +@AmigaStructDef +class AssignListStruct(AmigaStruct): + _format = [(APTR_SELF, "al_Next"), (BPTR_VOID, "al_Lock")] + + +@AmigaStructDef +class DosListAssignStruct(AmigaStruct): + _format = [ + (BPTR_VOID, "dol_Next"), + (LONG, "dol_Type"), + (APTR_VOID, "dol_Task"), + (BPTR_VOID, "dol_Lock"), + (CSTR, "dol_AssignName"), + (APTR(AssignListStruct), "dol_List"), + (ARRAY(ULONG, 4), "dol_Padding"), + (BSTR, "dol_Name"), + ] + + +@AmigaStructDef +class DosInfoStruct(AmigaStruct): + _format = [ + (BPTR_VOID, "di_McName"), + (BPTR_VOID, "di_DevInfo"), + (BPTR_VOID, "di_Devices"), + (BPTR_VOID, "di_Handlers"), + (BPTR_VOID, "di_NetHand"), + (SignalSemaphoreStruct, "di_DevLock"), + (SignalSemaphoreStruct, "di_EntryLock"), + (SignalSemaphoreStruct, "di_DeleteLock"), + ] + + +@AmigaStructDef +class RootNodeStruct(AmigaStruct): + _format = [ + (BPTR_VOID, "rn_TaskArray"), + (BPTR_VOID, "rn_ConsoleSegment"), + (DateStampStruct, "rn_Time"), + (LONG, "rn_RestartSeg"), + (BPTR_VOID, "rn_Info"), + (BPTR_VOID, "rn_FileHandlerSegment"), + (MinListStruct, "rn_CliList"), + (APTR(MsgPortStruct), "rn_BootProc"), + (BPTR_VOID, "rn_ShellSegment"), + (LONG, "rn_Flags"), + ] + + +@AmigaStructDef +class DosLibraryStruct(AmigaStruct): + _format = [ + (LibraryStruct, "lib"), + (APTR(RootNodeStruct), "dl_Root"), + (APTR_VOID, "dl_GV"), + (LONG, "dl_A2"), + (LONG, "dl_A5"), + (LONG, "dl_A6"), + (APTR_VOID, "dl_Errors"), + (APTR_VOID, "dl_TimeReq"), + (APTR_VOID, "dl_UtilityBase"), + (APTR_VOID, "dl_IntuitionBase"), + ] + + +@AmigaStructDef +class FileInfoBlockStruct(AmigaStruct): + _format = [ + (ULONG, "fib_DiskKey"), + (LONG, "fib_DirEntryType"), + (ARRAY(UBYTE, 108), "fib_FileName"), + (LONG, "fib_Protection"), + (LONG, "fib_EntryType"), + (ULONG, "fib_Size"), + (LONG, "fib_NumBlocks"), + (DateStampStruct, "fib_Date"), + (ARRAY(UBYTE, 80), "fib_Comment"), + (UWORD, "fib_OwnerUID"), + (UWORD, "fib_OwnerGID"), + (ARRAY(UBYTE, 32), "fib_Reserved"), + ] + + +@AmigaStructDef +class DosPacketStruct(AmigaStruct): + _format = [ + (APTR(MessageStruct), "dp_Link"), + (APTR(MsgPortStruct), "dp_Port"), + (LONG, "dp_Type"), + (LONG, "dp_Res1"), + (LONG, "dp_Res2"), + (LONG, "dp_Arg1"), + (LONG, "dp_Arg2"), + (LONG, "dp_Arg3"), + (LONG, "dp_Arg4"), + (LONG, "dp_Arg5"), + (LONG, "dp_Arg6"), + (LONG, "dp_Arg7"), + ] + + +@AmigaStructDef +class AChainStruct(AmigaStruct): + _format = [ + (APTR_SELF, "an_Child"), + (APTR_SELF, "an_Parent"), + (BPTR_VOID, "an_Lock"), + (FileInfoBlockStruct, "an_Info"), + (BYTE, "an_Flags"), + (UBYTE, "an_String"), + ] + + +@AmigaStructDef +class AnchorPathStruct(AmigaStruct): + _format = [ + (APTR(AChainStruct), "ap_Base"), + (APTR(AChainStruct), "ap_Last"), + (LONG, "ap_BreakBits"), + (LONG, "ap_FoundBreak"), + (BYTE, "ap_Flags"), + (BYTE, "ap_Reserved"), + (WORD, "ap_Strlen"), + (FileInfoBlockStruct, "ap_Info"), + (UBYTE, "ap_Buf"), + ] + + +@AmigaStructDef +class DevProcStruct(AmigaStruct): + _format = [ + (APTR(MsgPortStruct), "dvp_Port"), + (BPTR_VOID, "dvp_Lock"), + (ULONG, "dvp_Flags"), + (APTR_VOID, "dvp_DevNode"), + ] + + +@AmigaStructDef +class CSourceStruct(AmigaStruct): + _format = [(APTR(UBYTE), "CS_Buffer"), (LONG, "CS_Length"), (LONG, "CS_CurChr")] + + +@AmigaStructDef +class RDArgsStruct(AmigaStruct): + _format = [ + (CSourceStruct, "RDA_Source"), + (LONG, "RDA_DAList"), + (APTR(UBYTE), "RDA_Buffer"), + (LONG, "RDA_BufSiz"), + (APTR(UBYTE), "RDA_ExtHelp"), + (LONG, "RDA_Flags"), + ] + + +@AmigaStructDef +class DateTimeStruct(AmigaStruct): + _format = [ + (DateStampStruct, "dat_Stamp"), + (UBYTE, "dat_Format"), + (UBYTE, "dat_Flags"), + (APTR(UBYTE), "dat_StrDay"), + (APTR(UBYTE), "dat_StrDate"), + (APTR(UBYTE), "dat_StrTime"), + ] + + +@AmigaStructDef +class InfoDataStruct(AmigaStruct): + _format = [ + (LONG, "id_NumSoftErrors"), + (LONG, "id_UnitNumber"), + (LONG, "id_DiskState"), + (LONG, "id_NumBlocks"), + (LONG, "id_NumBlocksUsed"), + (LONG, "id_BytesPerBlock"), + (LONG, "id_DiskType"), + (BPTR_VOID, "id_VolumeNode"), + (LONG, "id_InUse"), + ] + + +@AmigaStructDef +class SegmentStruct(AmigaStruct): + _format = [ + (BPTR_VOID, "seg_Next"), + (ULONG, "seg_UC"), + (BPTR_VOID, "seg_Seg"), + (UBYTE, "seg_Name"), + ] + + +@AmigaStructDef +class LocalVarStruct(AmigaStruct): + _format = [ + (NodeStruct, "lv_Node"), + (UWORD, "lv_Flags"), + (APTR(UBYTE), "lv_Value"), + (ULONG, "lv_Len"), + ] + + +@AmigaStructDef +class PathStruct(AmigaStruct): + _format = [(BPTR_VOID, "path_Next"), (BPTR_VOID, "path_Lock")] diff --git a/amitools/vamos/libstructs/exec_.py b/amitools/vamos/libstructs/exec_.py new file mode 100644 index 00000000..6ab47ac5 --- /dev/null +++ b/amitools/vamos/libstructs/exec_.py @@ -0,0 +1,437 @@ +from amitools.vamos.astructs import ( + AmigaStructDef, + AmigaStruct, + EnumType, + Enum, + BitFieldType, + BitField, + APTR_SELF, + APTR_VOID, + APTR, + UBYTE, + BYTE, + UWORD, + WORD, + ULONG, + LONG, + CSTR, + ARRAY, +) + + +@EnumType +class NodeType(Enum, UBYTE): + """manage valid node type constants and conversions""" + + NT_UNKNOWN = 0 + NT_TASK = 1 + NT_INTERRUPT = 2 + NT_DEVICE = 3 + NT_MSGPORT = 4 + NT_MESSAGE = 5 + NT_FREEMSG = 6 + NT_REPLYMSG = 7 + NT_RESOURCE = 8 + NT_LIBRARY = 9 + NT_MEMORY = 10 + NT_SOFTINT = 11 + NT_FONT = 12 + NT_PROCESS = 13 + NT_SEMAPHORE = 14 + NT_SIGNALSEM = 15 + NT_BOOTNODE = 16 + NT_KICKMEM = 17 + NT_GRAPHICS = 18 + NT_DEATHMESSAGE = 19 + + NT_USER = 254 + NT_EXTENDED = 255 + + +# Node +@AmigaStructDef +class NodeStruct(AmigaStruct): + _format = [ + (APTR_SELF, "ln_Succ"), + (APTR_SELF, "ln_Pred"), + (NodeType, "ln_Type"), + (BYTE, "ln_Pri"), + (CSTR, "ln_Name"), + ] + + +# MinNode +@AmigaStructDef +class MinNodeStruct(AmigaStruct): + _format = [(APTR_SELF, "mln_Succ"), (APTR_SELF, "mln_Pred")] + + +@BitFieldType +class LibFlags(BitField, UBYTE): + LIBF_SUMMING = 1 << 0 + LIBF_CHANGED = 1 << 1 + LIBF_SUMUSED = 1 << 2 + LIBF_DELEXP = 1 << 3 + + +# Library +@AmigaStructDef +class LibraryStruct(AmigaStruct): + _format = [ + (NodeStruct, "lib_Node"), + (LibFlags, "lib_Flags"), + (UBYTE, "lib_pad"), + (UWORD, "lib_NegSize"), + (UWORD, "lib_PosSize"), + (UWORD, "lib_Version"), + (UWORD, "lib_Revision"), + (CSTR, "lib_IdString"), + (ULONG, "lib_Sum"), + (UWORD, "lib_OpenCnt"), + ] + _subfield_aliases = { + "name": "lib_Node.ln_Name", + "type": "lib_Node.ln_Type", + "pri": "lib_Node.ln_Pri", + } + + +# List +@AmigaStructDef +class ListStruct(AmigaStruct): + _format = [ + (APTR(NodeStruct), "lh_Head"), + (APTR(NodeStruct), "lh_Tail"), + (APTR(NodeStruct), "lh_TailPred"), + (NodeType, "lh_Type"), + (UBYTE, "l_pad"), + ] + + +# MinList +@AmigaStructDef +class MinListStruct(AmigaStruct): + _format = [ + (APTR(MinNodeStruct), "mlh_Head"), + (APTR(MinNodeStruct), "mlh_Tail"), + (APTR(MinNodeStruct), "mlh_TailPred"), + ] + + +@EnumType +class MsgPortFlags(Enum, UBYTE): + PA_SIGNAL = 0 + PA_SOFTINT = 1 + PA_IGNORE = 2 + + +# MsgPort +@AmigaStructDef +class MsgPortStruct(AmigaStruct): + _format = [ + (NodeStruct, "mp_Node"), + (MsgPortFlags, "mp_Flags"), + (UBYTE, "mp_SigBit"), + (APTR_VOID, "mp_SigTask"), + (ListStruct, "mp_MsgList"), + ] + _subfield_aliases = { + "name": "mp_Node.ln_Name", + "type": "mp_Node.ln_Type", + "pri": "mp_Node.ln_Pri", + } + + +# Message +@AmigaStructDef +class MessageStruct(AmigaStruct): + _format = [ + (NodeStruct, "mn_Node"), + (APTR(MsgPortStruct), "mn_ReplyPort"), + (UWORD, "mn_Length"), + ] + _subfield_aliases = { + "name": "mn_Node.ln_Name", + "type": "mn_Node.ln_Type", + "pri": "mn_Node.ln_Pri", + } + + +# IntVector +@AmigaStructDef +class IntVectorStruct(AmigaStruct): + _format = [ + (APTR_VOID, "iv_Data"), + (APTR_VOID, "iv_Code"), + (APTR(NodeStruct), "iv_Node"), + ] + + +# SoftIntList +@AmigaStructDef +class SoftIntListStruct(AmigaStruct): + _format = [(ListStruct, "sh_List"), (UWORD, "sh_Pad")] + + +@BitFieldType +class TaskFlags(BitField, UBYTE): + TF_PROCTIME = 1 << 0 + TF_ETASK = 1 << 3 + TF_STACKCHK = 1 << 4 + TF_EXCEPT = 1 << 5 + TF_SWITCH = 1 << 6 + TF_LAUNCH = 1 << 7 + + +@EnumType +class TaskState(Enum, UBYTE): + TS_INVALID = 0 + TS_ADDED = 1 + TS_RUN = 2 + TS_READY = 3 + TS_WAIT = 4 + TS_EXCEPT = 5 + TS_REMOVED = 6 + + +# Task +@AmigaStructDef +class TaskStruct(AmigaStruct): + _format = [ + (NodeStruct, "tc_Node"), + (TaskFlags, "tc_Flags"), + (TaskState, "tc_State"), + (BYTE, "tc_IDNestCnt"), + (BYTE, "tc_TDNestCnt"), + (ULONG, "tc_SigAlloc"), + (ULONG, "tc_SigWait"), + (ULONG, "tc_SigRecvd"), + (ULONG, "tc_SigExcept"), + (UWORD, "tc_TrapAlloc"), + (UWORD, "tc_TrapAble"), + (APTR_VOID, "tc_ExceptData"), + (APTR_VOID, "tc_ExceptCode"), + (APTR_VOID, "tc_TrapData"), + (APTR_VOID, "tc_TrapCode"), + (APTR_VOID, "tc_SPReg"), + (APTR_VOID, "tc_SPLower"), + (APTR_VOID, "tc_SPUpper"), + (APTR_VOID, "tc_Switch"), + (APTR_VOID, "tc_Launch"), + (ListStruct, "tc_MemEntry"), + (APTR_VOID, "tc_UserData"), + ] + _subfield_aliases = { + "name": "tc_Node.ln_Name", + "type": "tc_Node.ln_Type", + "pri": "tc_Node.ln_Pri", + } + + +@BitFieldType +class AttnFlags(BitField, UWORD): + AFF_68010 = 1 << 0 + AFF_68020 = 1 << 1 + AFF_68030 = 1 << 2 + AFF_68040 = 1 << 3 + AFF_68881 = 1 << 4 + AFF_68882 = 1 << 5 + AFF_FPU40 = 1 << 6 + AFF_68060 = 1 << 7 + + +@AmigaStructDef +class ExecLibraryStruct(AmigaStruct): + _format = [ + (LibraryStruct, "LibNode"), + # Static System Variables + (UWORD, "SoftVer"), + (WORD, "LowMemChkSum"), + (ULONG, "ChkBase"), + (APTR_VOID, "ColdCapture"), + (APTR_VOID, "CoolCapture"), + (APTR_VOID, "WarmCapture"), + (APTR_VOID, "SysStkUpper"), + (APTR_VOID, "SysStkLower"), + (ULONG, "MaxLocMem"), + (APTR_VOID, "DebugEntry"), + (APTR_VOID, "DebugData"), + (APTR_VOID, "AlertData"), + (APTR_VOID, "MaxExtMem"), + (UWORD, "ChkSum"), + # Interrupt Related + (ARRAY(IntVectorStruct, 16), "IntVects"), + # Dynamic System Variables + (APTR(TaskStruct), "ThisTask"), + (ULONG, "IdleCount"), + (ULONG, "DispCount"), + (UWORD, "Quantum"), + (UWORD, "Elapsed"), + (UWORD, "SysFlags"), + (BYTE, "IDNestCnt"), + (BYTE, "TDNestCnt"), + (AttnFlags, "AttnFlags"), + (UWORD, "AttnResched"), + (APTR_VOID, "ResModules"), + (APTR_VOID, "TaskTrapCode"), + (APTR_VOID, "TaskExceptCode"), + (APTR_VOID, "TaskExitCode"), + (ULONG, "TaskSigAlloc"), + (UWORD, "TaskTrapAlloc"), + # System Lists (private!) + (ListStruct, "MemList"), + (ListStruct, "ResourceList"), + (ListStruct, "DeviceList"), + (ListStruct, "IntrList"), + (ListStruct, "LibList"), + (ListStruct, "PortList"), + (ListStruct, "TaskReady"), + (ListStruct, "TaskWait"), + (ARRAY(SoftIntListStruct, 5), "SoftIntList"), + # Other Globals + (ARRAY(ULONG, 4), "LastAlert"), + (UBYTE, "VBlankFrequency"), + (UBYTE, "PowerSupplyFrequency"), + (ListStruct, "SemaphoreList"), + (APTR_VOID, "KickMemPtr"), + (APTR_VOID, "KickTagPtr"), + (APTR_VOID, "KickCheckSum"), + # V36 Additions + (ULONG, "ex_Pad0"), + (ULONG, "ex_LaunchPoint"), + (APTR_VOID, "ex_RamLibPrivate"), + (ULONG, "ex_EClockFrequency"), + (ULONG, "ex_CacheControl"), + (ULONG, "ex_TaskID"), + (ARRAY(ULONG, 5), "ex_Reserved1"), + (APTR_VOID, "ex_MMULock"), + (ARRAY(ULONG, 3), "ex_Reserved2"), + # V39 Additions + (MinListStruct, "ex_MemHandlers"), + (APTR_VOID, "ex_MemHandler"), + ] + _subfield_aliases = { + "name": "LibNode.lib_Node.ln_Name", + "type": "LibNode.lib_Node.ln_Type", + "pri": "LibNode.lib_Node.ln_Pri", + "id_string": "LibNode.lib_IdString", + "pos_size": "LibNode.lib_PosSize", + "neg_size": "LibNode.lib_NegSize", + } + + +# StackSwap +@AmigaStructDef +class StackSwapStruct(AmigaStruct): + _format = [ + (APTR_VOID, "stk_Lower"), + (ULONG, "stk_Upper"), + (APTR_VOID, "stk_Pointer"), + ] + + +# Semaphores +@AmigaStructDef +class SemaphoreRequestStruct(AmigaStruct): + _format = [(MinNodeStruct, "sr_Link"), (APTR(TaskStruct), "sr_Waiter")] + + +@AmigaStructDef +class SignalSemaphoreStruct(AmigaStruct): + _format = [ + (NodeStruct, "ss_Link"), + (WORD, "ss_NestCount"), + (MinListStruct, "ss_WaitQueue"), + (SemaphoreRequestStruct, "ss_MultipleLink"), + (APTR(TaskStruct), "ss_Owner"), + (WORD, "ss_QueueCount"), + ] + + +# Device +@AmigaStructDef +class DeviceStruct(AmigaStruct): + _format = [(LibraryStruct, "dd_Library")] + + +# Unit +@AmigaStructDef +class UnitStruct(AmigaStruct): + _format = [ + (MsgPortStruct, "unit_MsgPort"), + (UBYTE, "unit_flags"), + (UBYTE, "unit_pad"), + (UWORD, "unit_OpenCnt"), + ] + + +# IORequests +@AmigaStructDef +class IORequestStruct(AmigaStruct): + _format = [ + (MessageStruct, "io_Message"), + (APTR(DeviceStruct), "io_Device"), + (UnitStruct, "io_Unit"), + (UWORD, "io_Command"), + (UBYTE, "io_Flags"), + (BYTE, "io_Error"), + (ULONG, "io_Actual"), + (ULONG, "io_Length"), + (ULONG, "io_Data"), + (ULONG, "io_Offset"), + ] + + +# MemChunk +@AmigaStructDef +class MemChunkStruct(AmigaStruct): + _format = [(APTR_SELF, "mc_Next"), (ULONG, "mc_Bytes")] + + +# MemHeader +@AmigaStructDef +class MemHeaderStruct(AmigaStruct): + _format = [ + (NodeStruct, "mh_Node"), + (UWORD, "mh_Attributes"), + (APTR(MemChunkStruct), "mh_First"), + (APTR_VOID, "mh_Lower"), + (APTR_VOID, "mh_Upper"), + (ULONG, "mh_Free"), + ] + + +@BitFieldType +class ResidentFlags(BitField, UBYTE): + RTF_AUTOINIT = 1 << 7 + RTF_AFTERDOS = 1 << 2 + RTF_SINGLETASK = 1 << 1 + RTF_COLDSTART = 1 << 0 + + +# AutoInit used in Residents +@AmigaStructDef +class AutoInitStruct(AmigaStruct): + _format = [ + (ULONG, "ai_PosSize"), + (APTR_VOID, "ai_Functions"), + (APTR_VOID, "ai_InitStruct"), + (APTR_VOID, "ai_InitFunc"), + ] + + +# Resident +@AmigaStructDef +class ResidentStruct(AmigaStruct): + _format = [ + (UWORD, "rt_MatchWord"), + (APTR_VOID, "rt_MatchTag"), + (APTR_VOID, "rt_EndSkip"), + (ResidentFlags, "rt_Flags"), + (UBYTE, "rt_Version"), + (UBYTE, "rt_Type"), + (BYTE, "rt_Pri"), + (CSTR, "rt_Name"), + (CSTR, "rt_IdString"), + (APTR(AutoInitStruct), "rt_Init"), + ] diff --git a/amitools/vamos/libstructs/util.py b/amitools/vamos/libstructs/util.py new file mode 100644 index 00000000..b1836c87 --- /dev/null +++ b/amitools/vamos/libstructs/util.py @@ -0,0 +1,25 @@ +from amitools.vamos.astructs import ( + AmigaStructDef, + AmigaStruct, + UWORD, + ULONG, +) + +# TagItem +@AmigaStructDef +class TagItemStruct(AmigaStruct): + _format = [(ULONG, "ti_Tag"), (ULONG, "ti_Data")] + + +# ClockData +@AmigaStructDef +class ClockDataStruct(AmigaStruct): + _format = [ + (UWORD, "sec"), + (UWORD, "min"), + (UWORD, "hour"), + (UWORD, "mday"), + (UWORD, "month"), + (UWORD, "year"), + (UWORD, "wday"), + ] diff --git a/amitools/vamos/libtypes/__init__.py b/amitools/vamos/libtypes/__init__.py new file mode 100644 index 00000000..ef8424c7 --- /dev/null +++ b/amitools/vamos/libtypes/__init__.py @@ -0,0 +1,12 @@ +# exec +from .node import Node, MinNode +from .list_ import List, MinList +from .library import Library +from .execlib import ExecLibrary +from .resident import Resident +from .task import Task +from .msg import MsgPort, Message + +# dos +from .lock import FileLock, FileHandle +from .process import CLI, Process, PathList diff --git a/amitools/vamos/libtypes/execlib.py b/amitools/vamos/libtypes/execlib.py new file mode 100644 index 00000000..8a9d92c5 --- /dev/null +++ b/amitools/vamos/libtypes/execlib.py @@ -0,0 +1,25 @@ +from amitools.vamos.libstructs import ExecLibraryStruct, NodeType +from .library import Library, LibBase +from amitools.vamos.astructs import AmigaClassDef + + +@AmigaClassDef +class ExecLibrary(LibBase, ExecLibraryStruct): + def new_lib(self, version=0, revision=0, attn_flags=0, max_loc_mem=0): + self.lib_node.new_lib(version, revision) + self.attn_flags.val = attn_flags + self.max_loc_mem.val = max_loc_mem + # init lists + self.mem_list.new_list(NodeType.NT_MEMORY) + self.resource_list.new_list(NodeType.NT_RESOURCE) + self.device_list.new_list(NodeType.NT_DEVICE) + self.intr_list.new_list(NodeType.NT_INTERRUPT) + self.lib_list.new_list(NodeType.NT_LIBRARY) + self.port_list.new_list(NodeType.NT_MSGPORT) + self.task_ready.new_list(NodeType.NT_TASK) + self.task_wait.new_list(NodeType.NT_TASK) + self.semaphore_list.new_list(NodeType.NT_SEMAPHORE) + self.mem_handlers.new_list() + + def fill_funcs(self, opcode=None, param=None): + self.lib_node.fill_funcs(opcode, param) diff --git a/amitools/vamos/libtypes/library.py b/amitools/vamos/libtypes/library.py new file mode 100644 index 00000000..548018c1 --- /dev/null +++ b/amitools/vamos/libtypes/library.py @@ -0,0 +1,104 @@ +from amitools.vamos.libstructs import LibraryStruct, NodeType +from amitools.vamos.label import LabelLib +from amitools.vamos.machine.opcodes import op_rts +from amitools.vamos.astructs import AmigaClassDef + + +class LibBase: + def __init__(self, mem, addr, **kwargs): + # if 'neg_size' is given then move base_addr of lib struct + neg_size = kwargs.get("neg_size") + if neg_size: + addr += neg_size + # now setup library struct + super().__init__(mem, addr, **kwargs) + + @classmethod + def _alloc(cls, alloc, tag, pos_size, neg_size, fd): + if tag is None: + tag = cls.get_signature() + return alloc.alloc_lib(tag, cls, pos_size=pos_size, neg_size=neg_size, fd=fd) + + @classmethod + def _free(cls, alloc, mem_obj): + alloc.free_lib(mem_obj) + + @classmethod + def alloc(cls, alloc, tag=None, pos_size=0, neg_size=0, fd=None, **kwargs): + if pos_size == 0: + pos_size = cls.get_byte_size() + # round size + neg_size = (neg_size + 3) & ~3 + # alloc lib + return super().alloc( + alloc, + pos_size, + neg_size, + fd, + tag=tag, + pos_size=pos_size, + neg_size=neg_size, + **kwargs + ) + + +@AmigaClassDef +class Library(LibBase, LibraryStruct): + def new_lib(self, version=0, revision=0, type=NodeType.NT_LIBRARY, pri=0, flags=0): + """set all lib values but name, id_str, pos_size, neg_size.""" + node = self.node + node.succ.aptr = 0 + node.pred.aptr = 0 + node.type.val = type + node.pri.val = pri + + self.flags.val = flags + self.pad.val = 0 + self.version.val = version + self.revision.val = revision + self.sum.val = 0 + self.open_cnt.val = 0 + + def fill_funcs(self, opcode=None, param=None): + """quickly fill the function table of a library with an opcode and param""" + if opcode is None: + opcode = op_rts + neg_size = self.neg_size.val + base_addr = self.get_addr() + off = 6 + while off < neg_size: + addr = base_addr - off + self._mem.w16(addr, opcode) + if param: + self._mem.w32(addr + 2, param) + off += 6 + + def calc_sum(self): + """calc the lib sum and return it""" + neg_size = self.neg_size.val + addr = self.get_addr() - neg_size + lib_sum = 0 + while addr < self.get_addr(): + val = self._mem.r32(addr) + lib_sum += val + addr += 4 + lib_sum &= 0xFFFFFFFF + return lib_sum + + def update_sum(self): + """calc new lib sum and store it""" + lib_sum = self.calc_sum() + self.sum.val = lib_sum + return lib_sum + + def check_sum(self): + """calc and compare lib sum with stored value""" + lib_sum = self.calc_sum() + got_sum = self.sum.val + return lib_sum == got_sum + + def inc_open_cnt(self): + self.open_cnt.val += 1 + + def dec_open_cnt(self): + self.open_cnt.val -= 1 diff --git a/amitools/vamos/libtypes/list_.py b/amitools/vamos/libtypes/list_.py new file mode 100644 index 00000000..75748edc --- /dev/null +++ b/amitools/vamos/libtypes/list_.py @@ -0,0 +1,157 @@ +from amitools.vamos.libstructs import ListStruct, MinListStruct +from amitools.vamos.astructs import AmigaClassDef +from .node import Node, MinNode + + +class ListIter(object): + def __init__(self, alist, start_node=None): + self.alist = alist + self.mem = self.alist._mem + if start_node is None: + self.node = alist._head.succ.ref + else: + self.node = start_node + + def __iter__(self): + return self + + def __next__(self): + succ = self.node.succ.ref + if succ is None: + raise StopIteration() + res = self.node + self.node = succ + return res + + +class ListBase: + def __iter__(self): + return ListIter(self) + + def __len__(self): + l = 0 + node = self._head.succ.ref + while True: + node = node.succ.ref + if node is None: + break + l += 1 + return l + + def iter_at(self, node): + return ListIter(self, node) + + def add_head(self, node): + n = self._head.succ.ref + node.pred.ref = self._head + node.succ.ref = n + self._head.succ.ref = node + n.pred.ref = node + + def add_tail(self, node): + tp = self.tail_pred.ref + node.succ.ref = self._tail + self._tail.pred.ref = node + node.pred.ref = tp + tp.succ.ref = node + + def rem_head(self): + node = self._head.succ.ref + if node is None: + return None + node.remove() + return node + + def rem_tail(self): + node = self.tail_pred.ref + if node is None: + return None + node.remove() + return node + + def insert(self, node, pred): + if pred is not None and pred != self._head: + pred_succ = pred.succ.ref + if pred_succ: + # normal node + node.succ.ref = pred_succ + node.pred.ref = pred + pred_succ.pred.ref = node + pred.succ.ref = node + else: + # last node + self.add_tail(node) + else: + # first node + self.add_head(node) + + +@AmigaClassDef +class MinList(MinListStruct, ListBase): + def __init__(self, mem, addr, **kwargs): + super().__init__(mem, addr, **kwargs) + self._head = MinNode(mem, self.addr) + self._tail = MinNode(mem, self.addr + 4) + + def __str__(self): + return "[MinList:@%06x,h=%06x,t=%06x,tp=%06x]" % ( + self.addr, + self.head.aptr, + self.tail.aptr, + self.tail_pred.aptr, + ) + + def new_list(self): + self.head.ref = self._tail + self.tail.ref = None + self.tail_pred.ref = self._head + + +@AmigaClassDef +class List(ListStruct, ListBase): + def __init__(self, mem, addr, **kwargs): + super().__init__(mem, addr, **kwargs) + self._head = Node(mem, self.addr) + self._tail = Node(mem, self.addr + 4) + + def __str__(self): + return "[List:@%06x,h=%06x,t=%06x,tp=%06x,%s]" % ( + self.addr, + self.head.aptr, + self.tail.aptr, + self.tail_pred.aptr, + self.type, + ) + + # ----- list ops ----- + + def new_list(self, lt): + self.type.val = lt + self.head.ref = self._tail + self.tail.ref = None + self.tail_pred.ref = self._head + + def enqueue(self, node): + pred = None + pri = node.pri.val + for ln in self: + ln_pri = ln.pri.val + if ln_pri < pri: + self.insert(node, pred) + return + pred = ln + self.add_tail(node) + + def find_names(self, name): + """this method is a generator delivering all matches""" + for node in self: + node_name = node.name.str + if node_name == name: + yield node + + def find_name(self, name): + """this method is a function returning the first match""" + for node in self: + node_name = node.name.str + if node_name == name: + return node diff --git a/amitools/vamos/libtypes/lock.py b/amitools/vamos/libtypes/lock.py new file mode 100644 index 00000000..d0bbb9b0 --- /dev/null +++ b/amitools/vamos/libtypes/lock.py @@ -0,0 +1,12 @@ +from amitools.vamos.libstructs import FileLockStruct, FileHandleStruct +from amitools.vamos.astructs import AmigaClassDef + + +@AmigaClassDef +class FileLock(FileLockStruct): + pass + + +@AmigaClassDef +class FileHandle(FileHandleStruct): + pass diff --git a/amitools/vamos/libtypes/msg.py b/amitools/vamos/libtypes/msg.py new file mode 100644 index 00000000..502f3b02 --- /dev/null +++ b/amitools/vamos/libtypes/msg.py @@ -0,0 +1,22 @@ +from amitools.vamos.libstructs import MessageStruct, MsgPortStruct, NodeType +from amitools.vamos.astructs import AmigaClassDef + + +@AmigaClassDef +class MsgPort(MsgPortStruct): + def new_port(self, pri=0, flags=0, sig_bit=0, sig_task=0, nt=NodeType.NT_MSGPORT): + self.pri.val = pri + self.type.val = nt + self.flags.val = flags + self.sig_bit.val = sig_bit + self.sig_task.setup(sig_task) + self.msg_list.new_list(NodeType.NT_MESSAGE) + + +@AmigaClassDef +class Message(MessageStruct): + def new_msg(self, pri=0, reply_port=0, length=0, nt=NodeType.NT_MESSAGE): + self.pri.val = pri + self.type.val = nt + self.reply_port.setup(reply_port) + self.length.val = length diff --git a/amitools/vamos/libtypes/node.py b/amitools/vamos/libtypes/node.py new file mode 100644 index 00000000..a2db9709 --- /dev/null +++ b/amitools/vamos/libtypes/node.py @@ -0,0 +1,54 @@ +from amitools.vamos.libstructs import NodeStruct, MinNodeStruct +from amitools.vamos.astructs import AmigaClassDef + + +class NodeBase: + def remove(self, clear=True): + succ = self.succ.ref + pred = self.pred.ref + if succ is None or pred is None: + raise ValueError("remove node without succ/pred!") + succ.pred.ref = pred + pred.succ.ref = succ + if clear: + self.succ.ref = None + self.pred.ref = None + + +@AmigaClassDef +class MinNode(MinNodeStruct, NodeBase): + """wrap an Exec MinNode in memory an allow to operate on its values.""" + + def __str__(self): + return "[MinNode:@%06x,p=%06x,s=%06x]" % ( + self.addr, + self.pred.aptr, + self.succ.aptr, + ) + + +@AmigaClassDef +class Node(NodeStruct, NodeBase): + """wrap an Exec Node in memory an allow to operate on its values.""" + + def __str__(self): + return "[Node:@%06x,p=%06x,s=%06x,%s,%d,'%s']" % ( + self.addr, + self.pred.aptr, + self.succ.aptr, + self.type, + self.pri, + self.name, + ) + + # ----- node ops ----- + + def find_name(self, name): + """find name after this node""" + succ = self.succ.ref + if succ is None: + return None + succ_name = succ.name.str + if succ_name == name: + return succ + return succ.find_name(name) diff --git a/amitools/vamos/libtypes/process.py b/amitools/vamos/libtypes/process.py new file mode 100644 index 00000000..b69509d9 --- /dev/null +++ b/amitools/vamos/libtypes/process.py @@ -0,0 +1,20 @@ +from amitools.vamos.libstructs import ProcessStruct, CLIStruct, PathListStruct, NodeType +from amitools.vamos.astructs import AmigaClassDef + + +@AmigaClassDef +class PathList(PathListStruct): + pass + + +@AmigaClassDef +class CLI(CLIStruct): + pass + + +@AmigaClassDef +class Process(ProcessStruct): + def new_proc(self): + self.task.new_task(nt=NodeType.NT_PROCESS) + self.msg_port.new_port() + self.local_vars.new_list() diff --git a/amitools/vamos/libtypes/resident.py b/amitools/vamos/libtypes/resident.py new file mode 100644 index 00000000..7d7c3e0c --- /dev/null +++ b/amitools/vamos/libtypes/resident.py @@ -0,0 +1,64 @@ +from amitools.vamos.libstructs import ( + ResidentStruct, + AutoInitStruct, + LibraryStruct, + NodeType, +) +from amitools.vamos.mem import MemoryCache +from amitools.vamos.astructs import AmigaClassDef + + +@AmigaClassDef +class Resident(ResidentStruct): + + RTC_MATCHWORD = 0x4AFC + + @classmethod + def find(cls, mem, addr, size, only_first=True, mem_cache=True): + """scan a memory region for resident structures and return the residents. + if 'only_first' is set return a single instance or None. + otherwise a list of Resident objects. + """ + # use a memory cache to speed up search + if mem_cache: + memc = MemoryCache(addr, size) + memc.read_cache(mem) + mem = memc + # start search + end_addr = addr + size + finds = [] + while addr < end_addr: + # look for match word + mw = mem.r16(addr) + if mw == cls.RTC_MATCHWORD: + # check pointer + ptr = mem.r32(addr + 2) + if ptr == addr: + # yes its a resident... + if only_first: + return cls(mem, addr) + finds.append(cls(mem, addr)) + # read end skip + addr = mem.r32(addr + 6) + addr += 2 + # nothing found for single match: + if only_first: + return None + return finds + + def is_valid(self): + if self.match_word.val != self.RTC_MATCHWORD: + return False + return self.match_tag.aptr == self.get_addr() + + def new_resident( + self, flags=0, version=0, type=NodeType.NT_LIBRARY, pri=0, init=None + ): + self.match_word.val = self.RTC_MATCHWORD + self.match_tag.aptr = self.addr + self.end_skip.aptr = self.addr + self.get_size() + self.flags.val = flags + self.version.val = version + self.type.val = type + self.pri.val = pri + self.init.setup(init) diff --git a/amitools/vamos/libtypes/task.py b/amitools/vamos/libtypes/task.py new file mode 100644 index 00000000..10cafcb7 --- /dev/null +++ b/amitools/vamos/libtypes/task.py @@ -0,0 +1,14 @@ +from amitools.vamos.libstructs import TaskStruct, NodeType, TaskState +from amitools.vamos.astructs import AmigaClassDef + + +@AmigaClassDef +class Task(TaskStruct): + def new_task(self, pri=0, flags=0, nt=NodeType.NT_TASK): + node = self.node + node.type.val = nt + node.pri.val = pri + + self.flags.val = flags + self.state.val = TaskState.TS_INVALID + self.mem_entry.new_list(NodeType.NT_MEMORY) diff --git a/amitools/vamos/loader/segload.py b/amitools/vamos/loader/segload.py index 882cfa1c..85f01e59 100644 --- a/amitools/vamos/loader/segload.py +++ b/amitools/vamos/loader/segload.py @@ -56,7 +56,7 @@ def load_ami_seglist(self, ami_bin_file, lock=None): def unload_seglist(self, seglist_baddr): """unregister given seglist baddr and free seglist. - return True if seglist was unloaded""" + return True if seglist was unloaded""" if seglist_baddr not in self.infos: log_segload.error("unknown seglist at @%06x", seglist_baddr) return False diff --git a/amitools/vamos/log.py b/amitools/vamos/log.py index 3dfa4eaa..4b9e20f3 100644 --- a/amitools/vamos/log.py +++ b/amitools/vamos/log.py @@ -143,6 +143,10 @@ def log_setup(cfg): return True +def log_shutdown(): + logging.shutdown() + + def _setup_levels(levels): for name in levels: # get and parse level diff --git a/amitools/vamos/machine/hwaccess.py b/amitools/vamos/machine/hwaccess.py index 6488385f..1633c2f9 100644 --- a/amitools/vamos/machine/hwaccess.py +++ b/amitools/vamos/machine/hwaccess.py @@ -13,10 +13,10 @@ def __str__(self): class HWAccess: """As an OS-level emulator vamos usually does not need to emulate - any custom chips. Unfortunately, some libs (e.g. xvs.library) - need custom chip access to work correctly. We emulate a minimal - sub set to make those broken (i.e. non OS-compliant) libs work. - """ + any custom chips. Unfortunately, some libs (e.g. xvs.library) + need custom chip access to work correctly. We emulate a minimal + sub set to make those broken (i.e. non OS-compliant) libs work. + """ MODE_IGNORE = 0 MODE_EMU = 1 diff --git a/amitools/vamos/machine/machine.py b/amitools/vamos/machine/machine.py index ea6137e4..de60de20 100644 --- a/amitools/vamos/machine/machine.py +++ b/amitools/vamos/machine/machine.py @@ -43,36 +43,36 @@ def __str__(self): class Machine(object): """the main interface to the m68k emulation including CPU, memory, - and traps. The machine does only a minimal setup of RAM and the CPU. - It provides a way to run m68k code. + and traps. The machine does only a minimal setup of RAM and the CPU. + It provides a way to run m68k code. - Minimal Memory Layout: - ---------------------- + Minimal Memory Layout: + ---------------------- - VBR + VBR - 000000 SP before Reset / Later mem0 - 000004 PC before Reset / Later mem4 + 000000 SP before Reset / Later mem0 + 000004 PC before Reset / Later mem4 - 000008 BEGIN Exception Vectors - ...... - 0003FC END Exception Vectors + 000008 BEGIN Exception Vectors + ...... + 0003FC END Exception Vectors - Machine Area + Machine Area - 000400 run_exit trap - 000402 exception handling trap + 000400 run_exit trap + 000402 exception handling trap - 000500 Quick Trap 0 - 000502 Quick Trap 1 - ... ... - 0005FC Quick Trap 127 + 000500 Quick Trap 0 + 000502 Quick Trap 1 + ... ... + 0005FC Quick Trap 127 - 000600 BEGIN of scratch area - ... e.g. used for sys stack - 0007FC END of scratch area - 000800 RAM begin. useable by applications - """ + 000600 BEGIN of scratch area + ... e.g. used for sys stack + 0007FC END of scratch area + 000800 RAM begin. useable by applications + """ CPU_TYPE_68000 = M68K_CPU_TYPE_68000 CPU_TYPE_68020 = M68K_CPU_TYPE_68020 @@ -138,8 +138,8 @@ def cleanup(self): def from_cfg(cls, machine_cfg, use_labels=False): """extract machine parameters from the config - return new Machine() or None on config error - """ + return new Machine() or None on config error + """ cpu = machine_cfg.cpu cpu_type, cpu_name = cls.parse_cpu_type(cpu) if cpu_type is None: @@ -318,8 +318,8 @@ def get_ram_total_kib(self): def set_zero_mem(self, mem0, mem4): """define the long words at memory address 0 and 4 that are written - after a reset was performed. On Amiga typically 0 and ExecBase. - """ + after a reset was performed. On Amiga typically 0 and ExecBase. + """ self.mem.w32(0, mem0) self.mem.w32(4, mem4) diff --git a/amitools/vamos/machine/regs.py b/amitools/vamos/machine/regs.py index 87643087..d7482c62 100644 --- a/amitools/vamos/machine/regs.py +++ b/amitools/vamos/machine/regs.py @@ -36,3 +36,23 @@ "REG_A7", "REG_PC", ) + +str_to_reg_map = { + "REG_D0": REG_D0, + "REG_D1": REG_D1, + "REG_D2": REG_D2, + "REG_D3": REG_D3, + "REG_D4": REG_D4, + "REG_D5": REG_D5, + "REG_D6": REG_D6, + "REG_D7": REG_D7, + "REG_A0": REG_A0, + "REG_A1": REG_A1, + "REG_A2": REG_A2, + "REG_A3": REG_A3, + "REG_A4": REG_A4, + "REG_A5": REG_A5, + "REG_A6": REG_A6, + "REG_A7": REG_A7, + "REG_PC": REG_PC, +} diff --git a/amitools/vamos/main.py b/amitools/vamos/main.py index 34137934..3eed36ba 100644 --- a/amitools/vamos/main.py +++ b/amitools/vamos/main.py @@ -1,11 +1,10 @@ -import os import cProfile import io import pstats from .cfg import VamosMainParser from .machine import Machine, MemoryMap -from .machine.regs import * +from .machine.regs import REG_D0 from .log import log_main, log_setup, log_help from .path import VamosPathManager from .trace import TraceManager @@ -17,7 +16,7 @@ RET_CODE_CONFIG_ERROR = 1000 -def main(cfg_files=None, args=None, cfg_dict=None): +def main(cfg_files=None, args=None, cfg_dict=None, profile=False): """vamos main entry point. setup a vamos session and run it. @@ -29,11 +28,7 @@ def main(cfg_files=None, args=None, cfg_dict=None): if an internal error occurred then return: RET_CODE_CONFIG_ERROR (1000): config error - """ - # retrieve vamos home and data dir - home_dir = os.path.dirname(__file__) - data_dir = os.path.join(home_dir, "data") - + """ # --- parse config --- mp = VamosMainParser() if not mp.parse(cfg_files, args, cfg_dict): @@ -121,7 +116,8 @@ def main(cfg_files=None, args=None, cfg_dict=None): exit_code = 1 else: ok = True - exit_code = run_state.regs[REG_D0] + # return code is limited to 0-255 + exit_code = run_state.regs[REG_D0] & 0xFF log_main.info("done. exit code=%d", exit_code) log_main.info("total cycles: %d", run_state.cycles) else: @@ -151,7 +147,7 @@ def main(cfg_files=None, args=None, cfg_dict=None): machine.cleanup() # exit - log_main.info("vamos is exiting") + log_main.info("vamos is exiting: code=%d", exit_code) return exit_code @@ -160,8 +156,8 @@ def main_profile( ): """Run vamos main with profiling enabled. - Either dump profile after run or write a profile file. - """ + Either dump profile after run or write a profile file. + """ # profile run cpr = cProfile.Profile() cpr.enable() diff --git a/amitools/vamos/mem/alloc.py b/amitools/vamos/mem/alloc.py index b45048e0..e8204066 100644 --- a/amitools/vamos/mem/alloc.py +++ b/amitools/vamos/mem/alloc.py @@ -1,6 +1,6 @@ from amitools.vamos.error import * from amitools.vamos.log import log_mem_alloc -from amitools.vamos.label import LabelRange, LabelStruct +from amitools.vamos.label import LabelRange, LabelStruct, LabelLib from amitools.vamos.astructs import AccessStruct @@ -31,17 +31,17 @@ def __str__(self): def does_fit(self, size): """check if new size would fit into chunk - return < 0 if it does not fit, 0 for exact fit, > 0 n wasted bytes - """ + return < 0 if it does not fit, 0 for exact fit, > 0 n wasted bytes + """ return self.size - size class MemoryAlloc: def __init__(self, mem, addr=0, size=0, label_mgr=None): """mem is a interface. - setup allocator starting at addr with size bytes. - if label_mgr is set then labels are created for allocations. - """ + setup allocator starting at addr with size bytes. + if label_mgr is set then labels are created for allocations. + """ # if no size is specified then take mem total if size == 0: size = mem.get_ram_size_kib() * 1024 @@ -94,8 +94,8 @@ def is_all_free(self): def _find_best_chunk(self, size): """find best chunk that could take the given alloc - return: index of chunk in free list or -1 if none found + bytes left in chunk - """ + return: index of chunk in free list or -1 if none found + bytes left in chunk + """ chunk = self.free_first while chunk != None: left = chunk.does_fit(size) @@ -369,6 +369,36 @@ def free_struct(self, mem): self.free_mem(mem.addr, mem.size) del self.mem_objs[mem.addr] + # library + def alloc_lib( + self, name, lib_struct, pos_size=0, neg_size=0, fd=None, add_label=True + ): + if pos_size == 0: + pos_size = lib_struct.get_size() + # round neg_size to multiple of four + neg_size = (neg_size + 3) & ~3 + size = neg_size + pos_size + # alloc full size + addr = self.alloc_mem(size) + base_addr = addr + neg_size + if self.label_mgr is not None and add_label: + label = LabelLib(name, base_addr, neg_size, pos_size, lib_struct, fd) + self.label_mgr.add_label(label) + else: + label = None + access = AccessStruct(self.mem, lib_struct, base_addr) + mem = Memory(addr, size, label, access) + log_mem_alloc.info("alloc lib: %s", mem) + self.mem_objs[addr] = mem + return mem + + def free_lib(self, mem): + log_mem_alloc.info("free lib: %s", mem) + if self.label_mgr is not None: + self.label_mgr.remove_label(mem.label) + self.free_mem(mem.addr, mem.size) + del self.mem_objs[mem.addr] + # cstr def alloc_cstr(self, name, cstr): size = len(cstr) + 1 diff --git a/amitools/vamos/mem/cache.py b/amitools/vamos/mem/cache.py index 1d9eb091..bd15de60 100644 --- a/amitools/vamos/mem/cache.py +++ b/amitools/vamos/mem/cache.py @@ -3,7 +3,7 @@ class MemoryCache(object): """copy a region of emulator memory to a python bytearray - to work on it faster""" + to work on it faster""" def __init__(self, start_addr, size_bytes): """create cache of given byte size representing addr range""" diff --git a/amitools/vamos/path/amipath.py b/amitools/vamos/path/amipath.py index 7b3dc2cd..c7eaaf31 100644 --- a/amitools/vamos/path/amipath.py +++ b/amitools/vamos/path/amipath.py @@ -10,30 +10,30 @@ def __str__(self): class AmiPath(object): """holds a single Amiga path either in relative or absolute format. - A path is considered 'absolute' if it starts with a volume: or - assign: prefix. + A path is considered 'absolute' if it starts with a volume: or + assign: prefix. - A relative path can only be resolved with the current directory stored - in the associated path environment. + A relative path can only be resolved with the current directory stored + in the associated path environment. - A colon prefixed path is also considered relative, e.g. ':bla'. - It is local to the prefix of the current directory. + A colon prefixed path is also considered relative, e.g. ':bla'. + It is local to the prefix of the current directory. - The empty string '' represents the current directory of the associated - environment. + The empty string '' represents the current directory of the associated + environment. - In a 'volume path' all assigns are resolved and it - always starts with a valid volume: prefix. + In a 'volume path' all assigns are resolved and it + always starts with a valid volume: prefix. - Valid path syntax is: + Valid path syntax is: - (prefix:)?(name)?(/name)+/? + (prefix:)?(name)?(/name)+/? - prefix: all but '/:', may be empty - name: all but '/:', non-empty + prefix: all but '/:', may be empty + name: all but '/:', non-empty - ':/', '//' is invalid - """ + ':/', '//' is invalid + """ def __init__(self, pstr=""): self.pstr = pstr @@ -78,8 +78,8 @@ def is_name_only(self): def ends_with_name(self): """make sure the path ends with a name - A path ending with / or : is not valid. - """ + A path ending with / or : is not valid. + """ p = self.pstr # empty is invalid if len(p) == 0: @@ -93,10 +93,10 @@ def ends_with_name(self): def prefix(self, lower=False): """if the path is absolute then a prefix string is returned. - The prefix in a valid abs path is either an assign or volume name. + The prefix in a valid abs path is either an assign or volume name. - A relative path has a None prefix. - """ + A relative path has a None prefix. + """ pos = self.pstr.find(":") if pos <= 0: return None @@ -109,9 +109,9 @@ def prefix(self, lower=False): def postfix(self, skip_leading=False, lower=False, skip_trailing=True): """the postfix string of the path. - A relative path is returned as is. - The postifx of an absolute path is starting with the colon ':' - """ + A relative path is returned as is. + The postifx of an absolute path is starting with the colon ':' + """ p = self.pstr pos = p.find(":") # skip prefix @@ -165,8 +165,8 @@ def is_valid(self): def is_syntax_valid(self): """check if a path has valid syntax. - Returns True if all checks passed otherwise False - """ + Returns True if all checks passed otherwise False + """ # valid cases s = self.pstr if s in (":", "", "/"): @@ -194,20 +194,20 @@ def is_syntax_valid(self): def parent(self): """return a new path with the last path component removed. - Returns None if stripping is not possible. + Returns None if stripping is not possible. - The path is not expanded (to a absolute or even volume path)! + The path is not expanded (to a absolute or even volume path)! - Example: - bar -> '' - foo/bar -> foo - baz:foo/bar -> baz:foo - baz:foo -> '' - /bar -> / - / -> None - :foo -> foo - foo: -> None - """ + Example: + bar -> '' + foo/bar -> foo + baz:foo/bar -> baz:foo + baz:foo -> '' + /bar -> / + / -> None + :foo -> foo + foo: -> None + """ p = self.postfix() if p in ("/", ":"): return None @@ -231,10 +231,10 @@ def parent(self): def filename(self): """return the filename component of a path - even if the path is terminated with '/' the last component is taken + even if the path is terminated with '/' the last component is taken - return filename or None if not found - """ + return filename or None if not found + """ names = self.names() if len(names) == 0: return None @@ -243,8 +243,8 @@ def filename(self): def dirname(self): """return the dirname component of a path - return dirname or None if not found - """ + return dirname or None if not found + """ names = self.names(True) n = len(names) if n == 0: @@ -271,9 +271,9 @@ def absdirname(self): def names(self, with_special_name=False): """return a list of strings with the names contained in postfix - Note if skip_leading is False then a parent or prefix local path - gets a special name prefixed: '/' or ':' - """ + Note if skip_leading is False then a parent or prefix local path + gets a special name prefixed: '/' or ':' + """ p = self.postfix(not with_special_name) n = len(p) if n == 0: @@ -291,10 +291,10 @@ def names(self, with_special_name=False): def join(self, opath): """join this path with the given path. - If expand is True then this path can be made absolute if necessary. + If expand is True then this path can be made absolute if necessary. - Note:May return None if join is not possible. - """ + Note:May return None if join is not possible. + """ # join with cwd returns path itself if opath.is_cwd(): return self diff --git a/amitools/vamos/path/assign.py b/amitools/vamos/path/assign.py index a1523c76..00321795 100644 --- a/amitools/vamos/path/assign.py +++ b/amitools/vamos/path/assign.py @@ -249,13 +249,13 @@ def _concat_assign(self, aname, remainder): def resolve_assigns(self, ami_path, recursive=True, as_list=False): """replace all assigns found in path until only a volume path exists. - do not touch relative paths or abs paths without assign prefix. + do not touch relative paths or abs paths without assign prefix. - return: original path if path is not absolute - or does not contain assign prefix - or: string if no multi assigns are involved - or: list of string if multi assigns were encountered - """ + return: original path if path is not absolute + or does not contain assign prefix + or: string if no multi assigns are involved + or: list of string if multi assigns were encountered + """ log_path.info("resolve_assign: ami_path='%s'", ami_path) split = self._split_volume_remainder(ami_path) if split is None: diff --git a/amitools/vamos/path/mgr.py b/amitools/vamos/path/mgr.py index 7aba065f..ac5d880e 100644 --- a/amitools/vamos/path/mgr.py +++ b/amitools/vamos/path/mgr.py @@ -92,11 +92,11 @@ def get_default_env(self): def create_env(self, cwd=None, cmd_paths=None): """return a new AmiPathEnv - either share cwd or cmd_paths with current default env or - set own values. + either share cwd or cmd_paths with current default env or + set own values. - make sure that the env has a valid path_resolver - """ + make sure that the env has a valid path_resolver + """ if cwd is None: cwd = self.default_env.get_cwd() if cmd_paths is None: @@ -255,8 +255,8 @@ def get_cwd(self): def is_prefix_valid(self, ami_path): """check if prefix is either a volume or assign name - raise AmiPathError is path is relative - """ + raise AmiPathError is path is relative + """ if type(ami_path) is str: ami_path = AmiPath(ami_path) p = ami_path.prefix() @@ -273,8 +273,8 @@ def is_prefix_valid(self, ami_path): def is_volume_path(self, ami_path): """check if the prefix is a valid volume name. - raise AmiPathError is path is relative - """ + raise AmiPathError is path is relative + """ if type(ami_path) is str: ami_path = AmiPath(ami_path) p = ami_path.prefix() @@ -288,8 +288,8 @@ def is_volume_path(self, ami_path): def is_assign_path(self, ami_path): """check if the prefix is a valid assign name. - raise AmiPathError is path is relative - """ + raise AmiPathError is path is relative + """ if type(ami_path) is str: ami_path = AmiPath(ami_path) p = ami_path.prefix() @@ -302,7 +302,7 @@ def is_assign_path(self, ami_path): def is_valid(self, ami_path): """check if given path has valid syntax and if its absolute then - also check prefix""" + also check prefix""" if type(ami_path) is str: ami_path = AmiPath(ami_path) if not ami_path.is_syntax_valid(): @@ -315,8 +315,8 @@ def is_valid(self, ami_path): def is_multi_assign_path(self, ami_path): """check if path resolves to multiple paths - raise AmiPathError is path is relative - """ + raise AmiPathError is path is relative + """ if type(ami_path) is str: ami_path = AmiPath(ami_path) # expand along assigns until a multi assign or a volume is found @@ -340,13 +340,13 @@ def is_multi_assign_path(self, ami_path): def abspath(self, ami_path, env=None): """return an absolute Amiga path - If the path is already absolute then return path itself. - Otherwise create a new AmiPath object with the absolute path - by joining this path with the current directory of the path env. + If the path is already absolute then return path itself. + Otherwise create a new AmiPath object with the absolute path + by joining this path with the current directory of the path env. - An AmiPathError is raised if the relative path cannot be joined - to the current directory, e.g.: "foo:".join("/") - """ + An AmiPathError is raised if the relative path cannot be joined + to the current directory, e.g.: "foo:".join("/") + """ if type(ami_path) is str: ami_path = AmiPath(ami_path) # already absolute? @@ -369,15 +369,15 @@ def abspath(self, ami_path, env=None): def volpath(self, ami_path, env=None, strict=False): """return a volume path. - Try to resolve a path to a single volume path. If - assigns are encountered that resolve to multiple paths - then an AmiPathError is raised. + Try to resolve a path to a single volume path. If + assigns are encountered that resolve to multiple paths + then an AmiPathError is raised. - If the path has an unknown prefix then return None - of if 'strict' is True then raise an AmiPathError. + If the path has an unknown prefix then return None + of if 'strict' is True then raise an AmiPathError. - Raises AmiPathError is relative path is invalid. - """ + Raises AmiPathError is relative path is invalid. + """ if type(ami_path) is str: ami_path = AmiPath(ami_path) # if its a local path then simply append cwd @@ -406,12 +406,12 @@ def volpath(self, ami_path, env=None, strict=False): def volpaths(self, ami_path, env=None, strict=False): """return a list of volume paths for this path. - If the path resolves to multiple paths then all resulting paths - are generated by recursevily applying multi-assigns. + If the path resolves to multiple paths then all resulting paths + are generated by recursevily applying multi-assigns. - If the prefix does not exist then return an empty list - or if strict is True rais an - """ + If the prefix does not exist then return an empty list + or if strict is True rais an + """ if type(ami_path) is str: ami_path = AmiPath(ami_path) if ami_path.is_local(): @@ -433,15 +433,15 @@ def volpaths(self, ami_path, env=None, strict=False): def resolve_assigns(self, ami_path, recursive=False): """if path is prefixed by an assign name then resolve the assign. - return a list of AmiPaths if mutliple assigns are involved - or return a single path + return a list of AmiPaths if mutliple assigns are involved + or return a single path - If the path is relative or if the prefix is not an assign then - path is returned as is. + If the path is relative or if the prefix is not an assign then + path is returned as is. - Returns a list of AmiPaths if the assign chain resolved to multiple - paths otherwise a single AmiPath() is returned. - """ + Returns a list of AmiPaths if the assign chain resolved to multiple + paths otherwise a single AmiPath() is returned. + """ if type(ami_path) is str: ami_path = AmiPath(ami_path) if ami_path.is_absolute() and self.is_assign_path(ami_path): @@ -456,15 +456,15 @@ def resolve_assigns(self, ami_path, recursive=False): def cmdpaths(self, ami_path, env=None, prepend_cur_dir=True, make_volpaths=True): """return a list of command paths derived from this path. - If the path contains a name only then a list containing the - current cmd paths form the path env are returned. If - prepend_cur_dir is enabled then the current dir is added as well. + If the path contains a name only then a list containing the + current cmd paths form the path env are returned. If + prepend_cur_dir is enabled then the current dir is added as well. - If this path is not a name only then it is converted to a - volpath and returned in a single item list. + If this path is not a name only then it is converted to a + volpath and returned in a single item list. - Path that do not end with name raise an AmiPathError - """ + Path that do not end with name raise an AmiPathError + """ if type(ami_path) is str: ami_path = AmiPath(ami_path) if ami_path.is_name_only(): @@ -498,9 +498,9 @@ def cmdpaths(self, ami_path, env=None, prepend_cur_dir=True, make_volpaths=True) def to_sys_path(self, ami_path, env=None, strict=False): """Convert an Amiga path to a sys path - If path is not a volume path it will be converted with volpath() - first. - """ + If path is not a volume path it will be converted with volpath() + first. + """ if type(ami_path) is str: ami_path = AmiPath(ami_path) if ami_path.is_local() or not self.is_volume_path(ami_path): @@ -513,11 +513,11 @@ def to_sys_path(self, ami_path, env=None, strict=False): def from_sys_path(self, sys_path, strict=False): """Convert sys path to AmiPath - If sys_path is not absolute then resolve it first. + If sys_path is not absolute then resolve it first. - If path is not mappable return None or - if strict mode is True then raise an SysPathError - """ + If path is not mappable return None or + if strict mode is True then raise an SysPathError + """ ami_path = self.vol_mgr.sys_to_ami_path(sys_path) if ami_path: return AmiPath(ami_path) @@ -529,17 +529,17 @@ def from_sys_path(self, sys_path, strict=False): def resolve_esc_sys_path(self, ami_path, strict=False): """resolve an escaped sys path inside an ami path - An escaped sys path starts with a '::' and an absolute or relative sys - path. If an escaped path is detected then we try to convert this - path from sys to ami path and return it. + An escaped sys path starts with a '::' and an absolute or relative sys + path. If an escaped path is detected then we try to convert this + path from sys to ami path and return it. - Any amiga path is returned untouched. + Any amiga path is returned untouched. - If the sys path cannot be resolved then return None - or raise a SysPathError is strict is True. + If the sys path cannot be resolved then return None + or raise a SysPathError is strict is True. - An empty sys path raises an AmiPathError - """ + An empty sys path raises an AmiPathError + """ if type(ami_path) is str: ami_path = AmiPath(ami_path) ap = str(ami_path) diff --git a/amitools/vamos/path/spec.py b/amitools/vamos/path/spec.py index 1f303db4..54f69c22 100644 --- a/amitools/vamos/path/spec.py +++ b/amitools/vamos/path/spec.py @@ -4,9 +4,9 @@ class Spec(object): """a volume or assign specifier string parsed into components - format: - name:[+][src][+src][?key=val,key=val] - """ + format: + name:[+][src][+src][?key=val,key=val] + """ def __init__(self, name, src_list=None, cfg=None, append=False): if cfg is None: diff --git a/amitools/vamos/path/vamos.py b/amitools/vamos/path/vamos.py index 0ce21a10..30b6e775 100644 --- a/amitools/vamos/path/vamos.py +++ b/amitools/vamos/path/vamos.py @@ -7,8 +7,8 @@ class VamosPathManager(PathManager): """The VamosPathManager keeps the old vamos path manager API (for now) - but has already the new PathManager under the hood. - """ + but has already the new PathManager under the hood. + """ def _get_lock_env(self, lock): cmd_paths = self.get_default_env().get_cmd_paths() @@ -23,7 +23,7 @@ def _get_lock_env(self, lock): def ami_command_to_sys_path(self, cwd_lock, ami_path): """lookup a command on path if it does not contain a relative or - absolute path. otherwise perform normal 'ami_to_sys_path' conversion""" + absolute path. otherwise perform normal 'ami_to_sys_path' conversion""" env = self._get_lock_env(cwd_lock) cmd_paths = self.cmdpaths(ami_path, env=env) log_path.info( diff --git a/amitools/vamos/path/volume.py b/amitools/vamos/path/volume.py index 2905fcae..56138bc4 100644 --- a/amitools/vamos/path/volume.py +++ b/amitools/vamos/path/volume.py @@ -8,10 +8,10 @@ def resolve_sys_path(sys_path): """replace ~ (home) or environment variables in path and - make path absolute + make path absolute - return resolved path - """ + return resolved path + """ # expand system path sys_path = os.path.expanduser(sys_path) sys_path = os.path.expandvars(sys_path) @@ -305,10 +305,10 @@ def get_all_names(self): def sys_to_ami_path(self, sys_path): """try to map an absolute system path back to an amiga path - if multiple volumes overlap then take the shortest amiga path + if multiple volumes overlap then take the shortest amiga path - return ami_path or None if sys_path can't be mapped - """ + return ami_path or None if sys_path can't be mapped + """ if not os.path.isabs(sys_path): sys_path = resolve_sys_path(sys_path) log_path.debug("vol: sys_to_ami_path: resolved rel path: %s", sys_path) @@ -339,22 +339,22 @@ def sys_to_ami_path(self, sys_path): def ami_to_sys_path(self, ami_path, fast=False): """Map an Amiga path to a system path. - An absolute Amiga path with volume prefix is expected. - Any other path returns None. + An absolute Amiga path with volume prefix is expected. + Any other path returns None. - If volume does not exist also return None. + If volume does not exist also return None. - It replaces the volume with the sys_path prefix. - Furthermore, the remaining Amiga path is mapped to - the system file system and case corrected if a - corresponding entry is found. + It replaces the volume with the sys_path prefix. + Furthermore, the remaining Amiga path is mapped to + the system file system and case corrected if a + corresponding entry is found. - If 'fast' mode is enabled then the original case - of the path elements is kept if the underlying FS - is case insensitive. + If 'fast' mode is enabled then the original case + of the path elements is kept if the underlying FS + is case insensitive. - Return None on error or system path - """ + Return None on error or system path + """ # find volume pos = ami_path.find(":") if pos <= 0: diff --git a/amitools/vamos/schedule/scheduler.py b/amitools/vamos/schedule/scheduler.py index 50498016..dd58908a 100644 --- a/amitools/vamos/schedule/scheduler.py +++ b/amitools/vamos/schedule/scheduler.py @@ -22,11 +22,11 @@ def get_num_tasks(self): def schedule(self): """main work call for scheduler. at least one task must be added. - terminates if there are no more tasks to schedule or if a task - failed. + terminates if there are no more tasks to schedule or if a task + failed. - return None or failed task - """ + return None or failed task + """ if self.last_task is None: raise RuntimeError("no task was added!") return self.fail_task @@ -34,10 +34,10 @@ def schedule(self): def add_task(self, task): """add a new task and prepare for execution. - currently the task is also executed in place here. + currently the task is also executed in place here. - returns True if task was added - """ + returns True if task was added + """ self.tasks.append(task) self.last_task = task # prepare to run task @@ -67,11 +67,11 @@ def rem_task(self, task): def run_sub_task(self, sub_task): """run a given sub task in the context of the current task - for now the sub task directly runs in the current task - and returns when its finished + for now the sub task directly runs in the current task + and returns when its finished - returns return regs of sub task - """ + returns return regs of sub task + """ if len(self.tasks) == 0: raise ValueError("no tasks are running!") # prepare run of sub task diff --git a/amitools/vamos/tools/type.py b/amitools/vamos/tools/type.py index 4d27b21d..2d43122c 100644 --- a/amitools/vamos/tools/type.py +++ b/amitools/vamos/tools/type.py @@ -1,7 +1,7 @@ import sys from .tool import Tool -from amitools.vamos.astructs import AmigaStruct +from amitools.vamos.astructs import AmigaStructTypes, TypeDumper from amitools.vamos.cfgcore import parse_scalar @@ -29,46 +29,56 @@ def run(self, args): type_cmd = args.type_cmd # list if type_cmd == "list": - type_names = AmigaStruct.get_all_struct_names() + type_names = AmigaStructTypes.get_all_struct_names() for tn in sorted(type_names): print(tn) return 0 # dump elif type_cmd == "dump": name = args.type_name - s = AmigaStruct.find_struct(name) + s = AmigaStructTypes.find_struct(name) if s is None: print("type '%s' not found!" % name, file=sys.stderr) return 1 else: - s.dump_type() + td = TypeDumper() + td.dump(s) return 0 # lookup elif type_cmd == "lookup": name = args.type_name - s = AmigaStruct.find_struct(name) + s = AmigaStructTypes.find_struct(name) if s is None: print("type '%s' not found!" % name, file=sys.stderr) return 1 else: field_path = args.type_field_path - try: - fields = s.get_fields_by_path(field_path) - return self._dump_fields(field_path, fields) - except KeyError as e: - print("Field not found:", e, file=sys.stderr) + field_names = field_path.split(".") + field_defs = s.sdef.find_sub_field_defs_by_name(*field_names) + if field_defs: + td = TypeDumper() + td.dump_fields(*field_defs) + return 0 + else: + print("Field not found:", field_path, file=sys.stderr) return 1 # offset elif type_cmd == "offset": name = args.type_name - s = AmigaStruct.find_struct(name) + s = AmigaStructTypes.find_struct(name) if s is None: print("type '%s' not found!" % name, file=sys.stderr) return 1 else: offset = parse_scalar(int, args.type_offset) - fields = s.get_fields_by_offset(offset) - return self._dump_fields("@%04x" % offset, fields) + field_defs, delta = s.sdef.find_sub_field_defs_by_offset(offset) + if field_defs: + td = TypeDumper() + td.dump_fields(*field_defs) + return 0 + else: + print("No Field found at", offset, file=sys.stderr) + return 1 def _dump_fields(self, where, fields): if len(fields) == 0: diff --git a/amitools/vamos/trace/mgr.py b/amitools/vamos/trace/mgr.py index a57a02cd..434b37b0 100644 --- a/amitools/vamos/trace/mgr.py +++ b/amitools/vamos/trace/mgr.py @@ -165,15 +165,17 @@ def _get_label_extra(self, label, mode, addr, width, value): return "", "" def _get_struct_extra(self, label, addr, width): - delta = addr - label.struct_begin - if delta >= 0 and delta < label.struct_size: - struct = label.struct(None, addr) - st, field, f_delta = struct.get_struct_field_for_offset(delta) - - type_name = struct.get_type_name() - name = st.get_field_path_name(field) - type_sig = field.type_sig - addon = "%s+%d = %s(%s)+%d" % (type_name, delta, name, type_sig, f_delta) + offset = addr - label.struct_begin + if offset >= 0 and offset < label.struct_size: + # find sub fields and delta + struct = label.struct + sub_field_defs, delta = label.struct.sdef.find_sub_field_defs_by_offset( + offset + ) + type_name = struct.sdef.get_type_name() + name = ".".join(map(lambda x: x.name, sub_field_defs)) + type_sig = sub_field_defs[-1].type.get_signature() + addon = "%s+%d = %s(%s)+%d" % (type_name, offset, name, type_sig, delta) return "Struct", addon else: return "", "" diff --git a/docs/conf.py b/docs/conf.py index 1e493764..8e99cfd8 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -5,8 +5,8 @@ copyright = "2020, Christian Vogelgsang" author = "Christian Vogelgsang" -version = "0.5" -release = "0.5.0" +version = "0.6" +release = "0.6.0" # -- General configuration --------------------------------------------------- extensions = [ diff --git a/setup.py b/setup.py index 45a02230..9d7c1cdd 100644 --- a/setup.py +++ b/setup.py @@ -44,7 +44,7 @@ ext_file = "musashi/emu.c" if not os.path.exists(ext_file) and not use_cython: print("generated cython file missing! cython is essential to proceed!") - print("please install with: pip install cython") + print("please install with: pip3 install cython") sys.exit(1) @@ -183,7 +183,7 @@ def run(self): "musashi/softfloat/milieu.h", "musashi/softfloat/softfloat.h", "musashi/softfloat/softfloat-macros", - "musashi/softfloat/softfloat-specialize" + "musashi/softfloat/softfloat-specialize", ] inc_dirs = ["musashi", "gen"] @@ -234,7 +234,7 @@ def run(self): description="A package to support development with classic Amiga m68k systems", long_description=open("README.md").read(), long_description_content_type="text/markdown", - version="0.5.0", + version="0.6.0", maintainer="Christian Vogelgsang", maintainer_email="chris@vogelgsang.org", url="http://github.com/cnvogelg/amitools", @@ -255,5 +255,5 @@ def run(self): # win problems: # use_scm_version=True, include_package_data=True, - python_requires="~=3.5", + python_requires="~=3.6", ) diff --git a/test/Makefile b/test/Makefile index b2d42b0a..fdc8ef92 100644 --- a/test/Makefile +++ b/test/Makefile @@ -17,7 +17,7 @@ SOURCES_C += math_double.c math_double_trans.c SOURCES_C += math_single.c math_single_trans.c SOURCES_C += math_fast.c math_fast_trans.c SOURCES_C += test_hello.c test_raise.c -SOURCES_C += test_devtimer.c +SOURCES_C += test_devtimer.c test_execpy.c SOURCES_C += lib_testnix.c lib_testsc.c SOURCES_C += proc_args.c diff --git a/test/bin/test_execpy_agcc b/test/bin/test_execpy_agcc new file mode 100755 index 0000000000000000000000000000000000000000..8084940c2c647e9c27ee134be12a6e1abc2f0030 GIT binary patch literal 9440 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 literal 0 HcmV?d00001 diff --git a/test/bin/test_execpy_gcc b/test/bin/test_execpy_gcc new file mode 100755 index 0000000000000000000000000000000000000000..b81ea7f61fef23e056952d901772200bd16eb81e GIT binary patch literal 2364 zcmZWrZERCj7=CYi!C5LP3*rD{ZtiZzBz2u+F@r3o>zHn}-YxC;h)mnNwY@8DZPSlA z2+`338N?_W!VlsPei(iz5@Uc2e-OmQkeJ{!{2+utLXdBqL4rbN>+_!5eGpIb+;h%z z-mmk%@4YRN{V%eHy)xT~q%~Nt0G1HhZ}YzP9Cl5$XYj1A{RPiP_Xx?g=dp6vC8eeQ zujy!iokLSy8`o_4D8Fipz>u-+yA)iQVJ7qYKB2 z{kA$;SsA}}qPV(vvJgM7=ZB4{v75DR+O^rK9Zu_HrI$fUphN^zOkC;ExNaaYqmKLJ# zQ*7ER<$v5meLZ`}i3YJ@Hf5uY_ zTn#w&yvAOeME>j3k`@1CL0yD9_Xr&se%AO4@ek8g!&2>!te=rOTKQbFa-u=PrLY}2 zEqz-YtZutQaz;|mxoYCm!JQ{}_C2ga{1Pel3}S)aC@Ou$iclrZ4(<%sJu~v%C_3x3 z8nog7uP-dJW@JL~4olV5P8czhE@>gr-%cF#_raG0RyDm^r|XAM!Q5EATu3?Rr24M= zYJx@F;dEM>EdD+gp>lKkZIU2e?|PXe)gRpJIwN*VU{y66@LX+PW;SvwxGz+QoOKn_ zHr5RmBUF0gbIse`vuv@C$RYpdaVj0@7@?)2+oK&f6;RM#U8CU&Zk!CIv zyQNA|op0{+`r>LeA+VXK_3p%jbzd!y+^(zA;S6_@zcWf{3^EmVa!XDm$@Kap#A#5%n<}zoUiSVLcz1k|dH< zuYY-bI<%KP>Uq2|_i&p90FPd@%SLd1#E5Q`cY%8jkN`9td9h>}@X~$&UqRbe$Y+2t zpadO!E9Fms_aMIt!1t35R^}?qivgU|I$#)pFX{&1m(oBD?BZK1H3N{BUI)GeF2WRA z&IaBN+x9`=0Qf85Uc!7Ga|5_9u>KS4J(x?_9|gvN3G5#M+p+FJe27Cn41XAQ$dfG` zj*TR8f{2QJHkau)n2?WG&U3L5vB$kh4u`|ZRDWt{$Vf&96Ui8C^Wg&UQ6pzC!kO4$ zsy_>sWY&R?@Q{&6GVznxXLCk|wXDH56Uju*`WPo-(6m3m5%V0}JYF6H4}17}i}6BEj#C&=$Vzcd+K)I<7ftErwjt*^E%w@2hjXxHEVWXN8w? zZC(0050BOjJzbu*Kv$^Uv#GVSr=o|+e!VZZ8JxCFN@51pg5DO1c zGtNt((+7ll!eQ$`9D>j0qKVW8R<6AuS+;$1XU}|L^b>NpzANN2vZ$Z9FUxi6Ps?dI zvx7I9M0v2Ik2q)CGhAmT2CRK{X3rc3=f!>(3;yrGeFi(5e6eKzz}s1T;fWML4sYZi z;vW1ES0WMbn&=lJWAbZ490U7HaxyU1103s2ZDXRVtlYszUp9*r%xf-NIJ7NWvF?U0 p-zQ`Y$3op~&C2(Dd!nPkHv!zYv7`utnk#$6)sLm95hnYC{{U27FL?j} literal 0 HcmV?d00001 diff --git a/test/bin/test_execpy_gcc_dbg b/test/bin/test_execpy_gcc_dbg new file mode 100755 index 0000000000000000000000000000000000000000..9197abb97728c67244a65d0b28b6375e1c864285 GIT binary patch literal 21344 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 literal 0 HcmV?d00001 diff --git a/test/bin/test_execpy_vc b/test/bin/test_execpy_vc new file mode 100755 index 0000000000000000000000000000000000000000..5f300f61e93fe7f8ab989e644d6486291725cdd0 GIT binary patch literal 1352 zcmZWpZ)hAv6#wn+B(B#bZjZCpSi*E&F3IJ*%co+)A)C7fug7JhiAcfH-o<9OHUBP` zOG!a+IfL8>K~0K?_@N(!ihqt$MJSZgLIqp;;VdGx2nj+6wie47Gz~WK&D}+!?(*i% zdo#azZ+`P;0OfbozvhN+0;E0Ee~d)_wdqjH7$it`d9z30rL`zRR_M*4xvm4ydhbF1?whV8)c9GkrVHFyE^&)@ zKwmBy<52r)2B#eK-SKV};crh$z10qA9DMNl=6bvXlE2kGay|y`{z2`d?)|U7faa#d z*9H@08*adVg=WNRGnoHMrW$`TbAvpg+Gv&9kkjgK4M8ji^bXud>yDoW#YQ_NN9@#&4Szu0cA&`OOBU~GaK@x1Y@Da(G20yCH z$og1BC+42xqv(0J)Uls9XXoaQ*^P_>1&zy<0j2d$r!;NtX*gl^6Ow?2MoM~lQE@00P?sX$YE5BvW zm{G8Lb&96cXH7)fil%%YMfQV%-=I-ULfTXHxLgj4cv|8C z6~f`%6U5N1T4d&i`6Tp1B~B5CyxJ^rK9@`w0>SDy;(VCP8Gp^v>F$C(YnT5@=cM$a zRoh!%K0le~lev+J6B90PKbak$oGy$^7q;kZ_W}QN!k(ha`4jXy$@}D(Vk;MwB^E>z zB|60r)d=0RR91 literal 0 HcmV?d00001 diff --git a/test/bin/test_execpy_vc_dbg b/test/bin/test_execpy_vc_dbg new file mode 100755 index 0000000000000000000000000000000000000000..e5b53a92de1db7bc2f996e07fe401ad15dcaf79d GIT binary patch literal 2116 zcmaJ?Z)h837=P~1X6(+I)i`UF^6qwIZQ3RMAj6d~lC;G(q}SQhDL8gX)8v}9O|rXm zwjg-aBK;t!+Yk{y?1Ql3pE6VgVaNs(wy_U&44EQyh-J7rtaXezT<7n-T<50Xh39?# zzR&Z#zxTNdk@N@Ep7kDIMe&JSxLb1^6>Djh!QsURi|Lr$zz2r+Ah={roa>J?2O5(}Pd00ZOcLi&x(A3Zr5Kc#F@rx+w1=*_0 z_#pKZMC&Q+xRBVGc(-?g{1K^r+-bl`&H;mF@YCQbZ1qZsGz#Q>tz&fA2^M77gPGAR z(dDoMc@wB9LW!e%zZPR5(o7$Vsea^`iQg6+)u%;?r0dXsYKxIvSYHifNT|cUW{?UB zZjI3gpd%hWK2wJFA+HYPcCs<*e0#2C2QZh9&yMI`M}exs#D&>ij)fbzGlV?hT5g(W zv4xqv&dDg<|O&F@9}#FyEN9{+Usn`Y;@{hq00q#JzS;C zX#5)O!IU^2e^a0Ay&|j+M@e!X;&X)a98N7}oJ_OoBDXs1`30i;{mGB{JY|n-v8-GP z=#$v@s+vURH}pv@LUL_7`B{VQPS{jf7w=2us~#SA>Yjf~GeU;CjbziuF~XRd7J3L+-wzAeV>3ykAqTVa4<2k$qL=1O9WCp7O`jSm$aE6IX zbIH$rRJf6zG3&Xe**?+_re_K(kqG%YBI2=^22zWPjPCdMUZIX8Y(#}%aGLXqu;nD@ zY=fz2V?gF$FwHg;ZeX{PoppB-M~`Yf$X*Xh@{sA%Ue*sL4;60lN^8}mixJAcH}D%N zsEM8W{#xA*H5j3QR;I?v6J0osJkrkVD?sWJi)}drSvEuQt0Xfi?YKUEOJA#`2`Z12 z*Wy=)nV4z7+e>oHwP9NSN>zl^Vzw)Y<*{Kd$D>xlki zBz-?7=vUP7zEk3MAjfB42b}=@jCw8buLEI9$PfQK>{nrL0)=6dK=`H!-$5J(Sv8&8 zrT0NcJY9yo3G_5{$ULXC4|EeM>dOJx@JP#8M8h!hQ^hJ5kJa>^no1ISfZy{XevcPF zku8=WN{9Jk7!%p*s8KArIiv+Q#*2XaB6K{`?^w2RJ=Lf8K(3FxVCOA6k{h*(cBEpJ z_vLd{JCdC!R?FpaJHjE2*jCOJ&_g~~nGWZ8sv16=18Gt`&cFFw<`{C0$)-m@J3S7Z zsUE~R|8Lj@;^!OmIPj#6AkH5Hjes~#;L-{R_W$9-c zYL+LfkeREer)@7cKeuGOn8=rMqy0AiRPgXKAkPEFM%Ze3++JHGpW;;&(&=;h{^6^a&KNpFhXdb{~sI)st(kh-tGzX2hU^ltzF literal 0 HcmV?d00001 diff --git a/test/conftest.py b/test/conftest.py index 5113eaf1..440ab335 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -31,12 +31,19 @@ def pytest_addoption(parser): help="run the debug versions of the Amiga binaries", ) parser.addoption( - "--dump-output", + "--dump-file", "-O", action="store_true", default=False, help="write all vamos output to 'vamos.log'", ) + parser.addoption( + "--dump-console", + "-C", + action="store_true", + default=False, + help="write all vamos output to stdout", + ) parser.addoption( "--gen-data", "-G", @@ -82,6 +89,12 @@ def pytest_addoption(parser): default=False, help="run full test suite and include all tests", ) + parser.addoption( + "--run-subproc", + action="store_true", + default=False, + help="run vamos binaries via subprocess and not directly inside pytest", + ) def pytest_configure(config): @@ -154,29 +167,37 @@ def buildlibsc(request): def vamos(request): """Run vamos with test programs""" dbg = request.config.getoption("--use-debug-bins") - dump = request.config.getoption("--dump-output") + dump_file = request.config.getoption("--dump-file") + dump_console = request.config.getoption("--dump-console") gen = request.config.getoption("--gen-data") auto_build = request.config.getoption("--auto-build") + run_subproc = request.config.getoption("--run-subproc") flavor = request.param return VamosTestRunner( flavor, use_debug_bins=dbg, - dump_output=dump, + dump_file=dump_file, + dump_console=dump_console, generate_data=gen, vamos_bin=VAMOS_BIN, vamos_args=VAMOS_ARGS, auto_build=auto_build, + run_subproc=run_subproc, ) @pytest.fixture(scope="module") def vrun(request): - return VamosRunner(vamos_bin=VAMOS_BIN, vamos_args=VAMOS_ARGS) + run_subproc = request.config.getoption("--run-subproc") + return VamosRunner( + vamos_bin=VAMOS_BIN, vamos_args=VAMOS_ARGS, run_subproc=run_subproc + ) @pytest.fixture(scope="module") -def toolrun(): - return ToolRunner() +def toolrun(request): + run_subproc = request.config.getoption("--run-subproc") + return ToolRunner(run_subproc=run_subproc) @pytest.fixture(scope="module", params=["mach", "mach-label", "mock", "mock-label"]) diff --git a/test/extra/shell_seg.py b/test/extra/shell_seg.py index 8fdaa443..8c8f45e2 100644 --- a/test/extra/shell_seg.py +++ b/test/extra/shell_seg.py @@ -10,12 +10,10 @@ def check_shell_seg(vrun): def shell_seg_endcli_test(vrun): check_shell_seg(vrun) stdin = "endcli\n" - retcode, stdout, stderr = vrun.run_prog( - "wb:l/Shell-Seg", vargs=["-x"], stdin=stdin.encode("latin-1") - ) - assert retcode == 212 - assert stdout == [b"\x0f0.SYS:> PROCESS 0 ENDING"] + retcode, stdout, stderr = vrun.run_prog("wb:l/Shell-Seg", vargs=["-x"], stdin=stdin) + assert stdout == ["\x0f0.SYS:> PROCESS 0 ENDING"] assert stderr == [] + assert retcode == 212 def shell_seg_proc_args_test(vrun, vamos): @@ -23,16 +21,14 @@ def shell_seg_proc_args_test(vrun, vamos): vamos.make_prog("proc_args") cmd_name = vamos.get_prog_bin_name("proc_args") stdin = cmd_name + "\nendcli\n" - retcode, stdout, stderr = vrun.run_prog( - "wb:l/Shell-Seg", vargs=["-x"], stdin=stdin.encode("latin-1") - ) - assert retcode == 212 + retcode, stdout, stderr = vrun.run_prog("wb:l/Shell-Seg", vargs=["-x"], stdin=stdin) assert stdout == [ - b"\x0f0.SYS:> a0:NULL", - b'in:"\\n"', - b"\x0f0.SYS:> PROCESS 0 ENDING", + "\x0f0.SYS:> a0:NULL", + 'in:"\\n"', + "\x0f0.SYS:> PROCESS 0 ENDING", ] assert stderr == [] + assert retcode == 212 def shell_seg_run_proc_args_test(vrun, vamos): @@ -40,9 +36,7 @@ def shell_seg_run_proc_args_test(vrun, vamos): vamos.make_prog("proc_args") cmd_name = vamos.get_prog_bin_name("proc_args") stdin = "run " + cmd_name + "\nendcli\n" - retcode, stdout, stderr = vrun.run_prog( - "wb:l/Shell-Seg", vargs=["-x"], stdin=stdin.encode("latin-1") - ) - assert retcode == 212 - assert stdout == [b"\x0f0.SYS:> \x0f0.SYS:> PROCESS 0 ENDING"] + retcode, stdout, stderr = vrun.run_prog("wb:l/Shell-Seg", vargs=["-x"], stdin=stdin) + assert stdout == ["\x0f0.SYS:> \x0f0.SYS:> PROCESS 0 ENDING"] assert stderr == [] + assert retcode == 212 diff --git a/test/helper/runner.py b/test/helper/runner.py index 32c56d5a..fd508496 100644 --- a/test/helper/runner.py +++ b/test/helper/runner.py @@ -1,8 +1,74 @@ import os +import sys import pytest import subprocess import hashlib +import inspect +import io +import importlib from .builder import BinBuilder +from amitools.vamos.main import main as vamos_main + + +def run_proc(args, stdin_str=None, raw_output=False): + if stdin_str: + stdin_bytes = stdin_str.encode("latin-1") + stdin_flag = subprocess.PIPE + else: + stdin_bytes = None + stdin_flag = None + + p = subprocess.Popen( + args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=stdin_flag + ) + (stdout, stderr) = p.communicate(stdin_bytes) + + # process stdout + if not raw_output: + stdout = stdout.decode("latin-1").splitlines() + stderr = stderr.decode("latin-1").splitlines() + returncode = p.returncode + return returncode, stdout, stderr + + +def run_func(func, args, stdin_str=None, raw_output=False): + """run a function with args but redirect stdin, stdout, stderr like + a subprocess would do""" + # redirect i/o stream + old_stdin = sys.stdin + if stdin_str: + stdin_bytes = stdin_str.encode("latin-1") + sys.stdin = io.TextIOWrapper(io.BytesIO(stdin_bytes)) + + # run vamos directly + old_stdout = sys.stdout + old_stderr = sys.stderr + stdout_bytes_io = io.BytesIO() + stderr_bytes_io = io.BytesIO() + new_stdout = io.TextIOWrapper(stdout_bytes_io) + new_stderr = io.TextIOWrapper(stderr_bytes_io) + sys.stdout = new_stdout + sys.stderr = new_stderr + + try: + returncode = func(args) + finally: + # restore i/o streams + sys.stdin = old_stdin + sys.stdout = old_stdout + sys.stderr = old_stderr + + new_stdout.flush() + new_stderr.flush() + stdout = stdout_bytes_io.getvalue() + stderr = stderr_bytes_io.getvalue() + + # process stdout + if not raw_output: + stdout = stdout.decode("latin-1").splitlines() + stderr = stderr.decode("latin-1").splitlines() + + return returncode, stdout, stderr class VamosTestRunner: @@ -12,16 +78,20 @@ def __init__( vamos_bin=None, vamos_args=None, use_debug_bins=False, - dump_output=False, + dump_file=False, + dump_console=False, generate_data=False, auto_build=False, + run_subproc=False, ): self.flavor = flavor self.vamos_bin = vamos_bin self.vamos_args = vamos_args self.use_debug_bins = use_debug_bins - self.dump_output = dump_output + self.dump_file = dump_file + self.dump_console = dump_console self.generate_data = generate_data + self.run_subproc = run_subproc self.bin_builder = BinBuilder(flavor, use_debug_bins, auto_build) def _get_data_path(self, prog_name, kw_args): @@ -44,16 +114,16 @@ def get_prog_bin_name(self, prog_name): def run_prog(self, *prog_args, **kw_args): """run an AmigaOS binary with vamos - kw_args: - - stdin = string for stdin - - no_ts = no timestamps - - variant = a postfix string to append to data file + kw_args: + - stdin = string for stdin + - no_ts = no timestamps + - variant = a postfix string to append to data file - returns: - - returncode of process - - stdout as line array - - stderr as line array - """ + returns: + - returncode of process + - stdout as line array + - stderr as line array + """ # ensure that prog exists self.make_prog(prog_args[0]) @@ -77,6 +147,9 @@ def run_prog(self, *prog_args, **kw_args): if "vargs" in kw_args: args = args + kw_args["vargs"] + # terminate args + args.append("--") + # built binaries have special prog names prog_name = self.get_prog_bin_name(prog_args[0]) args.append(prog_name) @@ -85,29 +158,39 @@ def run_prog(self, *prog_args, **kw_args): # run and get stdout/stderr print("running:", " ".join(args)) - if stdin: - stdin_flag = subprocess.PIPE - stdin = stdin.encode("latin-1") + if self.run_subproc: + returncode, stdout, stderr = run_proc(args, stdin) else: - stdin_flag = None - p = subprocess.Popen( - args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=stdin_flag - ) - (stdout, stderr) = p.communicate(stdin) - # process stdout - stdout = stdout.decode("latin-1").splitlines() - stderr = stderr.decode("latin-1").splitlines() + def run(args): + return vamos_main(args=args) + + returncode, stdout, stderr = run_func(run, args[1:], stdin) # show? - if self.dump_output: - fh = open("vamos.log", "w+") + if self.dump_file: + fh = open("vamos.log", "a") fh.write(" ".join(args) + "\n") + fh.write("---stdout---\n") + for line in stdout: + fh.write(line) + fh.write("\n") + fh.write("---stderr---\n") for line in stdout: fh.write(line) fh.write("\n") + fh.write("---end---\n") fh.close() + if self.dump_console: + print("---stdout---") + for line in stdout: + print(line) + print("---stderr---") + for line in stderr: + print(line) + print("---end---") + # generate data? if self.generate_data: dat_path = self._get_data_path(prog_args[0], kw_args) @@ -117,7 +200,7 @@ def run_prog(self, *prog_args, **kw_args): f.write(line + "\n") f.close() - return (p.returncode, stdout, stderr) + return (returncode, stdout, stderr) def run_prog_checked(self, *prog_args, **kw_args): """like run_prog() but check return value and assume its 0""" @@ -134,7 +217,7 @@ def _compare(self, got, ok): def run_prog_check_data(self, *prog_args, **kw_args): """like run_prog_checked() but also verify the stdout - and compare with the corresponding data file of the suite""" + and compare with the corresponding data file of the suite""" stdout, stderr = self.run_prog_checked(*prog_args, **kw_args) # compare stdout with data dat_path = self._get_data_path(prog_args[0], kw_args) @@ -147,11 +230,21 @@ def run_prog_check_data(self, *prog_args, **kw_args): # asser stderr to be empty assert stderr == [] + def run_ctx_func(self, ctx_func, **kw_args): + """use test_execpy binary to run the given func in a lib context""" + func_name = ctx_func.__name__ + # get source file + src_file = inspect.getfile(ctx_func) + # now run 'test_execpy' + prog_args = ["test_execpy", "-c", src_file, func_name] + return self.run_prog(*prog_args, **kw_args) + class VamosRunner: - def __init__(self, vamos_bin=None, vamos_args=None): + def __init__(self, vamos_bin=None, vamos_args=None, run_subproc=False): self.vamos_bin = vamos_bin self.vamos_args = vamos_args + self.run_subproc = run_subproc def run_prog(self, *prog_args, **kw_args): # stdin given? @@ -178,19 +271,14 @@ def run_prog(self, *prog_args, **kw_args): # run and get stdout/stderr print("running:", " ".join(args)) print("stdin:", stdin) - if stdin: - stdin_flag = subprocess.PIPE + if self.run_subproc: + return run_proc(args, stdin) else: - stdin_flag = None - p = subprocess.Popen( - args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=stdin_flag - ) - (stdout, stderr) = p.communicate(stdin) - # process stdout - stdout = stdout.splitlines() - stderr = stderr.splitlines() - return (p.returncode, stdout, stderr) + def run(args): + return vamos_main(args=args) + + return run_func(run, args[1:], stdin) def _get_sha1(self, file_name): h = hashlib.sha1() @@ -218,6 +306,9 @@ def add_volume_or_skip(self, vol_name, vol_path): class ToolRunner: + def __init__(self, run_subproc=False): + self.run_subproc = run_subproc + def run_checked(self, tool, *prog_args, raw_output=False, return_code=0): ret_code, stdout, stderr = self.run(tool, *prog_args, raw_output=raw_output) if stderr: @@ -237,14 +328,18 @@ def run(self, tool, *prog_args, raw_output=False): pytest.skip("tool not found: " + tool_path) args = [tool_path] + list(prog_args) print("running tool:", " ".join(args)) - p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - (stdout, stderr) = p.communicate() - - # process stdout - if not raw_output: - stdout = stdout.decode("latin-1").splitlines() - stderr = stderr.decode("latin-1").splitlines() - return (p.returncode, stdout, stderr) + + if self.run_subproc: + return run_proc(args, raw_output=raw_output) + else: + # find tool func + tool_mod_name = "amitools.tools." + tool + tool_mod = importlib.import_module(tool_mod_name) + + def run(args): + return tool_mod.main(args=args) + + return run_func(run, args[1:], raw_output=raw_output) def _get_sha1(self, file_name): h = hashlib.sha1() diff --git a/test/include/Makefile b/test/include/Makefile index 3759e75b..3e5b7b2e 100644 --- a/test/include/Makefile +++ b/test/include/Makefile @@ -6,7 +6,7 @@ ARCH=m68k # the tools FD2PRAGMA=fd2pragma -FD2INLINE=fd2inline +SFDC=sfdc # the source file FD=../../amitools/data/fd/$(BASE)_lib.fd @@ -18,10 +18,11 @@ GCC_INLINE=inline/$(BASE).h AGCC_DEFINE=defines/$(BASE).h PROTO=proto/$(BASE).h PRAGMA=pragma/$(BASE).h +SFD=sfd/$(BASE)_lib.sfd FD_OPTS=-a $(ARCH) -i $(FD) -c $(CLIB) -l $(LIBTYPE) -FILES=$(VC_INLINE) $(GCC_INLINE) $(AGCC_DEFINE) $(PROTO) $(PRAGMA) +FILES=$(VC_INLINE) $(GCC_INLINE) $(AGCC_DEFINE) $(PROTO) $(PRAGMA) $(SFD) all: $(MAKE) lib BASE=vamostest LIBTYPE=VamosTestBase @@ -36,14 +37,17 @@ lib: $(FILES) clean-lib: rm -f $(FILES) +$(SFD): $(FD) + $(FD2PRAGMA) $(FD_OPTS) -s 112 -t sfd + $(VC_INLINE): $(FD) $(FD2PRAGMA) $(FD_OPTS) -s 70 -t inline $(GCC_INLINE): $(FD) $(FD2PRAGMA) $(FD_OPTS) -s 47 -t inline -$(AGCC_DEFINE): $(FD) - $(FD2INLINE) --target=m68k-aros --mode=new $(FD) $(CLIB) -o $@ +$(AGCC_DEFINE): $(SFD) + $(SFDC) --target=m68k-aros --mode=macros $(SFD) -o $@ $(PROTO): $(FD) $(FD2PRAGMA) $(FD_OPTS) -s 38 -t proto diff --git a/test/include/clib/vamostest_protos.h b/test/include/clib/vamostest_protos.h index bb140b84..097ac019 100644 --- a/test/include/clib/vamostest_protos.h +++ b/test/include/clib/vamostest_protos.h @@ -15,6 +15,7 @@ ULONG Add(ULONG a, ULONG b); ULONG Swap(ULONG a, ULONG b); ULONG Dummy(ULONG a, ULONG b); VOID RaiseError(STRPTR error); +ULONG ExecutePy(ULONG argc, STRPTR *argv); #ifdef __cplusplus } diff --git a/test/include/defines/testnix.h b/test/include/defines/testnix.h index 53953467..5d04058d 100644 --- a/test/include/defines/testnix.h +++ b/test/include/defines/testnix.h @@ -1,8 +1,17 @@ -/* Automatically generated header! Do not edit! */ +/* Automatically generated header (sfdc 1.11)! Do not edit! */ #ifndef _INLINE_TESTNIX_H #define _INLINE_TESTNIX_H +#ifndef _SFDC_VARARG_DEFINED +#define _SFDC_VARARG_DEFINED +#ifdef __HAVE_IPTR_ATTR__ +typedef APTR _sfdc_vararg __attribute__((iptr)); +#else +typedef ULONG _sfdc_vararg; +#endif /* __HAVE_IPTR_ATTR__ */ +#endif /* _SFDC_VARARG_DEFINED */ + #ifndef AROS_LIBCALL_H #include #endif /* !AROS_LIBCALL_H */ @@ -12,9 +21,9 @@ #endif /* !TESTNIX_BASE_NAME */ #define Add(___a, ___b) \ - AROS_LC2(ULONG, Add, \ - AROS_LCA(ULONG, (___a), D0), \ - AROS_LCA(ULONG, (___b), D1), \ - struct Library *, TESTNIX_BASE_NAME, 5, /* s */) + AROS_LC2(ULONG, Add, \ + AROS_LCA(ULONG, (___a), D0), \ + AROS_LCA(ULONG, (___b), D1), \ + struct Library *, TESTNIX_BASE_NAME, 5, Testnix) #endif /* !_INLINE_TESTNIX_H */ diff --git a/test/include/defines/vamostest.h b/test/include/defines/vamostest.h index 20a0a714..a2caf44b 100644 --- a/test/include/defines/vamostest.h +++ b/test/include/defines/vamostest.h @@ -1,8 +1,17 @@ -/* Automatically generated header! Do not edit! */ +/* Automatically generated header (sfdc 1.11)! Do not edit! */ #ifndef _INLINE_VAMOSTEST_H #define _INLINE_VAMOSTEST_H +#ifndef _SFDC_VARARG_DEFINED +#define _SFDC_VARARG_DEFINED +#ifdef __HAVE_IPTR_ATTR__ +typedef APTR _sfdc_vararg __attribute__((iptr)); +#else +typedef ULONG _sfdc_vararg; +#endif /* __HAVE_IPTR_ATTR__ */ +#endif /* _SFDC_VARARG_DEFINED */ + #ifndef AROS_LIBCALL_H #include #endif /* !AROS_LIBCALL_H */ @@ -11,36 +20,42 @@ #define VAMOSTEST_BASE_NAME VamosTestBase #endif /* !VAMOSTEST_BASE_NAME */ -#define Add(___a, ___b) \ - AROS_LC2(ULONG, Add, \ - AROS_LCA(ULONG, (___a), D0), \ - AROS_LCA(ULONG, (___b), D1), \ - struct Library *, VAMOSTEST_BASE_NAME, 7, /* s */) - -#define Dummy(___a, ___b) \ - AROS_LC2(ULONG, Dummy, \ - AROS_LCA(ULONG, (___a), D0), \ - AROS_LCA(ULONG, (___b), D1), \ - struct Library *, VAMOSTEST_BASE_NAME, 9, /* s */) - #define PrintHello() \ - AROS_LC0(void, PrintHello, \ - struct Library *, VAMOSTEST_BASE_NAME, 5, /* s */) + AROS_LC0(VOID, PrintHello, \ + struct VamosTestBase *, VAMOSTEST_BASE_NAME, 5, Vamostest) #define PrintString(___str) \ - AROS_LC1(void, PrintString, \ - AROS_LCA(STRPTR, (___str), A0), \ - struct Library *, VAMOSTEST_BASE_NAME, 6, /* s */) + AROS_LC1(VOID, PrintString, \ + AROS_LCA(STRPTR, (___str), A0), \ + struct VamosTestBase *, VAMOSTEST_BASE_NAME, 6, Vamostest) -#define RaiseError(___str) \ - AROS_LC1(void, RaiseError, \ - AROS_LCA(STRPTR, (___str), A0), \ - struct Library *, VAMOSTEST_BASE_NAME, 10, /* s */) +#define Add(___a, ___b) \ + AROS_LC2(ULONG, Add, \ + AROS_LCA(ULONG, (___a), D0), \ + AROS_LCA(ULONG, (___b), D1), \ + struct VamosTestBase *, VAMOSTEST_BASE_NAME, 7, Vamostest) #define Swap(___a, ___b) \ - AROS_LC2(ULONG, Swap, \ - AROS_LCA(ULONG, (___a), D0), \ - AROS_LCA(ULONG, (___b), D1), \ - struct Library *, VAMOSTEST_BASE_NAME, 8, /* s */) + AROS_LC2(ULONG, Swap, \ + AROS_LCA(ULONG, (___a), D0), \ + AROS_LCA(ULONG, (___b), D1), \ + struct VamosTestBase *, VAMOSTEST_BASE_NAME, 8, Vamostest) + +#define Dummy(___a, ___b) \ + AROS_LC2(ULONG, Dummy, \ + AROS_LCA(ULONG, (___a), D0), \ + AROS_LCA(ULONG, (___b), D1), \ + struct VamosTestBase *, VAMOSTEST_BASE_NAME, 9, Vamostest) + +#define RaiseError(___str) \ + AROS_LC1(VOID, RaiseError, \ + AROS_LCA(STRPTR, (___str), A0), \ + struct VamosTestBase *, VAMOSTEST_BASE_NAME, 10, Vamostest) + +#define ExecutePy(___argc, ___argv) \ + AROS_LC2(ULONG, ExecutePy, \ + AROS_LCA(ULONG, (___argc), D0), \ + AROS_LCA(STRPTR *, (___argv), A0), \ + struct VamosTestBase *, VAMOSTEST_BASE_NAME, 11, Vamostest) #endif /* !_INLINE_VAMOSTEST_H */ diff --git a/test/include/inline/vamostest.h b/test/include/inline/vamostest.h index 2f314090..70087d0e 100644 --- a/test/include/inline/vamostest.h +++ b/test/include/inline/vamostest.h @@ -62,4 +62,13 @@ (_RaiseError__bn - 60))(_RaiseError__bn, _RaiseError_str); \ });}) +#define ExecutePy(argc, argv) ({ \ + ULONG _ExecutePy_argc = (argc); \ + STRPTR * _ExecutePy_argv = (argv); \ + ({ \ + register char * _ExecutePy__bn __asm("a6") = (char *) (VAMOSTEST_BASE_NAME);\ + ((ULONG (*)(char * __asm("a6"), ULONG __asm("d0"), STRPTR * __asm("a0"))) \ + (_ExecutePy__bn - 66))(_ExecutePy__bn, _ExecutePy_argc, _ExecutePy_argv); \ +});}) + #endif /* _INLINE_VAMOSTEST_H */ diff --git a/test/include/inline/vamostest_protos.h b/test/include/inline/vamostest_protos.h index 08f6f84d..b6057b3a 100644 --- a/test/include/inline/vamostest_protos.h +++ b/test/include/inline/vamostest_protos.h @@ -23,4 +23,7 @@ ULONG __Dummy(__reg("a6") struct VamosTestBase *, __reg("d0") ULONG a, __reg("d1 VOID __RaiseError(__reg("a6") struct VamosTestBase *, __reg("a0") STRPTR str)="\tjsr\t-60(a6)"; #define RaiseError(str) __RaiseError(VamosTestBase, (str)) +ULONG __ExecutePy(__reg("a6") struct VamosTestBase *, __reg("d0") ULONG argc, __reg("a0") STRPTR * argv)="\tjsr\t-66(a6)"; +#define ExecutePy(argc, argv) __ExecutePy(VamosTestBase, (argc), (argv)) + #endif /* _VBCCINLINE_VAMOSTEST_H */ diff --git a/test/include/pragma/vamostest_lib.h b/test/include/pragma/vamostest_lib.h index 8b124903..4428985d 100644 --- a/test/include/pragma/vamostest_lib.h +++ b/test/include/pragma/vamostest_lib.h @@ -12,6 +12,7 @@ #pragma amicall(VamosTestBase,0x030,Swap(d0,d1)) #pragma amicall(VamosTestBase,0x036,Dummy(d0,d1)) #pragma amicall(VamosTestBase,0x03c,RaiseError(a0)) +#pragma amicall(VamosTestBase,0x042,ExecutePy(d0,a0)) #endif #if defined(_DCC) || defined(__SASC) #pragma libcall VamosTestBase PrintHello 01e 00 @@ -20,6 +21,7 @@ #pragma libcall VamosTestBase Swap 030 1002 #pragma libcall VamosTestBase Dummy 036 1002 #pragma libcall VamosTestBase RaiseError 03c 801 +#pragma libcall VamosTestBase ExecutePy 042 8002 #endif #endif /* _INCLUDE_PRAGMA_VAMOSTEST_LIB_H */ diff --git a/test/include/sfd/testnix_lib.sfd b/test/include/sfd/testnix_lib.sfd new file mode 100644 index 00000000..1eddf79f --- /dev/null +++ b/test/include/sfd/testnix_lib.sfd @@ -0,0 +1,10 @@ +==id $Id: testnix_lib.sfd,v 1.0 2021/03/17 22:04:32 noname Exp $ +* "testnix.library" +==base _TestNixBase +==basetype struct Library * +==libname testnix.library +==bias 30 +==public +==include +ULONG Add(ULONG a, ULONG b) (d0,d1) +==end diff --git a/test/include/sfd/vamostest_lib.sfd b/test/include/sfd/vamostest_lib.sfd new file mode 100644 index 00000000..1d9c143b --- /dev/null +++ b/test/include/sfd/vamostest_lib.sfd @@ -0,0 +1,16 @@ +==id $Id: vamostest_lib.sfd,v 1.0 2021/03/17 22:04:32 noname Exp $ +* "vamostest.library" +==base _VamosTestBase +==basetype struct VamosTestBase * +==libname vamostest.library +==bias 30 +==public +==include +VOID PrintHello() () +VOID PrintString(STRPTR str) (a0) +ULONG Add(ULONG a, ULONG b) (d0,d1) +ULONG Swap(ULONG a, ULONG b) (d0,d1) +ULONG Dummy(ULONG a, ULONG b) (d0,d1) +VOID RaiseError(STRPTR str) (a0) +ULONG ExecutePy(ULONG argc, STRPTR * argv) (d0,a0) +==end diff --git a/test/src/test_execpy.c b/test/src/test_execpy.c new file mode 100644 index 00000000..6091ff84 --- /dev/null +++ b/test/src/test_execpy.c @@ -0,0 +1,19 @@ +#include +#include +#include +#include + +struct VamosTestBase *VamosTestBase; + +int main(int argc, char *argv[]) +{ + if ((VamosTestBase = (struct VamosTestBase *)OpenLibrary("vamostest.library", 23))) + { + int result = ExecutePy(argc - 1, (STRPTR *)(argv + 1)); + CloseLibrary((struct Library *)VamosTestBase); + return result; + } else { + PutStr("Error opening 'vamostest.library'\n"); + return 1; + } +} diff --git a/test/suite/dos_examine.py b/test/suite/dos_examine.py index 60cad0e7..2383bd5f 100644 --- a/test/suite/dos_examine.py +++ b/test/suite/dos_examine.py @@ -15,14 +15,9 @@ def dos_examine_test(vamos, tmpdir): assert rc == 0 assert stderr == [] # allow any order of foo or bar - assert stdout == [ - 'Examine: bla', - ' 14 foo', - ' bar', - 'ok' - ] or stdout == [ - 'Examine: bla', - ' bar', - ' 14 foo', - 'ok' + assert stdout == ["Examine: bla", " 14 foo", " bar", "ok"] or stdout == [ + "Examine: bla", + " bar", + " 14 foo", + "ok", ] diff --git a/test/suite/dos_stdout.py b/test/suite/dos_stdout.py index e6a20db1..f6ca4eed 100644 --- a/test/suite/dos_stdout.py +++ b/test/suite/dos_stdout.py @@ -5,7 +5,4 @@ def dos_stdout_test(vamos): rc, stdout, stderr = vamos.run_prog("dos_stdout") assert rc == 0 assert stderr == [] - assert stdout == [ - "Hello, world!?", - "Hello, world!?" - ] + assert stdout == ["Hello, world!?", "Hello, world!?"] diff --git a/test/suite/dos_system.py b/test/suite/dos_system.py index a3ca55bc..889ead2d 100644 --- a/test/suite/dos_system.py +++ b/test/suite/dos_system.py @@ -9,4 +9,4 @@ def dos_system_test(vamos): def dos_system_not_found_test(vamos): rc, stdout, stderr = vamos.run_prog("dos_system", "bla hello!") - assert rc & 255 == 255 + assert rc == 255 diff --git a/test/suite/test_execpy.py b/test/suite/test_execpy.py new file mode 100644 index 00000000..55a4d96f --- /dev/null +++ b/test/suite/test_execpy.py @@ -0,0 +1,65 @@ +def test_execpy_exec_test(vamos): + # execute command and set return value + code = "rc = 42 ; print('hello, world!')" + retcode, stdout, stderr = vamos.run_prog("test_execpy", "-x", code) + assert retcode == 42 + assert stdout == ["hello, world!"] + assert stderr == [] + + +def test_execpy_eval_test(vamos): + # execute function an use its value as return value + code = "2 * 21" + retcode, stdout, stderr = vamos.run_prog("test_execpy", "-e", code) + assert retcode == 42 + assert stdout == [] + assert stderr == [] + + +def test_execpy_file_test(vamos, tmpdir): + # execute file + code = """rc = 42 +print('hello, world!')""" + script_file = tmpdir / "script" + script_file.write_text(code, "utf-8") + retcode, stdout, stderr = vamos.run_prog("test_execpy", "-f", str(script_file)) + assert retcode == 42 + assert stdout == ["hello, world!"] + assert stderr == [] + + +def test_execpy_ctx_func_test(vamos, tmpdir): + # execute file + code = """def foobar(ctx): + print('hello, world!') + print(type(ctx)) + return 42 +""" + script_file = tmpdir / "script" + script_file.write_text(code, "utf-8") + retcode, stdout, stderr = vamos.run_prog( + "test_execpy", "-c", str(script_file), "foobar" + ) + assert retcode == 42 + assert stdout == ["hello, world!", ""] + assert stderr == [] + + +def foobar(ctx): + """the test ctx_func""" + print("hello, world!") + print(type(ctx)) + print(sorted(ctx.__dict__)) + return 42 + + +def test_execpy_vamos_ctx_func_test(vamos): + # now use the vamos helper to run a function in lib ctx + retcode, stdout, stderr = vamos.run_ctx_func(foobar) + assert retcode == 42 + assert stdout == [ + "hello, world!", + "", + "['cpu', 'machine', 'mem', 'vlib']", + ] + assert stderr == [] diff --git a/test/tools/typetool.py b/test/tools/typetool.py index 94cbea8f..bf7f83a0 100644 --- a/test/tools/typetool.py +++ b/test/tools/typetool.py @@ -3,3 +3,30 @@ def typetool_list_test(toolrun): assert status == 0 assert err == [] assert "ExecLibrary" in out + + +def typetool_dump_test(toolrun): + status, out, err = toolrun.run("typetool", "dump", "ExecLibrary") + assert status == 0 + assert err == [] + assert "ExecLibrary" in out[0] + + +def typetool_lookup_test(toolrun): + status, out, err = toolrun.run( + "typetool", "lookup", "ExecLibrary", "ex_MemHandlers.mlh_Tail" + ) + assert status == 0 + assert err == [] + assert "ExecLibrary" in out[0] + assert "ex_MemHandlers" in out[1] + assert "mlh_Tail" in out[2] + + +def typetool_offset_test(toolrun): + status, out, err = toolrun.run("typetool", "offset", "ExecLibrary", "622") + assert status == 0 + assert err == [] + assert "ExecLibrary" in out[0] + assert "ex_MemHandlers" in out[1] + assert "mlh_Tail" in out[2] diff --git a/test/tools/xdftool.py b/test/tools/xdftool.py index 2a5d1b21..22fcbb93 100644 --- a/test/tools/xdftool.py +++ b/test/tools/xdftool.py @@ -62,7 +62,9 @@ def tag_full(value): TEST_TREES = { "simple": {"foo": {}, "bar": b"Hello, world!"}, - "deep": {"foo": {"bar": {"baz": {"hello": b"Hello, world!"}}},}, + "deep": { + "foo": {"bar": {"baz": {"hello": b"Hello, world!"}}}, + }, "data": {"bytes": DATA_bytes, "10k": DATA_10k, "100k": DATA_100k}, "latin": {"H\u00e4ll\u00f6": DATA_bytes, "D\u00dcR": {}}, } diff --git a/test/unit/astructs_access.py b/test/unit/astructs_access.py index 5c0f9523..f5b14333 100644 --- a/test/unit/astructs_access.py +++ b/test/unit/astructs_access.py @@ -1,27 +1,48 @@ import pytest -from amitools.vamos.astructs import AccessStruct, NodeStruct, TaskStruct +from amitools.vamos.astructs import ( + AccessStruct, + AmigaStruct, + AmigaStructDef, + APTR_SELF, + APTR, + APTR_VOID, + UBYTE, + BYTE, + CSTR, + BPTR_VOID, +) from amitools.vamos.machine import MockMemory -def mem_access_rw_all_node_test(): - mem = MockMemory() - a = AccessStruct(mem, NodeStruct, 0x42) - data = a.r_all() - print(data) - a.w_all(data) +@AmigaStructDef +class MyNodeStruct(AmigaStruct): + _format = [ + (APTR_SELF, "ln_Succ"), + (APTR_SELF, "ln_Pred"), + (UBYTE, "ln_Type"), + (BYTE, "ln_Pri"), + (CSTR, "ln_Name"), + ] -def mem_access_rw_all_task_test(): - mem = MockMemory() - a = AccessStruct(mem, TaskStruct, 0x42) - data = a.r_all() - print(data) - a.w_all(data) +# Task +@AmigaStructDef +class MyTaskStruct(AmigaStruct): + _format = [ + (MyNodeStruct, "tc_Node"), + ] + + +@AmigaStructDef +class MyBCPLStruct(AmigaStruct): + _format = [ + (BPTR_VOID, "bs_TestBptr"), + ] def mem_access_rw_field_node_test(): mem = MockMemory() - a = AccessStruct(mem, NodeStruct, 0x42) + a = AccessStruct(mem, MyNodeStruct, 0x42) a.w_s("ln_Succ", 42) a.w_s("ln_Pred", 21) a.w_s("ln_Pri", -27) @@ -32,7 +53,7 @@ def mem_access_rw_field_node_test(): def mem_access_rw_sub_field_task_test(): mem = MockMemory() - a = AccessStruct(mem, TaskStruct, 0x42) + a = AccessStruct(mem, MyTaskStruct, 0x42) a.w_s("tc_Node.ln_Succ", 42) a.w_s("tc_Node.ln_Pred", 21) assert a.r_s("tc_Node.ln_Succ") == 42 @@ -41,7 +62,7 @@ def mem_access_rw_sub_field_task_test(): def mem_access_invalid_node_test(): mem = MockMemory() - a = AccessStruct(mem, NodeStruct, 0x42) + a = AccessStruct(mem, MyNodeStruct, 0x42) with pytest.raises(KeyError): a.w_s("bla", 12) with pytest.raises(KeyError): @@ -50,6 +71,16 @@ def mem_access_invalid_node_test(): def mem_access_s_get_addr_test(): mem = MockMemory() - a = AccessStruct(mem, NodeStruct, 0x42) + a = AccessStruct(mem, MyNodeStruct, 0x42) assert a.s_get_addr("ln_Succ") == 0x42 assert a.s_get_addr("ln_Pred") == 0x46 + + +def mem_access_bptr_test(): + mem = MockMemory() + a = AccessStruct(mem, MyBCPLStruct, 0x42) + # write/read addr + a.w_s("bs_TestBptr", 44) + assert a.r_s("bs_TestBptr") == 44 + # check auto converted baddr + assert mem.r32(0x42) == 11 diff --git a/test/unit/astructs_array.py b/test/unit/astructs_array.py new file mode 100644 index 00000000..c21b820e --- /dev/null +++ b/test/unit/astructs_array.py @@ -0,0 +1,46 @@ +from amitools.vamos.astructs.typebase import TypeBase +from amitools.vamos.astructs.array import ARRAY, ArrayIter +from amitools.vamos.astructs.scalar import ULONG +from amitools.vamos.machine import MockMemory, MockCPU, REG_D0 + +cpu = MockCPU() +mem = MockMemory() + + +def astructs_array_test(): + ULONG_ARRAY = ARRAY(ULONG, 10) + a = ULONG_ARRAY(mem, 0x40) + assert a.get_array_size() == 10 + assert a.get_element_type() is ULONG + assert a.get_byte_size() == 10 * 4 + e = a.get(0) + assert type(e) is ULONG + e.set(0xDEAD) + assert mem.r32(0x40) == 0xDEAD + e = a.get(9) + e.set(0x1234) + assert mem.r32(0x40 + 9 * 4) == 0x1234 + # signature + assert a.get_signature() == "ULONG[10]" + # access entry value + a[0].val = 23 + assert a[0].val == 23 + assert a.get(0).val == 23 + + +def astructs_array_iter_test(): + ULONG_ARRAY = ARRAY(ULONG, 10) + a = ULONG_ARRAY(mem, 0x40) + val = 1 + for e in ArrayIter(a): + e.set(val) + val += 1 + assert val == 11 + # check values + addr = 0x40 + val = 1 + for i in range(10): + v = mem.r32(addr) + assert v == val + addr += 4 + val += 1 diff --git a/test/unit/astructs_astruct.py b/test/unit/astructs_astruct.py index a7052272..275117b2 100644 --- a/test/unit/astructs_astruct.py +++ b/test/unit/astructs_astruct.py @@ -1,118 +1,360 @@ -from amitools.vamos.machine import MockMemory -from amitools.vamos.astructs import AmigaStruct, AmigaStructDef, BAddr +import pytest +from amitools.vamos.machine import MockMemory, MockCPU, REG_D0 +from amitools.vamos.mem import MemoryAlloc +from amitools.vamos.astructs import ( + AmigaStruct, + AmigaStructDef, + AmigaClassDef, + WORD, + UWORD, + BPTR_VOID, + LONG, + APTR, + APTR_SELF, + CSTR, + BSTR, +) @AmigaStructDef class MyStruct(AmigaStruct): _format = [ - ("WORD", "ms_Word"), - ("UWORD", "ms_Pad"), - ("BPTR", "ms_SegList"), - ("LONG", "ms_StackSize"), - ] + (WORD, "ms_Word"), + (UWORD, "ms_Pad"), + (BPTR_VOID, "ms_SegList"), + (LONG, "ms_StackSize"), + ] # 12 @AmigaStructDef class SubStruct(AmigaStruct): - _format = [("My", "ss_My"), ("My*", "ss_MyPtr"), ("Sub*", "ss_SubPtr")] + _format = [ + (MyStruct, "ss_My"), # 0 +12 + (APTR(MyStruct), "ss_MyPtr"), # 12 +4 + (APTR_SELF, "ss_SubPtr"), # 16 +4 + (MyStruct, "ss_My2"), + ] # 20 +12 + # = 32 -def astruct_astruct_base_test(): - # check class - assert MyStruct.get_type_name() == "My" - assert MyStruct.get_size() == 12 - assert len(MyStruct.get_fields()) == 4 - assert MyStruct.get_num_fields() == 4 - ms_Word = MyStruct.get_field_by_name("ms_Word") - assert MyStruct.ms_Word_field == ms_Word - assert MyStruct.get_field_names() == [ - "ms_Word", - "ms_Pad", - "ms_SegList", - "ms_StackSize", - ] - # offset - assert MyStruct.get_field_offset_for_path("ms_SegList") == 4 - assert MyStruct.get_field_offset_for_path("ms_Pad") == 2 - MyStruct.dump_type() - # data class - dc = MyStruct.get_data_class() - assert dc - assert dc.__name__ == "MyData" - data = dc(ms_Word=12, ms_Pad=0, ms_SegList=3, ms_StackSize=5) +def astructs_astruct_base_class_test(): + # check class members + assert MyStruct.sdef.get_type_name() == "My" + assert MyStruct.get_byte_size() == 12 + # get field defs + field_defs = MyStruct.sdef.get_field_defs() + assert field_defs + assert len(field_defs) == 4 + # get one field + field_def = MyStruct.sdef.get_field_def(0) + assert field_def.name == "ms_Word" + # find by name + ms_Word = MyStruct.sdef.find_field_def_by_name("ms_Word") + assert MyStruct.sdef.ms_Word == ms_Word + res = MyStruct.sdef.find_sub_field_defs_by_name("ms_Word") + assert res == [ms_Word] + # find by alias name + stack_size = MyStruct.sdef.stack_size # alias for ms_StackSize + assert stack_size is MyStruct.sdef.ms_StackSize + # offset of fields + fd, delta = MyStruct.sdef.find_field_def_by_offset(4) + assert fd.name == "ms_SegList" + assert delta == 0 + fd, delta = MyStruct.sdef.find_field_def_by_offset(3) + assert fd.name == "ms_Pad" + assert delta == 1 + # offset/base offset of fields + assert MyStruct.sdef.ms_Pad.offset == 2 + assert MyStruct.sdef.ms_Pad.base_offset == 2 + # signature + assert MyStruct.get_signature() == "My" + + +def astructs_astruct_base_inst_test(): # check instance mem = MockMemory() ms = MyStruct(mem, 0x10) assert str(ms) == "[AStruct:My,@000010+00000c]" - # data - data = ms.read_data() - ms.write_data(data) - ms.dump() + # field access + fields = ms.sfields.get_fields() + assert len(fields) == 4 + field = ms.sfields.get_field_by_index(0) + assert field.get_addr() == 0x10 + assert ms.get("ms_Word") == field + assert ms.sfields.find_field_by_offset(0) == (field, 0) + assert ms.sfields.find_field_by_addr(0x10) == (field, 0) + assert ms.sfields.find_field_def_by_addr(0x10) == (ms.sdef[0], 0) + assert ms.ms_Word == field + # alias name + field = ms.ms_StackSize + assert ms.stack_size == field # access - ms.write_field("ms_Word", -3000) - assert ms.read_field("ms_Word") == -3000 - word_field = ms.get_field_by_name("ms_Word") - assert ms.read_field_ext("ms_Word") == (ms, word_field, -3000) - # addr to field - stack_field = ms.get_field_by_name("ms_StackSize") - addr = ms.get_field_addr(stack_field) - assert ms.get_struct_field_for_addr(addr) == (ms, stack_field, 0) - assert ms.get_struct_field_for_name("ms_StackSize") == (ms, stack_field) - # getattr/setattr - ms.ms_Word = 2000 - assert ms.ms_Word == 2000 + ms.get("ms_Word").set(-3000) + assert ms.get("ms_Word").get() == -3000 + # access via __getattr__ + ms.ms_Word.set(2000) + assert ms.ms_Word.get() == 2000 + # find field + field = ms.ms_Word + assert ms.sfields.find_sub_field_by_def(MyStruct.sdef.ms_Word) == field + # try to assign field directly -> forbidden! + with pytest.raises(AttributeError): + ms.ms_Word = 42 + + +def astructs_astruct_base_inst_reg_test(): + cpu = MockCPU() + mem = MockMemory() + ms = MyStruct(mem, 0x10) + ms.ms_Word.val = 42 + # prepare pointer + cpu.w_reg(REG_D0, 0x10) + MyStructAPtr = APTR(MyStruct) + ptr = MyStructAPtr(cpu=cpu, reg=REG_D0, mem=mem) + assert ptr.aptr == 0x10 + ms2 = ptr.ref + assert ms2.ms_Word.val == 42 + + +def astructs_astruct_base_inst_setup_test(): + mem = MockMemory() + ms = MyStruct(mem, 0x10, ms_Word=42, ms_Pad=21, ms_SegList=0x400) + assert ms.ms_Word.val == 42 + assert ms.ms_Pad.val == 21 + assert ms.ms_SegList.aptr == 0x400 -def astruct_astruct_sub_struct_test(): +def astructs_astruct_sub_struct_class_test(): # check class - assert SubStruct.get_type_name() == "Sub" - assert SubStruct.get_size() == 20 - assert len(SubStruct.get_fields()) == 3 - assert SubStruct.get_num_fields() == 3 - assert SubStruct.get_field_by_name("ss_My") - assert SubStruct.get_field_names() == ["ss_My", "ss_MyPtr", "ss_SubPtr"] - assert SubStruct.get_field_offset_for_path("ss_My") == 0 - assert SubStruct.get_field_offset_for_path("ss_My.ms_SegList") == 4 - SubStruct.dump_type() + assert SubStruct.sdef.get_type_name() == "Sub" + assert SubStruct.get_byte_size() == 32 + assert len(SubStruct.sdef.get_field_defs()) == 4 + field_defs = SubStruct.sdef.get_field_defs() + field_names = list(map(lambda x: x.name, field_defs)) + assert field_names == ["ss_My", "ss_MyPtr", "ss_SubPtr", "ss_My2"] + # find by name + assert SubStruct.sdef.find_field_def_by_name("ss_My") + res = SubStruct.sdef.find_sub_field_defs_by_name("ss_My", "ms_Pad") + assert res == [SubStruct.sdef.ss_My, MyStruct.sdef.ms_Pad] + assert SubStruct.sdef.find_sub_field_defs_by_name("ss_My", "Foo") is None + assert SubStruct.sdef.find_sub_field_defs_by_name("ss_SubPtr", "Foo") is None + # field defs + assert SubStruct.sdef.find_field_def_by_offset(3) == (SubStruct.sdef.ss_My, 3) + assert SubStruct.sdef.find_field_def_by_offset(23) == (SubStruct.sdef.ss_My2, 3) + # find sub field defs + sub_fds = SubStruct.sdef.find_sub_field_defs_by_offset(3) + assert sub_fds == ([SubStruct.sdef.ss_My, MyStruct.sdef.ms_Pad], 1) + sub_fds = SubStruct.sdef.find_sub_field_defs_by_offset(23) + assert sub_fds == ([SubStruct.sdef.ss_My2, MyStruct.sdef.ms_Pad], 1) + # access sub field + assert SubStruct.sdef.ss_My.ms_Word == MyStruct.sdef.ms_Word + # offset/base offset of field + assert SubStruct.sdef.ss_My2.offset == 20 + assert SubStruct.sdef.ss_My2.base_offset == 20 + assert SubStruct.sdef.ss_My2.ms_Pad.offset == 2 + assert SubStruct.sdef.ss_My2.ms_Pad.base_offset == 22 + # parent defs + assert SubStruct.sdef.ss_My2.ms_Pad.parent_def == SubStruct.sdef.ss_My2 + # def path + assert SubStruct.sdef.ss_My2.ms_Pad.get_def_path() == [ + SubStruct.sdef.ss_My2, + MyStruct.sdef.ms_Pad, + ] + + +def astructs_astruct_sub_struct_inst_test(): # check instance mem = MockMemory() ss = SubStruct(mem, 0x10) - assert str(ss) == "[AStruct:Sub,@000010+000014]" - # data - data = ss.read_data() - ss.write_data(data) - ss.dump() - # access - ss.write_field("ss_My.ms_Word", -3000) - assert ss.read_field("ss_My.ms_Word") == -3000 - # create my instance - my_field = ss.get_field_by_index(0) - ms = ss.create_struct(my_field) - word_field = ms.get_field_by_name("ms_Word") - assert ss.read_field_ext("ss_My.ms_Word") == (ms, word_field, -3000) - # addr to field - stack_field = ms.get_field_by_name("ms_StackSize") - addr = ms.get_field_addr(stack_field) - assert ss.get_struct_field_for_addr(addr) == (ms, stack_field, 0) - assert ss.get_struct_field_for_name("ss_My.ms_StackSize") == (ms, stack_field) + assert str(ss) == "[AStruct:Sub,@000010+000020]" + assert ss.mem is mem + assert ss.addr == 0x10 + # get/find field + ms = ss.ss_My + assert type(ms) is MyStruct + assert ms.addr == 0x10 + assert ms.ms_Pad.addr == 0x12 + assert ss.get("ss_My") is ms + assert ss.sfields.find_field_by_offset(3) == (ms, 3) + assert ss.sfields.find_field_by_addr(0x13) == (ms, 3) + ms2 = ss.ss_My2 + assert type(ms2) is MyStruct + assert ms2.addr == 0x10 + 20 + assert ms2.ms_Pad.addr == 0x12 + 20 + assert ss.get("ss_My2") is ms2 + assert ss.sfields.find_field_by_offset(23) == (ms2, 3) + assert ss.sfields.find_field_by_addr(0x13 + 20) == (ms2, 3) + # get sub fields + sub_fs = ss.sfields.find_sub_fields_by_offset(3) + assert sub_fs == ([ms, ms.ms_Pad], 1) + sub_fs = ss.sfields.find_sub_fields_by_offset(23) + assert sub_fs == ([ms2, ms2.ms_Pad], 1) # getattr/setattr - ss.ss_My.ms_Word = 2000 - assert ss.ss_My.ms_Word == 2000 + ss.ss_My.ms_Word.set(2000) + assert ss.ss_My.ms_Word.get() == 2000 + # addr/offset/base_offset + assert ss.ss_My.addr == 0x10 + assert ss.ss_My.mem == mem + assert ss.ss_My2.addr == 0x10 + 20 + assert ss.ss_My2.offset == 20 + assert ss.ss_My2.base_offset == 20 + # sub field: addr/offset/base_offset + assert ss.ss_My2.ms_Pad.addr == 0x10 + 22 + assert ss.ss_My2.ms_Pad.offset == 2 + assert ss.ss_My2.ms_Pad.base_offset == 22 + # find sub field + field = ss.ss_My2.ms_Pad + assert ss.sfields.find_sub_field_by_def(SubStruct.sdef.ss_My2.ms_Pad) == field -def astruct_astruct_baddr_test(): +def astructs_astruct_baddr_test(): mem = MockMemory() ms = MyStruct(mem, 0x10) - # write int (addr) to baddr - ms.ms_SegList = 0x100 - # baddr auto converts back to addr - assert ms.ms_SegList == 0x100 - # but its an BAddr - assert type(ms.ms_SegList) is BAddr - assert ms.ms_SegList == BAddr(0x40) + # write int to baddr + ms.ms_SegList.set(0x40) + # bptr auto converts back to baddr + assert ms.ms_SegList.get() == 0x40 # baddr is stored in mem assert mem.r32(0x14) == 0x40 # write baddr - ms.ms_SegList = BAddr(0x20) - assert ms.ms_SegList == BAddr(0x20) + ms.ms_SegList.set(0x20) assert mem.r32(0x14) == 0x20 + + +def astructs_astruct_alloc_ptr_test(): + mem = MockMemory() + alloc = MemoryAlloc(mem) + MyStructPtr = APTR(MyStruct) + ptr = MyStructPtr(mem, 0x10) + assert ptr.aptr == 0 + res = ptr.alloc_ref(alloc) + assert type(res) is MyStruct + assert ptr.aptr != 0 + ptr.free_ref() + assert ptr.aptr == 0 + assert alloc.is_all_free() + + +def astructs_astruct_alloc_test(): + mem = MockMemory() + alloc = MemoryAlloc(mem) + res = MyStruct.alloc(alloc, ms_Word=21, ms_Pad=42) + assert type(res) is MyStruct + assert res.ms_Word.val == 21 + assert res.ms_Pad.val == 42 + res.free() + assert alloc.is_all_free() + + +def astructs_astruct_alloc_sub_test(): + mem = MockMemory() + alloc = MemoryAlloc(mem) + res = SubStruct.alloc(alloc, ss_My={"ms_Word": 21, "ms_Pad": 42}) + assert type(res) is SubStruct + assert res.ss_My.ms_Word.val == 21 + assert res.ss_My.ms_Pad.val == 42 + res.free() + assert alloc.is_all_free() + + +@AmigaStructDef +class PlainStruct(AmigaStruct): + _format = [ + (APTR_SELF, "ps_Next"), + (APTR_SELF, "ps_Prev"), + (CSTR, "ps_Name"), + (BSTR, "ps_Bname"), + ] + + +def astructs_astruct_plain_setup_test(): + mem = MockMemory() + pc = PlainStruct(mem, 0x10, next=0x100, prev=0x200, name=0x120, bname=0x140) + assert pc.next.aptr == 0x100 + assert pc.prev.aptr == 0x200 + assert pc.name.aptr == 0x120 + assert pc.bname.aptr == 0x140 + + +@AmigaClassDef +class PlainClass(PlainStruct): + def foo(self): + result = [] + prev = self.prev.ref + if prev: + prev_foo = prev.foo() + result.append(prev_foo) + txt = self.name.str + if txt: + result.append(txt) + next = self.next.ref + if next: + next_foo = next.foo() + result.append(next_foo) + return " ".join(result) + + +def astructs_astruct_class_test(): + mem = MockMemory() + alloc = MemoryAlloc(mem) + pc = PlainClass.alloc(alloc) + assert type(pc) == PlainClass + assert pc.foo() == "" + # alloc a name + pc.name.alloc_str(alloc, "hello") + assert pc.foo() == "hello" + # another struct + pc2 = PlainClass.alloc(alloc, name="world!") + assert type(pc2) == PlainClass + assert pc2.name.str == "world!" + assert pc2.foo() == "world!" + pc.next.ref = pc2 + assert pc.foo() == "hello world!" + # more struct + pc3 = PlainClass.alloc(alloc, name="what:", bname="ugh!", next=pc2) + assert pc3.foo() == "what: world!" + assert pc3.next.ref is pc2 + assert pc3.name.str == "what:" + assert pc3.bname.str == "ugh!" + pc3.free() + pc2.free() + # clean up pc + pc.name.free_str() + pc.free() + assert alloc.is_all_free() + + +@AmigaStructDef +class SubPlainStruct(AmigaStruct): + _format = [ + (PlainStruct, "plain"), + (PlainStruct, "plain2"), + ] + _subfield_aliases = {"name": "plain.name"} + + +@AmigaClassDef +class SubPlainClass(SubPlainStruct): + pass + + +def astructs_astruct_class_sub_test(): + mem = MockMemory() + alloc = MemoryAlloc(mem) + spc = SubPlainClass.alloc(alloc, plain={"name": "abc"}, plain2={"name": "cde"}) + assert type(spc) == SubPlainClass + assert spc.plain.name.str == "abc" + assert spc.plain2.name.str == "cde" + spc.free() + assert alloc.is_all_free() + + +def astructs_astruct_class_subfield_alias_test(): + mem = MockMemory() + alloc = MemoryAlloc(mem) + spc = SubPlainClass.alloc(alloc, name="abc", plain2={"name": "cde"}) + assert type(spc) == SubPlainClass + assert spc.plain.name.str == "abc" + assert spc.plain2.name.str == "cde" + spc.free() + assert alloc.is_all_free() diff --git a/test/unit/astructs_baddr.py b/test/unit/astructs_baddr.py deleted file mode 100644 index 90ce1cde..00000000 --- a/test/unit/astructs_baddr.py +++ /dev/null @@ -1,41 +0,0 @@ -import pytest -from amitools.vamos.astructs import BAddr - - -def astructs_baddr_repr_test(): - assert repr(BAddr(0)) == "BAddr(0)" - assert repr(BAddr(44)) == "BAddr(44)" - - -def astructs_baddr_str_test(): - assert str(BAddr(0)) == "BAddr(00000000,addr=00000000)" - assert str(BAddr(0x40)) == "BAddr(00000040,addr=00000100)" - - -def astructs_baddr_eq_ne_test(): - assert BAddr(0) == BAddr(0) - assert BAddr(0) != BAddr(1) - assert BAddr(0x40) == 0x100 - assert BAddr(0x40) != 0x40 - - -def astructs_baddr_int_test(): - assert int(BAddr(0)) == 0 - assert int(BAddr(0x40)) == 0x100 - - -def astructs_baddr_from_addr_test(): - assert BAddr.from_addr(0x100) == BAddr(0x40) - with pytest.raises(ValueError): - BAddr.from_addr(0x102) - - -def astructs_baddr_get_test(): - assert BAddr(0).get_baddr() == 0 - assert BAddr(0).get_addr() == 0 - assert BAddr(0x40).get_baddr() == 0x40 - assert BAddr(0x40).get_addr() == 0x100 - - -def astructs_baddr_rshift_test(): - assert BAddr(0x40) >> 2 == 0x40 diff --git a/test/unit/astructs_bitfield.py b/test/unit/astructs_bitfield.py new file mode 100644 index 00000000..c3ca13c9 --- /dev/null +++ b/test/unit/astructs_bitfield.py @@ -0,0 +1,80 @@ +import pytest +from amitools.vamos.astructs import BitFieldType, BitField, ULONG, UWORD, UBYTE +from amitools.vamos.machine import MockMemory + + +@BitFieldType +class MyBF(BitField, ULONG): + a = 1 + b = 2 + c = 4 + + +def astructs_bitfield_type_test(): + # to_strs + assert MyBF.to_strs(1) == ["a"] + assert MyBF.to_str(1) == "a" + assert MyBF.to_strs(5) == ["a", "c"] + assert MyBF.to_str(5) == "a|c" + with pytest.raises(ValueError): + MyBF.to_strs(9) + assert MyBF.to_strs(9, check=False) == ["a", "8"] + assert MyBF.to_str(9, check=False) == "a|8" + # from_strs + assert MyBF.from_strs("a") == 1 + assert MyBF.from_strs("a", "c") == 5 + assert MyBF.from_str("a") == 1 + assert MyBF.from_str("a|c") == 5 + with pytest.raises(ValueError): + MyBF.from_strs("bla") + # is_set + assert MyBF.is_set("a", 1) + assert MyBF.is_set(1, 1) + assert not MyBF.is_set("a", 2) + with pytest.raises(ValueError): + MyBF.is_set("bla", 5) + # is_clr + assert MyBF.is_clr("a", 2) + assert MyBF.is_clr(1, 2) + assert not MyBF.is_clr("a", 1) + with pytest.raises(ValueError): + MyBF.is_clr("bla", 5) + + +def astructs_bitfield_inst_test(): + mem = MockMemory() + mem.w32(4, 1) + # instance + bf = MyBF(mem=mem, addr=4) + assert str(bf) == "a" + assert repr(bf) == "MyBF('a')" + assert int(bf) == MyBF.a + assert bf.has_bits(MyBF.a) + assert bf.has_bits("a") + # set bit + bf.set_bits(MyBF.c) + assert str(bf) == "a|c" + assert int(bf) == MyBF.c | MyBF.a + assert bf.has_bits("c") + assert bf.has_bits(MyBF.c) + assert bf.has_bits("a") + assert bf.has_bits(MyBF.a) + assert mem.r32(4) == MyBF.c | MyBF.a + # clr bit + bf.clr_bits(MyBF.a) + assert str(bf) == "c" + assert int(bf) == MyBF.c + assert bf.has_bits("c") + assert bf.has_bits(MyBF.c) + assert not bf.has_bits("a") + assert not bf.has_bits(MyBF.a) + assert mem.r32(4) == MyBF.c + # set value + bf.set(MyBF.a | MyBF.b) + assert str(bf) == "a|b" + assert int(bf) == MyBF.b | MyBF.a + assert bf.has_bits("b") + assert bf.has_bits(MyBF.b) + assert bf.has_bits("a") + assert bf.has_bits(MyBF.a) + assert mem.r32(4) == MyBF.b | MyBF.a diff --git a/test/unit/astructs_dump.py b/test/unit/astructs_dump.py new file mode 100644 index 00000000..d34dbf8c --- /dev/null +++ b/test/unit/astructs_dump.py @@ -0,0 +1,124 @@ +from amitools.vamos.astructs import ( + AmigaStruct, + AmigaStructDef, + TypeDumper, + WORD, + UWORD, + BPTR_VOID, + LONG, + ULONG, + APTR, + APTR_SELF, + APTR_VOID, + BitField, + BitFieldType, + Enum, + EnumType, +) + + +@AmigaStructDef +class MyStruct(AmigaStruct): + _format = [ + (WORD, "ms_Word"), + (UWORD, "ms_Pad"), + (BPTR_VOID, "ms_SegList"), + (LONG, "ms_StackSize"), + ] + + +@AmigaStructDef +class SubStruct(AmigaStruct): + _format = [ + (MyStruct, "ss_My"), + (APTR(MyStruct), "ss_MyPtr"), + (APTR_SELF, "ss_SubPtr"), + ] + + +@BitFieldType +class MyBitField(BitField, ULONG): + foo = 1 + bar = 2 + baz = 4 + + +@EnumType +class MyEnum(Enum, ULONG): + a = 3 + b = 4 + c = 0xFFFFFFFF + + +@AmigaStructDef +class SpecialStruct(AmigaStruct): + _format = [ + (MyBitField, "ss_MyBitField"), + (MyEnum, "ss_MyEnum"), + ] + + +def dump_type(type): + result = [] + + def print_func(txt): + result.append(txt) + + dumper = TypeDumper(print_func=print_func) + dumper.dump(type) + return result + + +def dump_fields(*fields): + result = [] + + def print_func(txt): + result.append(txt) + + dumper = TypeDumper(print_func=print_func) + dumper.dump_fields(*fields) + return result + + +def astructs_dump_type_simple_test(): + assert dump_type(ULONG) == [" @0000 ULONG"] + assert dump_type(APTR_VOID) == [" @0000 APTR_VOID"] + + +def astructs_dump_type_struct_test(): + assert dump_type(MyStruct) == [ + " @0000 My {", + "#0000 0000 @0000/0000 +0002 WORD ms_Word ", + "#0001 0001 @0002/0002 +0002 UWORD ms_Pad ", + "#0002 0002 @0004/0004 +0004 VOID# ms_SegList ", + "#0003 0003 @0008/0008 +0004 LONG ms_StackSize ", + " @0012 =0012 }", + ] + assert dump_type(SubStruct) == [ + " @0000 Sub {", + " @0000 My ss_My {", + "#0000 0000 @0000/0000 +0002 WORD ms_Word ", + "#0001 0001 @0002/0002 +0002 UWORD ms_Pad ", + "#0002 0002 @0004/0004 +0004 VOID# ms_SegList ", + "#0003 0003 @0008/0008 +0004 LONG ms_StackSize ", + " @0012 =0012 }", + "#0001 0004 @0012/000c +0004 My* ss_MyPtr ", + "#0002 0005 @0016/0010 +0004 Sub* ss_SubPtr ", + " @0020 =0020 }", + ] + assert dump_type(SpecialStruct) == [ + " @0000 Special {", + "#0000 0000 @0000/0000 +0004 MyBitField ss_MyBitField ", + "#0001 0001 @0004/0004 +0004 MyEnum ss_MyEnum ", + " @0008 =0008 }", + ] + + +def astructs_dump_fields_test(): + assert dump_fields(SubStruct.sdef.ss_My, MyStruct.sdef.ms_Pad) == [ + " @0000 Sub {", + " @0000 My ss_My {", + "#0001 0000 @0002/0002 +0002 UWORD ms_Pad ", + " @0002 =0020 }", + " @0002 =0020 }", + ] diff --git a/test/unit/astructs_enum.py b/test/unit/astructs_enum.py new file mode 100644 index 00000000..30ddda3e --- /dev/null +++ b/test/unit/astructs_enum.py @@ -0,0 +1,44 @@ +import pytest +from amitools.vamos.astructs import EnumType, Enum, ULONG +from amitools.vamos.machine import MockMemory + + +@EnumType +class MyEnum(Enum, ULONG): + a = 3 + b = 4 + c = 0xFFFFFFFF + + +def astructs_enum_type_test(): + # to_str + assert MyEnum.to_str(3) == "a" + with pytest.raises(ValueError): + MyEnum.to_str(5) + assert MyEnum.to_str(5, check=False) == "5" + # from_str + assert MyEnum.from_str("a") == 3 + with pytest.raises(ValueError): + MyEnum.from_str("bla") + + +def astructs_enum_inst_test(): + mem = MockMemory() + mem.w32(4, 3) + # instance + me = MyEnum(mem=mem, addr=4) + assert me.get() == MyEnum.a + assert str(me) == "a" + assert repr(me) == "MyEnum('a')" + assert int(me) == MyEnum.a + # change + me.set(MyEnum.c) + assert me.get() == MyEnum.c + assert str(me) == "c" + assert int(me) == MyEnum.c + assert mem.r32(4) == MyEnum.c + me.set("b") + assert me.get() == MyEnum.b + assert str(me) == "b" + assert int(me) == MyEnum.b + assert mem.r32(4) == MyEnum.b diff --git a/test/unit/astructs_pointer.py b/test/unit/astructs_pointer.py new file mode 100644 index 00000000..ef1cd5dd --- /dev/null +++ b/test/unit/astructs_pointer.py @@ -0,0 +1,98 @@ +import pytest +from amitools.vamos.astructs.typebase import TypeBase +from amitools.vamos.astructs.pointer import ( + PointerType, + BCPLPointerType, + APTR, + BPTR, + VOID, + APTR_VOID, + BPTR_VOID, +) +from amitools.vamos.machine import MockMemory, MockCPU, REG_D0 +from amitools.vamos.mem import MemoryAlloc + +cpu = MockCPU() +mem = MockMemory() +alloc = MemoryAlloc(mem) + + +def astructs_pointer_aptr_void_test(): + assert PointerType.get_byte_size() == 4 + void_ptr = APTR_VOID(mem=mem, addr=0x40) + mem.w32(0x40, 0x80) + ref = void_ptr.ref + assert type(ref) is VOID + assert ref.get_addr() == 0x80 + assert void_ptr.get_ref_addr() == 0x80 + assert int(void_ptr) == 0x80 + # change pointer + void_ptr.set_ref_addr(0x100) + assert void_ptr.get_ref_addr() == 0x100 + assert int(void_ptr) == 0x100 + new_ref = void_ptr.ref + assert type(new_ref) is VOID + assert new_ref.get_addr() == 0x100 + assert mem.r32(0x40) == 0x100 + # signature + assert void_ptr.get_signature() == "VOID*" + # check 'aptr' + void_ptr.aptr = 0x200 + assert void_ptr.aptr == 0x200 + # invalid .val access + with pytest.raises(AttributeError): + void_ptr.val = 12 + + +def astructs_pointer_aptr_null_test(): + null_ptr = APTR_VOID(mem=mem, addr=0x40) + mem.w32(0x40, 0) + assert null_ptr.ref is None + assert int(null_ptr) == 0 + assert null_ptr.get_ref_addr() == 0 + + +def astructs_pointer_bptr_void_test(): + assert BCPLPointerType.get_byte_size() == 4 + void_ptr = BPTR_VOID(mem=mem, addr=0x40) + mem.w32(0x40, 0x20) # 0x20 = BCPL addr of 0x80 + ref = void_ptr.ref + assert type(ref) is VOID + assert ref.get_addr() == 0x80 + assert void_ptr.get_ref_addr() == 0x80 + # int conversion is BPTR! + assert int(void_ptr) == 0x20 + # change pointer + void_ptr.set_ref_addr(0x100) + assert void_ptr.get_ref_addr() == 0x100 + new_ref = void_ptr.ref + assert type(new_ref) is VOID + assert new_ref.get_addr() == 0x100 + # int conversion is BPTR! + assert int(void_ptr) == 0x40 + assert mem.r32(0x40) == 0x40 # 0x40 = BCPL addr of 0x100 + # change pointer by baddr + void_ptr.set_ref_baddr(0x200) + assert void_ptr.get_ref_addr() == 0x200 << 2 + assert void_ptr.get_ref_baddr() == 0x200 + assert mem.r32(0x40) == 0x200 + # signature + assert void_ptr.get_signature() == "VOID#" + # check 'aptr' + void_ptr.aptr = 0x400 + assert void_ptr.aptr == 0x400 + assert void_ptr.get() == 0x100 + void_ptr.bptr = 0x200 + assert void_ptr.bptr == 0x200 + assert void_ptr.get() == 0x200 + # invalid .val access + with pytest.raises(AttributeError): + void_ptr.val = 12 + + +def astructs_pointer_bptr_null_test(): + null_ptr = BPTR_VOID(mem=mem, addr=0x40) + mem.w32(0x40, 0) + assert null_ptr.ref is None + assert int(null_ptr) == 0 + assert null_ptr.get_ref_addr() == 0 diff --git a/test/unit/astructs_scalar.py b/test/unit/astructs_scalar.py new file mode 100644 index 00000000..d2a69022 --- /dev/null +++ b/test/unit/astructs_scalar.py @@ -0,0 +1,106 @@ +import pytest +from amitools.vamos.astructs.typebase import TypeBase +from amitools.vamos.astructs.scalar import ULONG, LONG, UWORD, WORD, UBYTE, BYTE +from amitools.vamos.machine import MockMemory, MockCPU, REG_D0 + +cpu = MockCPU() +mem = MockMemory() + + +def astructs_scalar_mem_test(): + # unsigned + mem.w32(8, 1234) + l = ULONG(mem=mem, addr=8) + assert l.get() == 1234 + l.set(5432) + assert mem.r32(8) == 5432 + # signed + mem.w32s(16, -2233) + l = LONG(mem=mem, addr=16) + assert l.get() == -2233 + l.set(-3322) + assert mem.r32s(16) == -3322 + # test val access + l.val = 42 + assert l.val == 42 + # invalid .aptr or .bptr access + with pytest.raises(AttributeError): + l.aptr = 42 + with pytest.raises(AttributeError): + l.bptr = 42 + + +def astructs_scalar_reg_test(): + # unsigned + cpu.w_reg(REG_D0, 42) + l = ULONG(reg=REG_D0, cpu=cpu) + assert l.get() == 42 + l.set(112) + assert cpu.r_reg(REG_D0) == 112 + # signed + cpu.ws_reg(REG_D0, -23) + l = LONG(reg=REG_D0, cpu=cpu) + assert l.get() == -23 + l.set(-112) + assert cpu.rs_reg(REG_D0) == -112 + + +def astructs_scalar_ulong_test(): + assert ULONG.get_byte_size() == 4 + assert ULONG.get_mem_width() == 2 + assert not ULONG.is_signed() + mem.w32(0, 10) + l = ULONG(mem=mem, addr=0) + assert l.get() == 10 + assert repr(l) == "ULONG(value=10, addr=0)" + assert l.get_signature() == "ULONG" + + +def astructs_scalar_long_test(): + assert LONG.get_byte_size() == 4 + assert LONG.get_mem_width() == 2 + assert LONG.is_signed() + mem.w32s(0, -10) + l = LONG(mem=mem, addr=0) + assert repr(l) == "LONG(value=-10, addr=0)" + assert l.get_signature() == "LONG" + + +def astructs_scalar_uword_test(): + assert UWORD.get_byte_size() == 2 + assert UWORD.get_mem_width() == 1 + assert not UWORD.is_signed() + mem.w16(0, 10) + l = UWORD(mem=mem, addr=0) + assert repr(l) == "UWORD(value=10, addr=0)" + assert l.get_signature() == "UWORD" + + +def astructs_scalar_word_test(): + assert WORD.get_byte_size() == 2 + assert WORD.get_mem_width() == 1 + assert WORD.is_signed() + mem.w16s(0, -10) + l = WORD(mem=mem, addr=0) + assert repr(l) == "WORD(value=-10, addr=0)" + assert l.get_signature() == "WORD" + + +def astructs_scalar_ubyte_test(): + assert UBYTE.get_byte_size() == 1 + assert UBYTE.get_mem_width() == 0 + assert not UBYTE.is_signed() + mem.w8(0, 10) + l = UBYTE(mem=mem, addr=0) + assert repr(l) == "UBYTE(value=10, addr=0)" + assert l.get_signature() == "UBYTE" + + +def astructs_scalar_byte_test(): + assert BYTE.get_byte_size() == 1 + assert BYTE.get_mem_width() == 0 + assert BYTE.is_signed() + mem.w8s(0, -10) + l = BYTE(mem=mem, addr=0) + assert repr(l) == "BYTE(value=-10, addr=0)" + assert l.get_signature() == "BYTE" diff --git a/test/unit/astructs_string.py b/test/unit/astructs_string.py new file mode 100644 index 00000000..0b1b5d4c --- /dev/null +++ b/test/unit/astructs_string.py @@ -0,0 +1,167 @@ +from amitools.vamos.astructs.typebase import TypeBase +from amitools.vamos.astructs.string import CSTR, BSTR, CStringType, BStringType +from amitools.vamos.machine import MockMemory, MockCPU, REG_D0 +from amitools.vamos.mem import MemoryAlloc + +cpu = MockCPU() +mem = MockMemory() +alloc = MemoryAlloc(mem) + + +def astructs_string_cstr_test(): + # prepare mem + txt = "hello, world!" + mem.w32(0x20, 0x40) + mem.w_cstr(0x40, txt) + # now access with cstr + cstr = CSTR(mem=mem, addr=0x20) + assert cstr.get_signature() == "CSTR" + val = cstr.get_str() + assert val == txt + assert str(cstr) == txt + # modify cstr + txt2 = "wow, str!" + cstr.set_str(txt2) + val = mem.r_cstr(0x40) + assert val == txt2 + assert str(cstr) == txt2 + # modify ptr + mem.w_cstr(0x80, txt) + cstr.set(0x80) + assert cstr.get_str() == txt + assert str(cstr) == txt + # access via 'str' + cstr.str = "now!" + assert cstr.str == "now!" + + +def astructs_string_cstr_reg_test(): + txt = "hello, world!" + mem.w_cstr(0x40, txt) + cpu.w_reg(REG_D0, 0x40) + cstr = CSTR(cpu=cpu, reg=REG_D0, mem=mem) + assert cstr.str == txt + + +def astructs_string_cstr_null_test(): + mem.w32(0, 0) + cstr = CSTR(mem=mem, addr=0) + assert cstr.str is None + assert str(cstr) == "None" + mem.w32(4, 0) + cstr2 = CSTR(mem=mem, addr=4) + assert cstr2.str is None + assert str(cstr2) == "None" + assert cstr != cstr2 + + +def astructs_string_cstr_compare_test(): + txt = "hello, world!" + mem.w32(0x20, 0x40) + mem.w_cstr(0x40, txt) + cstr = CSTR(mem=mem, addr=0x20) + assert cstr == 0x20 + assert cstr.aptr == 0x40 + assert cstr.str == txt + cstr2 = CSTR(mem=mem, addr=0x20) + assert cstr == cstr2 + cstr3 = CSTR(mem=mem, addr=0x24) + assert cstr != cstr3 + + +def astructs_string_cstr_alloc_test(): + mem.w32(0x20, 0) + cstr = CSTR(mem=mem, addr=0x20) + assert cstr.str is None + assert cstr.aptr == 0 + txt = "hello, world!" + res = cstr.alloc_str(alloc, txt) + assert res is not None + assert type(res) is CStringType + assert cstr.str == txt + assert cstr.aptr != 0 + assert mem.r_cstr(cstr.aptr) == txt + cstr.free_str() + assert cstr.str is None + assert cstr.aptr == 0 + assert alloc.is_all_free() + + +def astructs_string_bstr_test(): + # prepare mem + txt = "hello, world!" + mem.w32(0x20, 0x10) # baddr for 0x40 + mem.w_bstr(0x40, txt) + # now access with cstr + bstr = BSTR(mem=mem, addr=0x20) + assert bstr.get_signature() == "BSTR" + val = bstr.get_str() + assert val == txt + assert str(bstr) == txt + # modify cstr + txt2 = "wow, str!" + bstr.set_str(txt2) + val = mem.r_bstr(0x40) + assert val == txt2 + assert str(bstr) == txt2 + # modify ptr + mem.w_bstr(0x80, txt) + bstr.set(0x20) # 0x20 is baddr of 0x80 + assert bstr.get_str() == txt + assert str(bstr) == txt + # access via 'str' + bstr.str = "now!" + assert bstr.str == "now!" + + +def astructs_string_cstr_reg_test(): + txt = "hello, world!" + mem.w_bstr(0x40, txt) + cpu.w_reg(REG_D0, 0x10) # BADDR + bstr = BSTR(cpu=cpu, reg=REG_D0, mem=mem) + assert bstr.str == txt + + +def astructs_string_bstr_null_test(): + mem.w32(0, 0) + bstr = BSTR(mem=mem, addr=0) + assert bstr.str is None + assert str(bstr) == "None" + mem.w32(4, 0) + bstr2 = BSTR(mem=mem, addr=4) + assert bstr2.str is None + assert str(bstr2) == "None" + assert bstr != bstr2 + + +def astructs_string_cstr_compare_test(): + txt = "hello, world!" + mem.w32(0x20, 0x10) + mem.w_bstr(0x40, txt) + bstr = BSTR(mem=mem, addr=0x20) + assert bstr == 0x20 + assert bstr.aptr == 0x40 + assert bstr.bptr == 0x10 + assert bstr.str == txt + bstr2 = BSTR(mem=mem, addr=0x20) + assert bstr == bstr2 + cstr3 = BSTR(mem=mem, addr=0x24) + assert bstr != cstr3 + + +def astructs_string_bstr_alloc_test(): + mem.w32(0x20, 0) + bstr = BSTR(mem=mem, addr=0x20) + assert bstr.str is None + assert bstr.aptr == 0 + txt = "hello, world!" + res = bstr.alloc_str(alloc, txt) + assert res is not None + assert type(res) is BStringType + assert bstr.str == txt + assert bstr.aptr != 0 + assert mem.r_bstr(bstr.aptr) == txt + bstr.free_str() + assert bstr.str is None + assert bstr.aptr == 0 + assert alloc.is_all_free() diff --git a/test/unit/atypes_atype.py b/test/unit/atypes_atype.py deleted file mode 100644 index e5ea4888..00000000 --- a/test/unit/atypes_atype.py +++ /dev/null @@ -1,180 +0,0 @@ -from amitools.vamos.machine import MockMemory -from amitools.vamos.mem import MemoryAlloc -from amitools.vamos.astructs import AmigaStruct, AmigaStructDef -from amitools.vamos.atypes import AmigaType, AmigaTypeDef, CString - - -@AmigaStructDef -class MyStruct(AmigaStruct): - _format = [ - ("WORD", "ms_Word"), - ("UWORD", "ms_Pad"), - ("BPTR", "ms_SegList"), - ("LONG", "ms_StackSize"), - ("char*", "ms_String"), - ] - - -@AmigaStructDef -class SubStruct(AmigaStruct): - _format = [("My", "ss_My"), ("My*", "ss_MyPtr"), ("Sub*", "ss_SubPtr")] - - -class Bla(object): - def __init__(self, val): - self.val = val - - def __eq__(self, other): - return self.val == other.val - - def __int__(self): - return self.val - - @staticmethod - def setf(obj): - assert type(obj) is Bla - return obj.val - - @staticmethod - def getf(val): - return Bla(val) - - -def atypes_atype_base_test(): - - # simple type without extra contents - # wrap field 'pad' with getter/setter - @AmigaTypeDef(MyStruct, wrap={"pad": (Bla.getf, Bla.setf)}) - class MyType(AmigaType): - pass - - # create a type instance - mem = MockMemory() - alloc = MemoryAlloc(mem, addr=0x100) - mt = MyType(mem, 0x10) - # regular values (signed/unsigend) - mt.set_word(-3) - assert mt.get_word() == -3 - # direct access - mt.word = 5 - assert mt.word == 5 - # wrapped type - assert type(mt.get_pad()) is Bla - mt.set_pad(Bla(3)) - assert mt.get_pad() == Bla(3) - assert mt.get_pad(raw=True) == 3 - mt.set_pad(21, raw=True) - assert mt.get_pad() == Bla(21) - # direct access - mt.pad = Bla(39) - assert mt.pad == Bla(39) - # cstring - assert type(mt.get_string()) is CString - txt = "hello, word!" - cstr = CString.alloc(alloc, txt) - cstr_addr = cstr.get_addr() - mt.set_string(cstr) - assert mt.get_string() == txt - assert mt.get_string() == CString(mem, cstr_addr) - assert mt.get_string(ptr=True) == cstr_addr - mt.string = cstr - assert mt.string == txt - assert mt.string == cstr - cstr.free() - - -def atypes_atype_complex_test(): - - # complex type with own ctor - # wrap field 'pad' with getter only, assume int() setter - @AmigaTypeDef(MyStruct, wrap={"pad": Bla.getf}) - class MyType(AmigaType): - def __init__(self, mem, addr, extra=None): - AmigaType.__init__(self, mem, addr) - self._extra = extra - - # create a type instance: signature is (mem, addr, extra) - mem = MockMemory() - mt = MyType(mem, 0x10, "hello") - assert mt._extra == "hello" - # regular values (signed/unsigend) - mt.set_word(-3) - assert mt.get_word() == -3 - # wrapped type - assert type(mt.get_pad()) is Bla - mt.set_pad(Bla(3)) - assert mt.get_pad() == Bla(3) - assert mt.get_pad(raw=True) == 3 - mt.set_pad(21, raw=True) - assert mt.get_pad() == Bla(21) - - -def atypes_atype_struct_test(): - # complex type with own ctor - # wrap field 'pad' with getter only, assume int() setter - @AmigaTypeDef(SubStruct) - class SubType(AmigaType): - pass - - # create instance - mem = MockMemory() - st = SubType(mem, 0x10) - # get struct - mt = st.get_my() - my_type = AmigaType.find_type("My") - assert type(mt) is my_type - assert mt.get_addr() == 0x10 - assert mt == st.my - # struct pointer - # null pointer -> None - mtp = st.get_my_ptr() - assert mtp is None - assert st.my_ptr is None - # valid pointer -> type object - st.set_my_ptr(0x30) - mtp = st.get_my_ptr() - assert type(mtp) is my_type - assert mtp.get_addr() == 0x30 - st.my_ptr = 0x40 - assert st.my_ptr.get_addr() == 0x40 - assert type(st.my_ptr) is my_type - # self pointer - msp = st.get_sub_ptr() - assert msp is None - st.set_sub_ptr(0x50) - msp = st.get_sub_ptr() - assert type(msp) is type(st) - assert msp.get_addr() == 0x50 - st.sub_ptr = 0x60 - assert st.sub_ptr.get_addr() == 0x60 - assert type(st.sub_ptr) is type(st) - # null pointer - st.set_sub_ptr(None) - assert st.get_sub_ptr() is None - assert st.get_sub_ptr(ptr=True) == 0 - st.sub_ptr = None - assert st.sub_ptr is None - - -def atypes_atype_alloc_test(): - @AmigaTypeDef(MyStruct, wrap={"pad": (Bla.getf, Bla.setf)}) - class MyType(AmigaType): - pass - - @AmigaTypeDef(SubStruct) - class SubType(AmigaType): - pass - - mem = MockMemory() - alloc = MemoryAlloc(mem) - # my type - mt = MyType.alloc(alloc) - assert mt - assert mt.get_addr() != 0 - mt.free() - # sub - st = SubType.alloc(alloc) - assert st - assert st.get_addr() != 0 - st.free() - assert alloc.is_all_free() diff --git a/test/unit/atypes_bitfield.py b/test/unit/atypes_bitfield.py deleted file mode 100644 index 0fdd8b0e..00000000 --- a/test/unit/atypes_bitfield.py +++ /dev/null @@ -1,64 +0,0 @@ -import pytest -from amitools.vamos.atypes import BitFieldType - - -def atypes_bitfield_test(): - @BitFieldType - class MyBF: - a = 1 - b = 2 - c = 4 - - # to_strs - assert MyBF.to_strs(1) == ["a"] - assert MyBF.to_str(1) == "a" - assert MyBF.to_strs(5) == ["a", "c"] - assert MyBF.to_str(5) == "a|c" - with pytest.raises(ValueError): - MyBF.to_strs(9) - assert MyBF.to_strs(9, check=False) == ["a", "8"] - assert MyBF.to_str(9, check=False) == "a|8" - # from_strs - assert MyBF.from_strs("a") == 1 - assert MyBF.from_strs("a", "c") == 5 - assert MyBF.from_str("a") == 1 - assert MyBF.from_str("a|c") == 5 - with pytest.raises(ValueError): - MyBF.from_strs("bla") - # is_set - assert MyBF.is_set("a", 1) - assert MyBF.is_set(1, 1) - assert not MyBF.is_set("a", 2) - with pytest.raises(ValueError): - MyBF.is_set("bla", 5) - # is_clr - assert MyBF.is_clr("a", 2) - assert MyBF.is_clr(1, 2) - assert not MyBF.is_clr("a", 1) - with pytest.raises(ValueError): - MyBF.is_clr("bla", 5) - # instance - a = MyBF("a") - assert str(a) == "a" - assert repr(a) == "MyBF('a')" - assert int(a) == 1 - assert a == 1 - assert a == MyBF(1) - assert a.has_bits(1) - assert a.has_bits("a") - ac = MyBF("c|a") - assert str(ac) == "a|c" - assert int(ac) == 5 - assert ac == 5 - assert ac.has_bits("a") - assert ac.has_bits(1) - ac.clr_bits(1) - assert not ac.has_bits(1) - ac.set_bits(1) - assert ac.has_bits(1) - ac2 = MyBF("c", "a") - assert str(ac2) == "a|c" - assert int(ac2) == 5 - ac3 = MyBF(MyBF.a | MyBF.c) - assert str(ac3) == "a|c" - assert int(ac3) == 5 diff --git a/test/unit/atypes_bstring.py b/test/unit/atypes_bstring.py deleted file mode 100644 index 16374812..00000000 --- a/test/unit/atypes_bstring.py +++ /dev/null @@ -1,76 +0,0 @@ -import pytest -from amitools.vamos.machine import MockMemory -from amitools.vamos.mem import MemoryAlloc -from amitools.vamos.atypes import BString - - -def atypes_bstring_base_test(): - mem = MockMemory() - alloc = MemoryAlloc(mem) - # simple string - txt = "hello, world!" - bs = BString.alloc(alloc, txt) - assert bs - assert bs.get_baddr() << 2 == bs.get_addr() - assert mem.r_bstr(bs.get_addr()) == txt - assert bs.get_string() == txt - assert bs == txt - assert bs == bs.get_addr() - assert bs == BString(mem, bs.get_addr()) - bs.free() - assert alloc.is_all_free() - - -def atypes_bstring_empty_test(): - mem = MockMemory() - alloc = MemoryAlloc(mem) - # empty string - txt = "" - bs = BString.alloc(alloc, txt) - assert bs - assert bs.get_baddr() << 2 == bs.get_addr() - assert mem.r_bstr(bs.get_addr()) == txt - assert bs.get_string() == txt - assert bs == txt - assert bs == bs.get_addr() - bs.free() - assert alloc.is_all_free() - - -def atypes_bstring_null_test(): - mem = MockMemory() - alloc = MemoryAlloc(mem) - # no string - bs = BString.alloc(alloc, None) - assert bs - assert bs.get_baddr() << 2 == bs.get_addr() - assert bs.get_addr() == 0 - assert bs.get_string() is None - bs.free() - assert alloc.is_all_free() - - -def atypes_bstring_alloc_bstr_test(): - mem = MockMemory() - alloc = MemoryAlloc(mem) - # no string - bs = BString.alloc(alloc, "bla") - bs2 = BString.alloc(alloc, bs) - assert bs - assert bs.get_string() == "bla" - assert bs2 - assert bs2.get_string() == "bla" - bs.free() - bs2.free() - assert alloc.is_all_free() - - -def atypes_bstring_max_size_test(): - mem = MockMemory() - alloc = MemoryAlloc(mem) - bs = BString.alloc(alloc, "bla") - assert bs.get_max_size() == 3 - bs.set_string("foo") - assert bs == "foo" - with pytest.raises(ValueError): - bs.set_string("foo!") diff --git a/test/unit/atypes_cstring.py b/test/unit/atypes_cstring.py deleted file mode 100644 index 7711dbec..00000000 --- a/test/unit/atypes_cstring.py +++ /dev/null @@ -1,73 +0,0 @@ -import pytest -from amitools.vamos.machine import MockMemory -from amitools.vamos.mem import MemoryAlloc -from amitools.vamos.atypes import CString - - -def atypes_cstring_base_test(): - mem = MockMemory() - alloc = MemoryAlloc(mem) - # simple string - txt = "hello, world!" - cs = CString.alloc(alloc, txt) - assert cs - assert mem.r_cstr(cs.get_addr()) == txt - assert cs.get_string() == txt - assert cs == txt - assert cs == cs.get_addr() - assert cs == CString(mem, cs.get_addr()) - cs.free() - assert alloc.is_all_free() - - -def atypes_cstring_empty_test(): - mem = MockMemory() - alloc = MemoryAlloc(mem) - # empty string - txt = "" - cs = CString.alloc(alloc, txt) - assert cs - assert mem.r_cstr(cs.get_addr()) == txt - assert cs.get_string() == txt - assert cs == txt - assert cs == cs.get_addr() - cs.free() - assert alloc.is_all_free() - - -def atypes_cstring_null_test(): - mem = MockMemory() - alloc = MemoryAlloc(mem) - # no string - cs = CString.alloc(alloc, None) - assert cs - assert cs.get_addr() == 0 - assert cs.get_string() is None - cs.free() - assert alloc.is_all_free() - - -def atypes_cstring_alloc_cstr_test(): - mem = MockMemory() - alloc = MemoryAlloc(mem) - # no string - cs = CString.alloc(alloc, "bla") - cs2 = CString.alloc(alloc, cs) - assert cs - assert cs.get_string() == "bla" - assert cs2 - assert cs2.get_string() == "bla" - cs.free() - cs2.free() - assert alloc.is_all_free() - - -def atypes_cstring_max_size_test(): - mem = MockMemory() - alloc = MemoryAlloc(mem) - cs = CString.alloc(alloc, "bla") - assert cs.get_max_size() == 3 - cs.set_string("foo") - assert cs == "foo" - with pytest.raises(ValueError): - cs.set_string("foo!") diff --git a/test/unit/atypes_enum.py b/test/unit/atypes_enum.py deleted file mode 100644 index 11aafdb3..00000000 --- a/test/unit/atypes_enum.py +++ /dev/null @@ -1,33 +0,0 @@ -import pytest -from amitools.vamos.atypes import EnumType - - -def atype_enum_test(): - @EnumType - class MyEnum: - a = 3 - b = 4 - c = 0xFFFFFFFF - - # to_str - assert MyEnum.to_str(3) == "a" - with pytest.raises(ValueError): - MyEnum.to_str(5) - assert MyEnum.to_str(5, check=False) == "5" - # from_str - assert MyEnum.from_str("a") == 3 - with pytest.raises(ValueError): - MyEnum.from_str("bla") - # instance - a = MyEnum("a") - assert a.get_value() == 3 - assert str(a) == "a" - assert repr(a) == "MyEnum('a')" - assert int(a) == 3 - assert a == 3 - assert a == MyEnum(3) - c = MyEnum("c") - assert c.get_value() == 0xFFFFFFFF - assert str(c) == "c" - assert int(c) == 0xFFFFFFFF - assert c == 0xFFFFFFFF diff --git a/test/unit/atypes_execlib.py b/test/unit/atypes_execlib.py deleted file mode 100644 index 2e66ed58..00000000 --- a/test/unit/atypes_execlib.py +++ /dev/null @@ -1,20 +0,0 @@ -from amitools.vamos.machine import MockMemory -from amitools.vamos.mem import MemoryAlloc -from amitools.vamos.atypes import ExecLibrary - - -def atypes_execlib_base_test(): - mem = MockMemory() - el = ExecLibrary(mem, 0x100) - el.setup() - el.fill_funcs() - - -def atypes_execlib_alloc_test(): - mem = MockMemory() - alloc = MemoryAlloc(mem) - el = ExecLibrary.alloc(alloc, "exec.library", "bla", 20) - el.setup() - el.fill_funcs() - el.free() - assert alloc.is_all_free() diff --git a/test/unit/atypes_library.py b/test/unit/atypes_library.py deleted file mode 100644 index d5aeea09..00000000 --- a/test/unit/atypes_library.py +++ /dev/null @@ -1,115 +0,0 @@ -import pytest -from amitools.vamos.machine import MockMemory -from amitools.vamos.machine.opcodes import op_rts -from amitools.vamos.mem import MemoryAlloc -from amitools.vamos.astructs import LibraryStruct -from amitools.vamos.atypes import Library, CString, NodeType, LibFlags -from amitools.vamos.label import LabelLib -from amitools.fd import read_lib_fd - - -def atypes_library_base_test(mem_alloc): - mem, alloc = mem_alloc - # alloc lib - name = "my.library" - id_str = "my.library 1.2" - neg_size = 36 - pos_size = LibraryStruct.get_size() - lib = Library.alloc(alloc, name, id_str, neg_size) - assert lib.get_name() == name - assert lib.get_id_string() == id_str - size = lib.get_size() - assert pos_size == lib.get_pos_size() - assert neg_size == lib.get_neg_size() - assert size == pos_size + neg_size - # lib setup - flags = LibFlags(LibFlags.LIBF_SUMMING, LibFlags.LIBF_CHANGED) - ltype = NodeType(NodeType.NT_DEVICE) - pri = -3 - ver = 1 - rev = 2 - lib.setup(version=ver, revision=rev, pri=pri, flags=flags, type=ltype) - # check lib - node = lib.get_node() - assert node.get_succ() is None - assert node.get_pred() is None - assert node.get_type() == ltype - assert node.get_pri() == pri - assert lib.get_flags() == flags - assert lib.get_pad() == 0 - assert lib.get_neg_size() == neg_size - assert lib.get_pos_size() == pos_size - assert lib.get_version() == ver - assert lib.get_revision() == rev - assert lib.get_sum() == 0 - assert lib.get_open_cnt() == 0 - assert lib.get_name() == name - assert lib.get_id_string() == id_str - # fill funcs - lib.fill_funcs() - lib_base = lib.get_addr() - assert mem.r16(lib_base - 6) == op_rts - # done - lib.free() - assert alloc.is_all_free() - - -def atypes_library_sum_test(mem_alloc): - mem, alloc = mem_alloc - # alloc lib - name = "my.library" - id_str = "my.library 1.2" - neg_size = 30 - pos_size = LibraryStruct.get_size() - lib = Library.alloc(alloc, name, id_str, neg_size) - # assume rounded neg size - assert lib.get_neg_size() == 32 - mem.w32(lib.addr - 32, 0xDEADBEEF) - mem.w32(lib.addr - 28, 0xCAFEBABE) - my_sum = (0xDEADBEEF + 0xCAFEBABE) & 0xFFFFFFFF - lib_sum = lib.calc_sum() - assert lib_sum == my_sum - lib.update_sum() - assert lib.get_sum() == my_sum - assert lib.check_sum() - # done - lib.free() - assert alloc.is_all_free() - - -def atypes_libary_open_cnt_test(mem_alloc): - mem, alloc = mem_alloc - # alloc lib - name = "my.library" - id_str = "my.library 1.2" - neg_size = 30 - pos_size = LibraryStruct.get_size() - lib = Library.alloc(alloc, name, id_str, neg_size) - # test open cnt - assert lib.get_open_cnt() == 0 - lib.inc_open_cnt() - assert lib.get_open_cnt() == 1 - lib.dec_open_cnt() - assert lib.get_open_cnt() == 0 - # done - lib.free() - assert alloc.is_all_free() - - -def atypes_library_label_test(mem_alloc): - mem, alloc = mem_alloc - name = "vamostest.library" - id_str = "vamostest.library 0.1" - fd = read_lib_fd("vamostest.library") - neg_size = fd.get_neg_size() - lib = Library.alloc(alloc, name, id_str, neg_size, fd=fd) - # check for label - if alloc.get_label_mgr(): - assert lib._label - assert isinstance(lib._label, LabelLib) - assert lib._label.fd == fd - else: - assert not lib._label - # done - lib.free() - assert alloc.is_all_free() diff --git a/test/unit/atypes_msg.py b/test/unit/atypes_msg.py deleted file mode 100644 index 74f8ee66..00000000 --- a/test/unit/atypes_msg.py +++ /dev/null @@ -1,45 +0,0 @@ -import pytest -from amitools.vamos.machine import MockMemory -from amitools.vamos.mem import MemoryAlloc -from amitools.vamos.atypes import MsgPort, Message, MsgPortFlags, NodeType - - -def atypes_msg_msgport_test(): - mem = MockMemory() - alloc = MemoryAlloc(mem) - # alloc msg port - mp1 = MsgPort.alloc(alloc) - assert mp1.name == 0 - mp1.setup(pri=-5, flags=MsgPortFlags.PA_SOFTINT, sig_bit=5) - assert mp1.node.pri == -5 - assert mp1.node.type == NodeType.NT_MSGPORT - assert mp1.flags == MsgPortFlags.PA_SOFTINT - assert mp1.sig_bit == 5 - assert mp1.sig_task == 0 - assert len(mp1.msg_list) == 0 - # with name - mp2 = MsgPort.alloc(alloc, "bla") - assert mp2.get_name() == "bla" - # free - mp1.free() - mp2.free() - assert alloc.is_all_free() - - -def atypes_msg_msg_test(): - mem = MockMemory() - alloc = MemoryAlloc(mem) - # alloc msg - msg = Message.alloc(alloc) - assert msg.name == 0 - msg.setup(pri=-7, length=10) - assert msg.node.pri == -7 - assert msg.node.type == NodeType.NT_MESSAGE - assert msg.length == 10 - # with name - msg2 = Message.alloc(alloc, "bla") - assert msg2.name == "bla" - # free - msg.free() - msg2.free() - assert alloc.is_all_free() diff --git a/test/unit/atypes_node.py b/test/unit/atypes_node.py deleted file mode 100644 index 83ba13bc..00000000 --- a/test/unit/atypes_node.py +++ /dev/null @@ -1,123 +0,0 @@ -import pytest -from amitools.vamos.machine import MockMemory -from amitools.vamos.mem import MemoryAlloc -from amitools.vamos.atypes import Node, NodeType, MinNode -from amitools.vamos.astructs import NodeStruct, MinNodeStruct - - -def atypes_node_type_to_str_test(): - assert NodeType.to_str(NodeType.NT_UNKNOWN) == "NT_UNKNOWN" - with pytest.raises(ValueError): - NodeType.to_str(-1) - - -def atypes_node_type_from_str_test(): - assert NodeType.from_str("NT_INTERRUPT") == NodeType.NT_INTERRUPT - with pytest.raises(ValueError): - NodeType.from_str("bla") - - -def atypes_node_base_test(): - mem = MockMemory() - text = "hello, world!" - mem.w_cstr(12, text) - node = Node(mem, 0x42) - # set node - node.set_succ(1234) - node.set_pred(5678) - node.set_type(NodeType.NT_LIBRARY) - node.set_pri(-3) - node.set_name(12) - # check node - assert node.get_succ() == 1234 - assert node.get_pred() == 5678 - assert int(node.get_type()) == NodeType.NT_LIBRARY - assert node.get_type() == NodeType(NodeType.NT_LIBRARY) - assert node.get_pri() == -3 - assert node.get_name(True) == 12 - assert node.get_name() == text - - -def atypes_node_setup_test(): - mem = MockMemory() - text = "hello, world!" - mem.w_cstr(12, text) - node = Node(mem, 0x42) - node.setup(1234, 5678, NodeType.NT_DEVICE, -5, 12) - # check node - assert node.get_succ() == 1234 - assert node.get_pred() == 5678 - assert int(node.get_type()) == NodeType.NT_DEVICE - assert node.get_pri() == -5 - assert node.get_name(True) == 12 - assert node.get_name() == text - node.set_type(NodeType(NodeType.NT_DEVICE)) - - -def atypes_node_setup_min_test(): - mem = MockMemory() - node = MinNode(mem, 0x42) - node.setup(1234, 5678) - # check node - assert node.get_succ() == 1234 - assert node.get_pred() == 5678 - - -def atypes_node_str_test(): - mem = MockMemory() - text = "hello, world!" - mem.w_cstr(12, text) - node = Node(mem, 0x42) - node.setup(0x1234, 0x5678, NodeType.NT_DEVICE, -5, 12) - assert str(node) == "[Node:@000042,p=005678,s=001234,NT_DEVICE,-5,'hello, world!']" - - -def atypes_node_str_min_test(): - mem = MockMemory() - min_node = MinNode(mem, 0x80) - min_node.setup(0x1234, 0x5678) - assert str(min_node) == "[MinNode:@000080,p=005678,s=001234]" - - -def atypes_node_remove_test(): - mem = MockMemory() - text = "hello, world!" - mem.w_cstr(12, text) - node = Node(mem, 0x80) - node.setup(0x60, 0x100, NodeType.NT_DEVICE, -5, 12) - node.remove() - - -def atypes_node_remove_min_test(): - mem = MockMemory() - min_node = MinNode(mem, 0x80) - min_node.setup(0x60, 0x100) - min_node.remove() - - -def atypes_node_alloc_test(): - mem = MockMemory() - alloc = MemoryAlloc(mem) - node = Node.alloc(alloc) - assert node.get_size() == NodeStruct.get_size() - node.free() - assert alloc.is_all_free() - - -def atypes_node_alloc_name_test(): - mem = MockMemory() - alloc = MemoryAlloc(mem) - node = Node.alloc(alloc, "foobar") - assert node.get_size() == NodeStruct.get_size() - assert node.get_name() == "foobar" - node.free() - assert alloc.is_all_free() - - -def atypes_node_alloc_min_test(): - mem = MockMemory() - alloc = MemoryAlloc(mem) - node = MinNode.alloc(alloc) - assert node.get_size() == MinNodeStruct.get_size() - node.free() - assert alloc.is_all_free() diff --git a/test/unit/atypes_resident.py b/test/unit/atypes_resident.py deleted file mode 100644 index dcda832e..00000000 --- a/test/unit/atypes_resident.py +++ /dev/null @@ -1,95 +0,0 @@ -from amitools.vamos.loader import SegmentLoader -from amitools.vamos.machine import MockMemory -from amitools.vamos.mem import MemoryAlloc -from amitools.vamos.atypes import Resident, ResidentFlags, NodeType - - -def load_lib(mem, buildlibnix): - lib_file = buildlibnix.make_lib("testnix") - alloc = MemoryAlloc(mem) - loader = SegmentLoader(alloc) - info = loader.int_load_sys_seglist(lib_file) - seg_list = info.seglist - seg = seg_list.get_segment() - addr = seg.get_addr() - size = seg.get_size() - end = seg.get_end() - return addr, size, end - - -def atypes_resident_find_lib_test(buildlibnix): - mem = MockMemory(fill=23) - addr, size, end = load_lib(mem, buildlibnix) - # search list - res = Resident.find(mem, addr, size, only_first=False) - assert len(res) == 1 - assert res[0].is_valid() - res_obj = res[0] - assert res_obj.get_addr() > addr - assert res_obj.get_addr() < end - # search first only - res2 = Resident.find(mem, addr, size) - assert res == [res2] - assert res2.is_valid() - - -def atypes_resident_read_lib_test(buildlibnix): - mem = MockMemory(fill=23) - addr, size, end = load_lib(mem, buildlibnix) - res = Resident.find(mem, addr, size) - assert res.get_match_word() == res.RTC_MATCHWORD - assert res.get_match_tag() == res.get_addr() - assert res.get_end_skip() >= res.get_addr() + res.get_type_size() - assert res.get_flags() == ResidentFlags.RTF_AUTOINIT - assert res.get_version() == 0 - assert res.get_type() == NodeType.NT_LIBRARY - assert res.get_pri() == 0 - assert res.get_name() == "testnix.library" - assert res.get_id_string() == "testnix.library 1.0 (07.07.2007)" - assert res.get_init() > 0 - ai = res.get_auto_init() - assert ai.get_addr() == res.get_init() - assert ai.get_pos_size() > 0 - assert ai.get_functions() > 0 - assert ai.get_init_struct() == 0 - assert ai.get_init_func() > 0 - assert res.is_valid() - - -def atypes_resident_alloc_test(): - mem = MockMemory(fill=23) - alloc = MemoryAlloc(mem) - # alloc - res = Resident.alloc(alloc, "bla.library", "blub") - # free - res.free() - assert alloc.is_all_free() - - -def atypes_resident_setup_test(): - mem = MockMemory(fill=23) - alloc = MemoryAlloc(mem) - # alloc - res2 = Resident.alloc(alloc, "bla.library", "blub") - res2.setup( - flags=ResidentFlags.RTF_AUTOINIT, - version=42, - type=NodeType.NT_DEVICE, - pri=-7, - init=0xDEADBEEF, - ) - # find resource - res = Resident.find(mem, 0, 1024) - assert res.get_match_word() == res.RTC_MATCHWORD - assert res.get_match_tag() == res.get_addr() - assert res.get_end_skip() >= res.get_addr() + res.get_type_size() - assert res.get_flags() == ResidentFlags.RTF_AUTOINIT - assert res.get_version() == 42 - assert res.get_type() == NodeType.NT_DEVICE - assert res.get_pri() == -7 - assert res.get_name() == "bla.library" - assert res.get_id_string() == "blub" - assert res.get_init() == 0xDEADBEEF - # free - res2.free() - assert alloc.is_all_free() diff --git a/test/unit/atypes_task.py b/test/unit/atypes_task.py deleted file mode 100644 index f89f3131..00000000 --- a/test/unit/atypes_task.py +++ /dev/null @@ -1,25 +0,0 @@ -import pytest -from amitools.vamos.machine import MockMemory -from amitools.vamos.mem import MemoryAlloc -from amitools.vamos.atypes import Task, TaskFlags, TaskState, NodeType - - -def atypes_task_base_test(): - mem = MockMemory() - alloc = MemoryAlloc(mem) - # alloc task - name = "my_task" - task = Task.alloc(alloc, name) - assert task.get_name() == name - # task setup - task.setup(pri=-5, flags=TaskFlags.TF_LAUNCH) - node = task.get_node() - assert node.get_type() == NodeType.NT_TASK - assert node.get_pri() == -5 - assert task.get_flags() == TaskFlags.TF_LAUNCH - assert task.get_state() == TaskState.TS_INVALID - assert len(task.mem_entry) == 0 - assert task.mem_entry.type == NodeType.NT_MEMORY - # done - task.free() - assert alloc.is_all_free() diff --git a/test/unit/cfg_path.py b/test/unit/cfg_path.py index 15cde1df..a5adb67d 100644 --- a/test/unit/cfg_path.py +++ b/test/unit/cfg_path.py @@ -94,8 +94,13 @@ def cfg_path_args_test(): def cfg_path_ini_args_test(): lp = PathParser() ini_dict = { - "volumes": [["sys", "~/.vamos/sys"],], - "assigns": [["c", "sys:c"], ["libs", "sys:libs"],], + "volumes": [ + ["sys", "~/.vamos/sys"], + ], + "assigns": [ + ["c", "sys:c"], + ["libs", "sys:libs"], + ], "path": { "path": "c:", "cwd": "~/amiga", diff --git a/test/unit/dos_args.py b/test/unit/dos_args.py index 2fef75c5..a80584c9 100644 --- a/test/unit/dos_args.py +++ b/test/unit/dos_args.py @@ -1,4 +1,3 @@ -from amitools.vamos.astructs import * from amitools.vamos.lib.dos.CSource import CSource from amitools.vamos.lib.dos.Args import * from amitools.vamos.lib.dos.Error import * diff --git a/test/unit/dos_item.py b/test/unit/dos_item.py index c874414f..2f66a7b3 100644 --- a/test/unit/dos_item.py +++ b/test/unit/dos_item.py @@ -1,4 +1,3 @@ -from amitools.vamos.astructs import * from amitools.vamos.lib.dos.CSource import CSource from amitools.vamos.lib.dos.Item import ItemParser diff --git a/test/unit/fd_format.py b/test/unit/fd_format.py index 8844c59a..a978b12a 100644 --- a/test/unit/fd_format.py +++ b/test/unit/fd_format.py @@ -34,9 +34,9 @@ def fd_format_generate_fd_num_call_test(): def fd_format_read_vamostest_lib_test(): fd = read_lib_fd("vamostest.library") assert fd.get_base_name() == "_VamosTestBase" - assert fd.get_max_bias() == 60 - assert fd.get_neg_size() == 66 - assert fd.get_num_indices() == 10 + assert fd.get_max_bias() == 66 + assert fd.get_neg_size() == 72 + assert fd.get_num_indices() == 11 # check entries tab = fd.get_index_table() assert len(tab) == fd.get_num_indices() diff --git a/test/unit/libcore_create.py b/test/unit/libcore_create.py index 4468e1c9..6eabcf29 100644 --- a/test/unit/libcore_create.py +++ b/test/unit/libcore_create.py @@ -63,7 +63,7 @@ def libcore_create_lib_fake_without_fd_test(): creator = LibCreator(alloc, traps) lib = creator.create_lib(info, ctx, impl) assert lib.get_fd().get_neg_size() == 30 - assert lib.get_library().neg_size == 32 + assert lib.get_library().neg_size.val == 32 # free lib lib.free() assert alloc.is_all_free() @@ -82,7 +82,7 @@ def libcore_create_lib_fake_without_fd_cfg_test(): creator = LibCreator(alloc, traps) lib = creator.create_lib(info, ctx, impl, lib_cfg) assert lib.get_fd().get_neg_size() == 66 - assert lib.get_library().neg_size == 68 + assert lib.get_library().neg_size.val == 68 # free lib lib.free() assert alloc.is_all_free() @@ -99,7 +99,7 @@ def libcore_create_lib_label_test(): lib = creator.create_lib(info, ctx, impl) # check label assert alloc.get_label_mgr() - label = lib.get_library()._label + label = lib.get_library()._mem_obj.label assert label assert label.fd == lib.get_fd() # free lib diff --git a/test/unit/libcore_impl.py b/test/unit/libcore_impl.py index c103a208..873ae194 100644 --- a/test/unit/libcore_impl.py +++ b/test/unit/libcore_impl.py @@ -1,8 +1,9 @@ import pytest from amitools.fd import read_lib_fd from amitools.vamos.lib.VamosTestLibrary import VamosTestLibrary -from amitools.vamos.libcore import LibImplScanner +from amitools.vamos.libcore import LibImplScanner, LibImplScan, LibImplFuncArg from amitools.vamos.error import VamosInternalError +from amitools.vamos.machine.regs import * def libcore_impl_scan_checked_vamos_test(): @@ -30,34 +31,95 @@ def PrintHello(self, ctx, wrong): assert res.get_name() == name assert res.get_impl() == impl assert res.get_fd() == fd - assert res.get_num_valid_funcs() == 4 + assert res.get_num_valid_funcs() == 5 assert res.get_num_missing_funcs() == 1 assert res.get_num_error_funcs() == 1 assert res.get_num_invalid_funcs() == 1 - # check funcs + + # missing func missing_funcs = res.get_missing_funcs() - assert missing_funcs == {"Dummy": fd.get_func_by_name("Dummy")} + missing_func = res.get_func_by_name("Dummy") + assert missing_funcs == {"Dummy": missing_func} + assert missing_func.name == "Dummy" + assert missing_func.fd_func == fd.get_func_by_name("Dummy") + assert missing_func.method is None + assert missing_func.tag == LibImplScan.TAG_MISSING + assert missing_funcs == {"Dummy": missing_func} missing_names = res.get_missing_func_names() assert missing_names == ["Dummy"] + # invalid func invalid_funcs = res.get_invalid_funcs() - assert invalid_funcs == {"InvalidFunc": impl.InvalidFunc} + invalid_func = res.get_func_by_name("InvalidFunc") + assert invalid_funcs == {"InvalidFunc": invalid_func} + assert invalid_func.name == "InvalidFunc" + assert invalid_func.fd_func == fd.get_func_by_name("InvalidFunc") + assert invalid_func.method == impl.InvalidFunc + assert invalid_func.tag == LibImplScan.TAG_INVALID invalid_names = res.get_invalid_func_names() assert invalid_names == ["InvalidFunc"] + # error func error_funcs = res.get_error_funcs() - assert error_funcs == { - "PrintHello": (fd.get_func_by_name("PrintHello"), impl.PrintHello) - } + error_func = res.get_func_by_name("PrintHello") + assert error_func.name == "PrintHello" + assert error_func.fd_func == fd.get_func_by_name("PrintHello") + assert error_func.method == impl.PrintHello + assert error_func.tag == LibImplScan.TAG_ERROR + assert error_funcs == {"PrintHello": error_func} error_names = res.get_error_func_names() assert error_names == ["PrintHello"] + # valid funcs valid_funcs = res.get_valid_funcs() assert valid_funcs == { - "PrintString": (fd.get_func_by_name("PrintString"), impl.PrintString), - "Add": (fd.get_func_by_name("Add"), impl.Add), - "Swap": (fd.get_func_by_name("Swap"), impl.Swap), - "RaiseError": (fd.get_func_by_name("RaiseError"), impl.RaiseError), + "PrintString": res.get_func_by_name("PrintString"), + "Add": res.get_func_by_name("Add"), + "Swap": res.get_func_by_name("Swap"), + "RaiseError": res.get_func_by_name("RaiseError"), + "ExecutePy": res.get_func_by_name("ExecutePy"), } valid_names = res.get_valid_func_names() - assert valid_names == ["Add", "PrintString", "RaiseError", "Swap"] + assert valid_names == ["Add", "ExecutePy", "PrintString", "RaiseError", "Swap"] + valid_func = res.get_func_by_name("ExecutePy") + assert valid_func.name == "ExecutePy" + assert valid_func.fd_func == fd.get_func_by_name("ExecutePy") + assert valid_func.method == impl.ExecutePy + assert valid_func.tag == LibImplScan.TAG_VALID + + +def libcore_impl_scan_vamos_extra_args_test(): + name = "vamostest.library" + fd = read_lib_fd(name) + impl = VamosTestLibrary() + + # setup test function with annotated args + # also test name replacement with _ if it collides with Python + def PrintString(self, ctx, str_: str): + pass + + impl.PrintString = PrintString.__get__(impl, impl.__class__) + + # scan impl + scanner = LibImplScanner() + res = scanner.scan(name, impl, fd) + + # check valid funcs + valid_func = res.get_func_by_name("ExecutePy") + assert valid_func.name == "ExecutePy" + assert valid_func.fd_func == fd.get_func_by_name("ExecutePy") + assert valid_func.method == impl.ExecutePy + assert valid_func.tag == LibImplScan.TAG_VALID + assert valid_func.extra_args == [ + LibImplFuncArg("argc", REG_D0, int), + LibImplFuncArg("argv", REG_A0, int), + ] + + valid_func = res.get_func_by_name("PrintString") + assert valid_func.name == "PrintString" + assert valid_func.fd_func == fd.get_func_by_name("PrintString") + assert valid_func.method == impl.PrintString + assert valid_func.tag == LibImplScan.TAG_VALID + assert valid_func.extra_args == [ + LibImplFuncArg("str", REG_A0, str), + ] diff --git a/test/unit/libcore_mgr.py b/test/unit/libcore_mgr.py index c671fdd0..74adba8f 100644 --- a/test/unit/libcore_mgr.py +++ b/test/unit/libcore_mgr.py @@ -2,7 +2,7 @@ from amitools.vamos.lib.ExecLibrary import ExecLibrary from amitools.vamos.lib.VamosTestLibrary import VamosTestLibrary from amitools.vamos.lib.VamosTestDevice import VamosTestDevice -from amitools.vamos.libcore import VLibManager, LibRegistry, LibCtxMap +from amitools.vamos.libcore import VLibManager from amitools.vamos.machine import Machine from amitools.vamos.mem import MemoryAlloc from amitools.vamos.lib.lexec.ExecLibCtx import ExecLibCtx @@ -24,7 +24,7 @@ def setup(main_profiler=None, prof_names=None, prof_calls=False): mem = machine.get_mem() cpu_type = machine.get_cpu_type() segloader = SegmentLoader(alloc) - exec_ctx = ExecLibCtx(machine, alloc, segloader, None) + exec_ctx = ExecLibCtx(machine, alloc, segloader, None, None) mgr.add_ctx("exec.library", exec_ctx) mgr.add_impl_cls("exec.library", ExecLibrary) mgr.add_impl_cls("vamostest.library", VamosTestLibrary) @@ -41,7 +41,7 @@ def libcore_mgr_bootstrap_shutdown_test(): # make sure exec is in place assert mgr.get_vlib_by_name("exec.library") == exec_vlib assert mgr.get_vlib_by_addr(exec_base) == exec_vlib - assert exec_lib.open_cnt == 1 + assert exec_lib.open_cnt.val == 1 assert machine.get_mem().r32(4) == exec_base # we can't expunge exec assert not mgr.expunge_lib(exec_vlib) @@ -65,7 +65,7 @@ def libcore_mgr_make_test(): assert impl assert impl.get_cnt() == 0 lib = test_vlib.get_library() - assert lib.version == impl.get_version() + assert lib.version.val == impl.get_version() # shutdown left = mgr.shutdown() assert left == 0 @@ -85,8 +85,8 @@ def libcore_mgr_make_version_revision_test(): assert impl assert impl.get_cnt() == 0 lib = test_vlib.get_library() - assert lib.version == 11 - assert lib.revision == 23 + assert lib.version.val == 11 + assert lib.revision.val == 23 # shutdown left = mgr.shutdown() assert left == 0 @@ -152,7 +152,7 @@ def libcore_mgr_make_fake_without_fd_test(): impl = test_vlib.get_impl() assert impl is None assert test_vlib.get_fd().get_neg_size() == 30 - assert test_vlib.get_library().neg_size == 32 + assert test_vlib.get_library().neg_size.val == 32 # shutdown left = mgr.shutdown() assert left == 0 @@ -176,7 +176,7 @@ def libcore_mgr_make_fake_without_fd_cfg_test(): impl = test_vlib.get_impl() assert impl is None assert test_vlib.get_fd().get_neg_size() == 66 - assert test_vlib.get_library().neg_size == 68 + assert test_vlib.get_library().neg_size.val == 68 # shutdown left = mgr.shutdown() assert left == 0 @@ -191,7 +191,7 @@ def libcore_mgr_make_open_test(): test_base = test_vlib.get_addr() impl = test_vlib.get_impl() lib = test_vlib.get_library() - assert lib.version == impl.get_version() + assert lib.version.val == impl.get_version() assert impl.get_cnt() == 0 assert mgr.open_lib_name("vamostest.library") == test_vlib assert impl.get_cnt() == 1 @@ -211,7 +211,7 @@ def libcore_mgr_open_test(): test_vlib = mgr.open_lib_name("vamostest.library") impl = test_vlib.get_impl() lib = test_vlib.get_library() - assert lib.version == impl.get_version() + assert lib.version.val == impl.get_version() assert impl.get_cnt() == 1 assert mgr.close_lib(test_vlib) assert impl.get_cnt() is None @@ -230,7 +230,7 @@ def libcore_mgr_make_open_dev_test(): impl = test_vlib.get_impl() lib = test_vlib.get_library() assert test_vlib.is_device() - assert lib.version == impl.get_version() + assert lib.version.val == impl.get_version() assert impl.get_cnt() == 0 assert mgr.open_lib_name("vamostestdev.device") == test_vlib assert impl.get_cnt() == 1 @@ -251,7 +251,7 @@ def libcore_mgr_open_dev_test(): impl = test_vlib.get_impl() lib = test_vlib.get_library() assert test_vlib.is_device() - assert lib.version == impl.get_version() + assert lib.version.val == impl.get_version() assert impl.get_cnt() == 1 assert mgr.close_lib(test_vlib) assert impl.get_cnt() is None diff --git a/test/unit/libcore_proxy.py b/test/unit/libcore_proxy.py new file mode 100644 index 00000000..f04b7ead --- /dev/null +++ b/test/unit/libcore_proxy.py @@ -0,0 +1,133 @@ +from amitools.vamos.libcore import LibCtx, LibProxyGen +from amitools.vamos.lib.VamosTestLibrary import VamosTestLibrary +from amitools.vamos.machine import MockMachine +from amitools.fd import read_lib_fd +from amitools.vamos.machine.regs import * + + +def _create_ctx(): + machine = MockMachine() + return LibCtx(machine) + + +def _create_fd(): + name = "vamostest.library" + impl = VamosTestLibrary() + fd = read_lib_fd(name) + return fd + + +class MyStub: + def __init__(self, ctx): + self.ctx = ctx + self.hello_count = 0 + self.hello_kwargs = None + self.string_count = 0 + self.string_kwargs = None + self.string_reg_a0 = None + + def PrintHello(self, **kwargs): + self.hello_count += 1 + self.hello_kwargs = kwargs + self.ctx.cpu.w_reg(REG_D0, self.hello_count) + + def PrintString(self, **kwargs): + self.string_count += 1 + self.string_kwargs = kwargs + self.string_reg_a0 = self.ctx.cpu.r_reg(REG_A0) + self.ctx.cpu.w_reg(REG_D0, self.string_count) + self.ctx.cpu.w_reg(REG_D1, 2 * self.string_count) + + +class MyMachine: + def __init__(self): + self.pc = None + self.sp = None + self.set_regs = None + self.get_regs = None + self.name = None + self.regs = None + + def run(self, pc, sp=None, set_regs=None, get_regs=None, name=None): + self.pc = pc + self.sp = sp + self.set_regs = set_regs + self.get_regs = get_regs + self.name = name + if len(get_regs) == 2: + self.regs = {REG_D0: 23, REG_D1: 42} + else: + self.regs = {REG_D0: 11} + return self + + +def libcore_proxy_gen_stub_test(): + ctx = _create_ctx() + lib_fd = _create_fd() + gen = LibProxyGen() + stub = MyStub(ctx) + # generate proxy type + VamosTestProxy = gen.gen_proxy_for_stub("VamosTestProxy", lib_fd, stub) + # check some functions + type_dict = VamosTestProxy.__dict__ + assert "PrintString" in type_dict + assert "PrintHello" in type_dict + # create proxy instance + proxy = VamosTestProxy(ctx) + # call hello + assert stub.hello_count == 0 + ret = proxy.PrintHello() + assert ret == 1 + assert stub.hello_count == 1 + assert ctx.cpu.r_reg(REG_D0) == stub.hello_count + # call hello with kwargs + assert stub.hello_count == 1 + ret = proxy.PrintHello(what="why") + assert ret == 2 + assert stub.hello_count == 2 + assert stub.hello_kwargs == {"what": "why"} + assert ctx.cpu.r_reg(REG_D0) == stub.hello_count + # call string + assert stub.string_count == 0 + ret = proxy.PrintString(0x10, ret_d1=True) + assert ret == (1, 2) + assert stub.string_count == 1 + assert stub.string_reg_a0 == 0x10 + assert ctx.cpu.r_reg(REG_D0) == stub.string_count + assert ctx.cpu.r_reg(REG_D1) == stub.string_count * 2 + # call string with kwargs + assert stub.string_count == 1 + ret = proxy.PrintString(0x20, ret_d1=True, foo="bar") + assert ret == (2, 4) + assert stub.string_count == 2 + assert stub.string_reg_a0 == 0x20 + assert stub.string_kwargs == {"foo": "bar"} + assert ctx.cpu.r_reg(REG_D0) == stub.string_count + assert ctx.cpu.r_reg(REG_D1) == stub.string_count * 2 + + +def libcore_proxy_gen_libcall_test(): + machine = MyMachine() + ctx = _create_ctx() + ctx.machine = machine + lib_fd = _create_fd() + gen = LibProxyGen() + base_addr = 0x1000 + # gen prxoy + VamosTestProxy = gen.gen_proxy_for_libcall("VamosTestProxy", lib_fd) + # check some functions + type_dict = VamosTestProxy.__dict__ + assert "PrintString" in type_dict + assert "PrintHello" in type_dict + # create proxy instance + proxy = VamosTestProxy(ctx, base_addr) + # call hello + ret = proxy.PrintHello() + assert ret == 11 + assert machine.set_regs == {} + assert machine.get_regs == [REG_D0] + # call string + ret = proxy.PrintString(0x10, ret_d1=True) + assert ret == (23, 42) + assert machine.set_regs == {REG_A0: 0x10} + assert machine.get_regs == [REG_D0, REG_D1] diff --git a/test/unit/libcore_stub.py b/test/unit/libcore_stub.py index 1df1ccbe..d69d4357 100644 --- a/test/unit/libcore_stub.py +++ b/test/unit/libcore_stub.py @@ -6,6 +6,7 @@ from amitools.vamos.machine import MockMachine from amitools.vamos.libcore import LibProfileData from amitools.fd import read_lib_fd +from amitools.vamos.machine.regs import * def _check_stub(stub): @@ -40,6 +41,12 @@ def _check_log(caplog): "{ CALL: 48 Swap( a[d0]=00000000, b[d1]=00000000 ) from PC=000000", ), ("valid", logging.INFO, "} CALL: -> d0=00000000, d1=00000000"), + ( + "valid", + logging.INFO, + "{ CALL: 36 PrintString( str[a0]=00000010 ) from PC=000000", + ), + ("valid", logging.INFO, "} CALL: -> d0=00000000"), ] @@ -60,11 +67,20 @@ def _check_log_fake(caplog): logging.WARN, "? CALL: 48 Swap( a[d0]=00000000, b[d1]=00000000 ) from PC=000000 -> d0=0 (default)", ), + ( + "missing", + logging.WARN, + "? CALL: 36 PrintString( str[a0]=00000010 ) from PC=000000 -> d0=0 " + "(default)", + ), ] def _create_ctx(): machine = MockMachine() + # prepare PrintString() + machine.mem.w_cstr(0x10, "hello, world!") + machine.cpu.w_reg(REG_A0, 0x10) return LibCtx(machine) @@ -76,7 +92,7 @@ def _create_scan(): return scanner.scan(name, impl, fd, True) -def libcore_stub_gen_base_test(): +def libcore_stub_gen_base_test(capsys): scan = _create_scan() ctx = _create_ctx() # create stub @@ -85,8 +101,21 @@ def libcore_stub_gen_base_test(): _check_stub(stub) # call func stub.PrintHello() + cap = capsys.readouterr() + assert cap.out.strip() == "VamosTest: PrintHello()" stub.Dummy() + # check arg transfer + ctx.cpu.w_reg(REG_D0, 21) + ctx.cpu.w_reg(REG_D1, 10) stub.Swap() + assert ctx.cpu.r_reg(REG_D0) == 10 + assert ctx.cpu.r_reg(REG_D1) == 21 + # check return + stub.Add() + assert ctx.cpu.r_reg(REG_D0) == 31 + stub.PrintString() + cap = capsys.readouterr() + assert cap.out.strip() == "VamosTest: PrintString('hello, world!')" def libcore_stub_gen_profile_test(): @@ -101,6 +130,7 @@ def libcore_stub_gen_profile_test(): stub.PrintHello() stub.Dummy() stub.Swap() + stub.PrintString() _check_profile(scan.get_fd(), profile) @@ -118,6 +148,7 @@ def libcore_stub_gen_log_test(caplog): stub.PrintHello() stub.Dummy() stub.Swap() + stub.PrintString() _check_log(caplog) @@ -136,6 +167,7 @@ def libcore_stub_gen_log_profile_test(caplog): stub.PrintHello() stub.Dummy() stub.Swap() + stub.PrintString() _check_log(caplog) _check_profile(scan.get_fd(), profile) @@ -148,12 +180,13 @@ def libcore_stub_gen_exc_default_test(): stub = gen.gen_stub(scan, ctx) _check_stub(stub) # call func - ctx.mem.w_cstr(0, "RuntimeError") + ctx.cpu.w_reg(REG_A0, 0x20) + ctx.mem.w_cstr(0x20, "RuntimeError") with pytest.raises(RuntimeError): stub.RaiseError() -def libcore_stub_gen_multi_arg(caplog): +def libcore_stub_gen_multi_arg_test(caplog): caplog.set_level(logging.INFO) scan = _create_scan() ctx = _create_ctx() @@ -168,6 +201,7 @@ def libcore_stub_gen_multi_arg(caplog): stub.PrintHello(1, 2, a=3) stub.Dummy(3, b="hello") stub.Swap("hugo", None, c=3) + stub.PrintString("a", b=4, c=5) _check_log(caplog) _check_profile(scan.get_fd(), profile) @@ -184,6 +218,7 @@ def libcore_stub_gen_fake_base_test(): stub.PrintHello() stub.Dummy() stub.Swap() + stub.PrintString() def libcore_stub_gen_fake_profile_test(): @@ -199,6 +234,7 @@ def libcore_stub_gen_fake_profile_test(): stub.PrintHello() stub.Dummy() stub.Swap() + stub.PrintString() _check_profile(fd, profile) @@ -217,6 +253,7 @@ def libcore_stub_gen_fake_log_test(caplog): stub.PrintHello() stub.Dummy() stub.Swap() + stub.PrintString() _check_log_fake(caplog) @@ -236,5 +273,6 @@ def libcore_stub_gen_fake_log_profile_test(caplog): stub.PrintHello() stub.Dummy() stub.Swap() + stub.PrintString() _check_log_fake(caplog) _check_profile(fd, profile) diff --git a/test/unit/libmgr_mgr.py b/test/unit/libmgr_mgr.py index 268cf399..61c3a5a5 100644 --- a/test/unit/libmgr_mgr.py +++ b/test/unit/libmgr_mgr.py @@ -2,7 +2,7 @@ import pytest from amitools.vamos.log import log_libmgr, log_exec from amitools.vamos.libcore import LibCtx -from amitools.vamos.libmgr import LibManager, LibMgrCfg, LibCfg +from amitools.vamos.libmgr import LibManager, LibMgrCfg, LibCfg, LibProxyManager from amitools.vamos.machine import Machine from amitools.vamos.mem import MemoryAlloc from amitools.vamos.lib.lexec.ExecLibCtx import ExecLibCtx @@ -28,7 +28,7 @@ def setup(path_mgr=None): cpu = machine.get_cpu() mem = machine.get_mem() cpu_type = machine.get_cpu_type() - exec_ctx = ExecLibCtx(machine, alloc, segloader, path_mgr) + exec_ctx = ExecLibCtx(machine, alloc, segloader, path_mgr, mgr) mgr.add_ctx("exec.library", exec_ctx) mgr.add_impl_cls("exec.library", ExecLibrary) dos_ctx = DosLibCtx(machine, alloc, segloader, path_mgr, None, None) @@ -49,8 +49,8 @@ def libmgr_mgr_bootstrap_shutdown_test(): vmgr = mgr.vlib_mgr assert vmgr.get_vlib_by_name("exec.library") == exec_vlib assert vmgr.get_vlib_by_addr(exec_base) == exec_vlib - assert exec_vlib.get_ctx() == vmgr.ctx_map.get_ctx("exec.library") - assert exec_lib.open_cnt == 1 + assert exec_vlib.get_ctx() == vmgr.ctx_map["exec.library"] + assert exec_lib.open_cnt.val == 1 assert machine.get_mem().r32(4) == exec_base # we can't expunge exec assert not mgr.expunge_lib(exec_base) @@ -94,9 +94,15 @@ def libmgr_mgr_open_vlib_test(): assert impl assert impl.get_cnt() == 1 lib = test_vlib.get_library() - assert lib.version == impl.get_version() + assert lib.version.val == impl.get_version() mgr.close_lib(test_base) assert impl.get_cnt() is None + # try to open a proxy + proxy_mgr = LibProxyManager(mgr) + proxy = proxy_mgr.open_lib_proxy("vamostest.library") + assert proxy + assert proxy.Add(2, 3) == 5 + proxy_mgr.close_lib_proxy(proxy) # shutdown left = mgr.shutdown() assert left == 0 @@ -118,9 +124,15 @@ def libmgr_mgr_open_vlib_dev_test(): assert impl assert impl.get_cnt() == 1 lib = test_vlib.get_library() - assert lib.version == impl.get_version() + assert lib.version.val == impl.get_version() mgr.close_lib(test_base) assert impl.get_cnt() is None + # try to open a proxy + proxy_mgr = LibProxyManager(mgr) + proxy = proxy_mgr.open_lib_proxy("vamostestdev.device") + assert proxy + assert proxy.Add(1, 2) == 3 + proxy_mgr.close_lib_proxy(proxy) # shutdown left = mgr.shutdown() assert left == 0 @@ -143,7 +155,7 @@ def libmgr_mgr_open_vlib_fake_fd_test(): impl = test_vlib.get_impl() assert impl is None lib = test_vlib.get_library() - assert lib.version == 40 + assert lib.version.val == 40 mgr.close_lib(test_base) # shutdown left = mgr.shutdown() @@ -170,8 +182,8 @@ def libmgr_mgr_open_vlib_fake_no_fd_test(): impl = test_vlib.get_impl() assert impl is None lib = test_vlib.get_library() - assert lib.version == 40 - assert lib.neg_size == 68 + assert lib.version.val == 40 + assert lib.neg_size.val == 68 mgr.close_lib(test_base) # shutdown left = mgr.shutdown() @@ -250,6 +262,12 @@ def open_alib(lib_file, lib_name, ok=True, version=0, mode=None): assert not amgr.is_load_addr(lib_base) lib_info = amgr.get_lib_info_for_name(lib_name) assert not lib_info + # proxy + proxy_mgr = LibProxyManager(mgr) + proxy = proxy_mgr.open_lib_proxy(lib_name, run_sp=h.sp) + assert proxy + assert proxy.Add(3, 4) == 7 + proxy_mgr.close_lib_proxy(proxy, run_sp=h.sp) # shutdown h.shutdown() diff --git a/test/unit/libnative_initres.py b/test/unit/libnative_initres.py index 9ee91f4e..568c5603 100644 --- a/test/unit/libnative_initres.py +++ b/test/unit/libnative_initres.py @@ -2,13 +2,8 @@ from amitools.vamos.mem import MemoryAlloc from amitools.vamos.machine import Machine from amitools.vamos.machine.regs import * -from amitools.vamos.atypes import ( - Resident, - ResidentFlags, - NodeType, - AutoInit, - ExecLibrary, -) +from amitools.vamos.libtypes import Resident, ExecLibrary, Library +from amitools.vamos.libstructs import ResidentFlags, NodeType, AutoInitStruct from amitools.vamos.libnative import InitRes @@ -43,8 +38,10 @@ def init_func(op, pc): trap_id = traps.setup(init_func, auto_rts=True) mem.w16(init_addr, trap_id | 0xA000) # build fake resident - res = Resident.alloc(alloc, "bla.library", "blub") - res.setup(flags=0, version=42, type=NodeType.NT_LIBRARY, pri=-7, init=init_addr) + res = Resident.alloc(alloc, name="bla.library", id_string="blub") + res.new_resident( + flags=0, version=42, type=NodeType.NT_LIBRARY, pri=-7, init=init_addr + ) # init resident ir = InitRes(machine, alloc) lib_base, mem_obj = ir.init_resident(res.get_addr(), seglist.get_baddr(), run_sp=sp) @@ -79,16 +76,17 @@ def init_func(op, pc): mem.w32(vectors + 8, 0x800) mem.w32(vectors + 12, 0xFFFFFFFF) # build fake resident - res = Resident.alloc(alloc, "bla.library", "blub") - res.setup( + res = Resident.alloc(alloc, name="bla.library", id_string="blub") + res.new_resident( flags=ResidentFlags.RTF_AUTOINIT, version=42, type=NodeType.NT_LIBRARY, pri=-7 ) - auto_init = AutoInit.alloc(alloc) - auto_init.setup(functions=vectors, init_func=init_addr) - res.set_auto_init(auto_init) + auto_init = AutoInitStruct.alloc(alloc, functions=vectors, init_func=init_addr) + res.init.ref = auto_init # setup exec lib - exec_lib = ExecLibrary.alloc(alloc, "exec.library", "bla", 36) - exec_lib.setup() + exec_lib = ExecLibrary.alloc( + alloc, name="exec.library", id_string="bla", neg_size=36 + ) + exec_lib.new_lib() mem.w32(4, exec_lib.get_addr()) # init resident ir = InitRes(machine, alloc) @@ -97,6 +95,12 @@ def init_func(op, pc): ) assert lib_base assert mem_obj + # check lib + lib = Library(mem, lib_base) + assert lib.name.str == "bla.library" + assert lib.id_string.str == "blub" + assert lib.neg_size.val == 20 # fits the 3*6 vectors + # clean up seglist.free() res.free() auto_init.free() diff --git a/test/unit/libnative_libfuncs.py b/test/unit/libnative_libfuncs.py index 1cbf0778..a7004dab 100644 --- a/test/unit/libnative_libfuncs.py +++ b/test/unit/libnative_libfuncs.py @@ -3,7 +3,8 @@ from amitools.vamos.machine.opcodes import op_jmp from amitools.vamos.mem import MemoryAlloc from amitools.vamos.libnative import LibFuncs -from amitools.vamos.atypes import ExecLibrary, Library, LibFlags +from amitools.vamos.libtypes import ExecLibrary, Library +from amitools.vamos.libstructs import LibFlags from amitools.vamos.loader import SegList, SegmentLoader @@ -12,23 +13,31 @@ def libnative_libfuncs_add_library_test(): mem = machine.get_mem() alloc = MemoryAlloc.for_machine(machine) # setup exec lib - exec_lib = ExecLibrary.alloc(alloc, "exec.library", "bla", 36) - exec_lib.setup() + exec_lib = ExecLibrary.alloc( + alloc, name="exec.library", id_string="bla", neg_size=36 + ) + assert exec_lib.LibNode.neg_size.val == 36 + assert exec_lib.LibNode.pos_size.val == ExecLibrary.get_size() + exec_lib.new_lib() mem.w32(4, exec_lib.get_addr()) # new lib - lib = Library.alloc(alloc, "my.library", "bla", 36) - lib.setup() + lib = Library.alloc(alloc, name="my.library", id_string="bla", neg_size=36) + assert lib.neg_size.val == 36 + lib.new_lib() mem.w32(lib.get_addr() - 36, 0xDEADBEEF) - # check lib sum - assert lib.sum == 0 + # check initial lib sum + assert lib.sum.val == 0 + assert lib.calc_sum() == 0xDEADBEEF # add lib lf = LibFuncs(machine, alloc) - lf.add_library(lib.get_addr()) + lf.add_library(lib.addr) # check that lib was added assert len(exec_lib.lib_list) == 1 - assert [a for a in exec_lib.lib_list] == [lib] - assert lib.sum == 0xDEADBEEF - assert lf.find_library("my.library") == lib.get_addr() + libs = [a for a in exec_lib.lib_list] + assert list(map(lambda x: x.addr, libs)) == [lib.addr] + # check that libsum was calced + assert lib.sum.val == 0xDEADBEEF + assert lf.find_library("my.library") == lib.addr # cleanup lib.free() exec_lib.free() @@ -40,13 +49,13 @@ def libnative_libfuncs_sum_library_test(): mem = machine.get_mem() alloc = MemoryAlloc.for_machine(machine) # new lib - lib = Library.alloc(alloc, "my.library", "bla", 36) - lib.setup() + lib = Library.alloc(alloc, name="my.library", id_string="bla", neg_size=36) + lib.new_lib() mem.w32(lib.get_addr() - 36, 0xDEADBEEF) # sum lib lf = LibFuncs(machine, alloc) lf.sum_library(lib.get_addr()) - assert lib.sum == 0xDEADBEEF + assert lib.sum.val == 0xDEADBEEF # cleanup lib.free() assert alloc.is_all_free() @@ -61,8 +70,8 @@ def libnative_libfuncs_rem_library_test(): alloc = MemoryAlloc.for_machine(machine) segloader = SegmentLoader(alloc) # new lib - lib = Library.alloc(alloc, "my.library", "bla", 36) - lib.setup() + lib = Library.alloc(alloc, name="my.library", id_string="bla", neg_size=36) + lib.new_lib() # setup seglist seglist = SegList.alloc(alloc, [64]) segloader.register_seglist(seglist.get_baddr()) @@ -94,8 +103,8 @@ def libnative_libfuncs_close_library_test(): alloc = MemoryAlloc.for_machine(machine) segloader = SegmentLoader(alloc) # new lib - lib = Library.alloc(alloc, "my.library", "bla", 36) - lib.setup() + lib = Library.alloc(alloc, name="my.library", id_string="bla", neg_size=36) + lib.new_lib() # setup seglist seglist = SegList.alloc(alloc, [64]) segloader.register_seglist(seglist.get_baddr()) @@ -126,8 +135,8 @@ def libnative_libfuncs_open_library_test(): sp = machine.get_ram_begin() - 4 alloc = MemoryAlloc.for_machine(machine) # new lib - lib = Library.alloc(alloc, "my.library", "bla", 36) - lib.setup() + lib = Library.alloc(alloc, name="my.library", id_string="bla", neg_size=36) + lib.new_lib() # setup open func def open_func(op, pc): @@ -153,11 +162,11 @@ def libnative_libfuncs_set_function_test(): sp = machine.get_ram_begin() - 4 alloc = MemoryAlloc.for_machine(machine) # new lib - lib = Library.alloc(alloc, "my.library", "bla", 36) + lib = Library.alloc(alloc, name="my.library", id_string="bla", neg_size=36) lib_addr = lib.get_addr() - lib.setup() + lib.new_lib() lib.fill_funcs(op_jmp, 0xCAFEBABE) - assert lib.neg_size == 36 + assert lib.neg_size.val == 36 # patch function lvo = -30 addr = lib.get_addr() + lvo diff --git a/test/unit/libnative_loader.py b/test/unit/libnative_loader.py index 48e6e604..d6c92475 100644 --- a/test/unit/libnative_loader.py +++ b/test/unit/libnative_loader.py @@ -2,7 +2,7 @@ from amitools.vamos.loader import SegmentLoader from amitools.vamos.mem import MemoryAlloc from amitools.vamos.machine import Machine -from amitools.vamos.atypes import ExecLibrary, Library +from amitools.vamos.libtypes import ExecLibrary, Library def setup(path_mgr=None): @@ -13,8 +13,11 @@ def setup(path_mgr=None): loader = LibLoader(machine, alloc, segload) sp = machine.get_ram_begin() - 4 # setup exec - exec_lib = ExecLibrary.alloc(alloc, "exec.library", "bla", 520 * 6) - exec_lib.setup() + exec_lib = ExecLibrary.alloc( + alloc, name="exec.library", id_string="bla", neg_size=520 * 6 + ) + assert exec_lib.LibNode.neg_size.val == 520 * 6 + exec_lib.new_lib() exec_lib.fill_funcs() exec_base = exec_lib.get_addr() mem.w32(4, exec_base) @@ -22,6 +25,13 @@ def setup(path_mgr=None): return loader, segload, alloc, mem, sp, exec_lib +def free_lib(mem, alloc, lib_base): + lib = Library(mem, lib_base) + mem_obj = alloc.get_memory(lib_base - lib.neg_size.val) + assert mem_obj + alloc.free_memory(mem_obj) + + def libnative_loader_load_sys_lib_test(buildlibnix): loader, segload, alloc, mem, sp, exec_lib = setup() # load @@ -30,8 +40,7 @@ def libnative_loader_load_sys_lib_test(buildlibnix): assert lib_base > 0 assert seglist_baddr > 0 # we have to manually clean the lib here (as Exec FreeMem() does not work) - lib = Library(mem, lib_base, alloc) - lib.free() + free_lib(mem, alloc, lib_base) # cleanup segload.unload_seglist(seglist_baddr) assert segload.shutdown() == 0 @@ -71,8 +80,7 @@ def ami_to_sys_path(self, lock, ami_path, mustExist=True): assert info.ami_file == "LIBS:testnix.library" assert info.sys_file == lib_file # we have to manually clean the lib here (as Exec FreeMem() does not work) - lib = Library(mem, lib_base, alloc) - lib.free() + free_lib(mem, alloc, lib_base) # cleanup segload.unload_seglist(seglist_baddr) assert segload.shutdown() == 0 diff --git a/test/unit/libnative_makelib.py b/test/unit/libnative_makelib.py index b2d28558..15941243 100644 --- a/test/unit/libnative_makelib.py +++ b/test/unit/libnative_makelib.py @@ -2,8 +2,8 @@ from amitools.vamos.mem import MemoryAlloc from amitools.vamos.machine import Machine from amitools.vamos.machine.opcodes import * -from amitools.vamos.astructs import LibraryStruct -from amitools.vamos.atypes import Library +from amitools.vamos.libstructs import LibraryStruct +from amitools.vamos.libtypes import Library def libnative_makelib_test(): @@ -23,7 +23,7 @@ def libnative_makelib_test(): init_tab = 0x200 ib = InitStructBuilder(mem, init_tab) - name_off = LibraryStruct.get_field_offset_for_path("lib_Node.ln_Name") + name_off = LibraryStruct.sdef.lib_Node.ln_Name.base_offset ib.init_long(name_off, name_addr) ib.end() @@ -41,9 +41,9 @@ def libnative_makelib_test(): # check library lib = Library(mem, lib_base) - assert lib.get_name() == "bla.library" - assert lib.get_pos_size() == 36 - assert lib.get_neg_size() == 20 # round_long(3*6) + assert lib.name.str == "bla.library" + assert lib.pos_size.val == 36 + assert lib.neg_size.val == 20 # round_long(3*6) assert mem.r32(lib_base - 4) == 0x400 assert mem.r32(lib_base - 10) == 0x600 assert mem.r32(lib_base - 16) == 0x800 diff --git a/test/unit/libnative_mgr.py b/test/unit/libnative_mgr.py index e124efd0..816e4029 100644 --- a/test/unit/libnative_mgr.py +++ b/test/unit/libnative_mgr.py @@ -5,7 +5,7 @@ from amitools.vamos.loader import SegmentLoader from amitools.vamos.mem import MemoryAlloc from amitools.vamos.machine import Machine -from amitools.vamos.atypes import ExecLibrary, Library +from amitools.vamos.libtypes import ExecLibrary, Library def setup(): @@ -14,8 +14,10 @@ def setup(): alloc = MemoryAlloc.for_machine(machine) sp = machine.get_ram_begin() - 4 # setup exec - exec_lib = ExecLibrary.alloc(alloc, "exec.library", "bla", 520 * 6) - exec_lib.setup() + exec_lib = ExecLibrary.alloc( + alloc, name="exec.library", id_string="bla", neg_size=520 * 6 + ) + exec_lib.new_lib() exec_lib.fill_funcs() exec_base = exec_lib.get_addr() mem.w32(4, exec_base) @@ -23,6 +25,13 @@ def setup(): return machine, alloc, sp, mem, exec_lib +def free_lib(mem, alloc, lib_base): + lib = Library(mem, lib_base) + mem_obj = alloc.get_memory(lib_base - lib.neg_size.val) + assert mem_obj + alloc.free_memory(mem_obj) + + def libnative_mgr_test(buildlibnix): if buildlibnix.flavor not in ("gcc", "gcc-dbg"): pytest.skip("only single base lib supported") @@ -46,6 +55,7 @@ def ami_to_sys_path(self, lock, ami_path, mustExist=True): lib_info = mgr.get_lib_info_for_name("testnix.library") assert lib_info assert lib_info.is_base_addr(lib_base) + assert lib_info.get_lib_fd() # close lib seglist = mgr.close_lib(lib_base, run_sp=sp) assert seglist == 0 @@ -59,8 +69,7 @@ def ami_to_sys_path(self, lock, ami_path, mustExist=True): lib_info = mgr.get_lib_info_for_name("testnix.library") assert not lib_info # we have to manually clean the lib here (as Exec FreeMem() does not work) - lib = Library(mem, lib_base, alloc) - lib.free() + free_lib(mem, alloc, lib_base) # cleanup exec_lib.free() assert alloc.is_all_free() diff --git a/test/unit/libstructs_dos.py b/test/unit/libstructs_dos.py new file mode 100644 index 00000000..1bd4cda9 --- /dev/null +++ b/test/unit/libstructs_dos.py @@ -0,0 +1,9 @@ +import pytest +from amitools.vamos.libstructs import DosLibraryStruct +from amitools.vamos.machine import MockMemory + + +def libstructs_dos_dosbase_test(): + mem = MockMemory() + dosbase = DosLibraryStruct(mem, 0x100) + assert dosbase.get_byte_size() == 70 diff --git a/test/unit/libstructs_exec.py b/test/unit/libstructs_exec.py new file mode 100644 index 00000000..7740c1e5 --- /dev/null +++ b/test/unit/libstructs_exec.py @@ -0,0 +1,29 @@ +import pytest +from amitools.vamos.libstructs import NodeStruct, TaskStruct, ExecLibraryStruct +from amitools.vamos.machine import MockMemory + + +def libstructs_exec_node_test(): + mem = MockMemory() + node = NodeStruct(mem, 0x42) + node.ln_Succ.set(42) + node.ln_Pred.set(21) + node.ln_Pri.set(-27) + assert node.ln_Succ.get() == 42 + assert node.ln_Pred.get() == 21 + assert node.ln_Pri.get() == -27 + + +def libstructs_exec_task_test(): + mem = MockMemory() + task = TaskStruct(mem, 0x40) + task.tc_Node.ln_Succ.set(42) + task.tc_Node.ln_Pred.set(21) + assert task.tc_Node.ln_Succ.get() == 42 + assert task.tc_Node.ln_Pred.get() == 21 + + +def libstructs_exec_execbase_test(): + mem = MockMemory() + execbase = ExecLibraryStruct(mem, 0x100) + assert execbase.get_byte_size() == 634 diff --git a/test/unit/libstructs_util.py b/test/unit/libstructs_util.py new file mode 100644 index 00000000..6cab1952 --- /dev/null +++ b/test/unit/libstructs_util.py @@ -0,0 +1,9 @@ +import pytest +from amitools.vamos.libstructs import ClockDataStruct +from amitools.vamos.machine import MockMemory + + +def libstructs_util_clockdata_test(): + mem = MockMemory() + clock_data = ClockDataStruct(mem, 0x100) + assert clock_data.get_byte_size() == 14 diff --git a/test/unit/libtypes_execlib.py b/test/unit/libtypes_execlib.py new file mode 100644 index 00000000..dc745ea0 --- /dev/null +++ b/test/unit/libtypes_execlib.py @@ -0,0 +1,24 @@ +from amitools.vamos.machine import MockMemory +from amitools.vamos.mem import MemoryAlloc +from amitools.vamos.libtypes import ExecLibrary + + +def libtypes_execlib_base_test(): + mem = MockMemory() + el = ExecLibrary(mem, 0x100) + el.new_lib() + el.fill_funcs() + + +def libtypes_execlib_alloc_test(): + mem = MockMemory() + alloc = MemoryAlloc(mem) + el = ExecLibrary.alloc(alloc, name="exec.library", id_string="bla", neg_size=20) + assert el.name.str == "exec.library" + assert el.id_string.str == "bla" + assert el.neg_size.val == 20 + assert el.pos_size.val == ExecLibrary.get_size() + el.new_lib() + el.fill_funcs() + el.free() + assert alloc.is_all_free() diff --git a/test/unit/libtypes_library.py b/test/unit/libtypes_library.py new file mode 100644 index 00000000..7ef5d8ac --- /dev/null +++ b/test/unit/libtypes_library.py @@ -0,0 +1,112 @@ +import pytest +from amitools.vamos.machine.opcodes import op_rts +from amitools.vamos.libstructs import LibraryStruct, NodeType, LibFlags +from amitools.vamos.libtypes import Library +from amitools.vamos.label import LabelLib +from amitools.fd import read_lib_fd + + +def libtypes_library_base_test(mem_alloc): + mem, alloc = mem_alloc + # alloc lib + name = "my.library" + id_str = "my.library 1.2" + neg_size = 12 + pos_size = LibraryStruct.get_size() + lib = Library.alloc(alloc, name=name, id_string=id_str, neg_size=neg_size) + assert lib._mem_obj.size == neg_size + pos_size + assert pos_size == lib.pos_size.val + assert neg_size == lib.neg_size.val + assert lib.node.name.str == name + assert lib.id_string.str == id_str + # lib setup + flags = LibFlags.LIBF_SUMMING | LibFlags.LIBF_CHANGED + ltype = NodeType.NT_DEVICE + pri = -3 + ver = 1 + rev = 2 + lib.new_lib(version=ver, revision=rev, pri=pri, flags=flags, type=ltype) + # check lib + node = lib.node + assert node.succ.ref is None + assert node.pred.ref is None + assert node.type.val == ltype + assert node.pri.val == pri + assert lib.flags.val == flags + assert lib.pad.val == 0 + assert lib.neg_size.val == neg_size + assert lib.pos_size.val == pos_size + assert lib.version.val == ver + assert lib.revision.val == rev + assert lib.sum.val == 0 + assert lib.open_cnt.val == 0 + assert lib.node.name.str == name + assert lib.id_string.str == id_str + # fill funcs + lib.fill_funcs() + lib_base = lib.get_addr() + assert mem.r16(lib_base - 6) == op_rts + # done + lib.free() + assert alloc.is_all_free() + + +def libtypes_library_sum_test(mem_alloc): + mem, alloc = mem_alloc + # alloc lib + name = "my.library" + id_str = "my.library 1.2" + neg_size = 30 + lib = Library.alloc(alloc, name=name, id_string=id_str, neg_size=neg_size) + # assume rounded neg size + assert lib.neg_size.val == 32 + mem.w32(lib.addr - 32, 0xDEADBEEF) + mem.w32(lib.addr - 28, 0xCAFEBABE) + my_sum = (0xDEADBEEF + 0xCAFEBABE) & 0xFFFFFFFF + lib_sum = lib.calc_sum() + assert lib_sum == my_sum + lib.update_sum() + assert lib.sum.val == my_sum + assert lib.check_sum() + # done + lib.free() + assert alloc.is_all_free() + + +def libtypes_library_open_cnt_test(mem_alloc): + mem, alloc = mem_alloc + # alloc lib + name = "my.library" + id_str = "my.library 1.2" + neg_size = 30 + pos_size = LibraryStruct.get_size() + lib = Library.alloc(alloc, name=name, id_string=id_str, neg_size=neg_size) + # test open cnt + assert lib.open_cnt.val == 0 + lib.inc_open_cnt() + assert lib.open_cnt.val == 1 + lib.dec_open_cnt() + assert lib.open_cnt.val == 0 + # done + lib.free() + assert alloc.is_all_free() + + +def libtypes_library_label_test(mem_alloc): + mem, alloc = mem_alloc + name = "vamostest.library" + id_str = "vamostest.library 0.1" + fd = read_lib_fd("vamostest.library") + neg_size = fd.get_neg_size() + lib = Library.alloc(alloc, name=name, id_string=id_str, neg_size=neg_size, fd=fd) + # check for label + label = lib._mem_obj.label + if alloc.get_label_mgr(): + assert label + assert isinstance(label, LabelLib) + assert label.fd == fd + else: + assert not label + # done + lib.free() + assert alloc.is_all_free() diff --git a/test/unit/atypes_list.py b/test/unit/libtypes_list.py similarity index 83% rename from test/unit/atypes_list.py rename to test/unit/libtypes_list.py index efb502ab..9b72bbec 100644 --- a/test/unit/atypes_list.py +++ b/test/unit/libtypes_list.py @@ -1,7 +1,7 @@ from amitools.vamos.machine import MockMemory from amitools.vamos.mem import MemoryAlloc -from amitools.vamos.atypes import List, MinList, Node, NodeType, MinNode -from amitools.vamos.astructs import ListStruct, MinListStruct +from amitools.vamos.libtypes import List, MinList, Node, MinNode +from amitools.vamos.libstructs import ListStruct, MinListStruct, NodeType def new_list(): @@ -18,21 +18,21 @@ def new_min_list(): return l -def atypes_list_new_test(): +def libtypes_list_new_test(): l = new_list() assert str(l) == "[List:@000040,h=000044,t=000000,tp=000040,NT_DEVICE]" assert len(l) == 0 assert [a for a in l] == [] -def atypes_list_min_new_test(): +def libtypes_list_min_new_test(): l = new_min_list() assert str(l) == "[MinList:@000040,h=000044,t=000000,tp=000040]" assert len(l) == 0 assert [a for a in l] == [] -def atypes_list_add_head_test(): +def libtypes_list_add_head_test(): l = new_list() n1 = Node(l.mem, 0x50) assert len(l) == 0 @@ -45,7 +45,7 @@ def atypes_list_add_head_test(): assert [a for a in l] == [n2, n1] -def atypes_list_add_tail_test(): +def libtypes_list_add_tail_test(): l = new_list() n1 = Node(l.mem, 0x50) assert len(l) == 0 @@ -58,7 +58,7 @@ def atypes_list_add_tail_test(): assert [a for a in l] == [n1, n2] -def atypes_list_rem_head_test(): +def libtypes_list_rem_head_test(): l = new_list() n1 = Node(l.mem, 0x50) l.add_tail(n1) @@ -75,7 +75,7 @@ def atypes_list_rem_head_test(): assert len(l) == 0 -def atypes_list_rem_tail_test(): +def libtypes_list_rem_tail_test(): l = new_list() n1 = Node(l.mem, 0x50) l.add_tail(n1) @@ -92,7 +92,7 @@ def atypes_list_rem_tail_test(): assert len(l) == 0 -def atypes_list_remove_node_test(): +def libtypes_list_remove_node_test(): l = new_list() n1 = Node(l.mem, 0x50) l.add_tail(n1) @@ -107,7 +107,7 @@ def atypes_list_remove_node_test(): assert len(l) == 0 -def atypes_list_insert_node_test(): +def libtypes_list_insert_node_test(): l = new_list() n1 = Node(l.mem, 0x50) l.add_tail(n1) @@ -132,34 +132,30 @@ def atypes_list_insert_node_test(): assert [a for a in l] == [n5, n1, n3, n2, n4] -def atypes_list_enqueue_node_test(): +def libtypes_list_enqueue_node_test(): l = new_list() - n1 = Node(l.mem, 0x50) - n1.set_pri(0) + n1 = Node(l.mem, 0x50, pri=0) l.enqueue(n1) assert len(l) == 1 assert [a for a in l] == [n1] # same pri - n2 = Node(l.mem, 0x60) - n2.set_pri(0) + n2 = Node(l.mem, 0x60, pri=0) l.enqueue(n2) assert len(l) == 2 assert [a for a in l] == [n1, n2] # higher pri - n3 = Node(l.mem, 0x70) - n3.set_pri(1) + n3 = Node(l.mem, 0x70, pri=1) l.enqueue(n3) assert len(l) == 3 assert [a for a in l] == [n3, n1, n2] # lower pri - n4 = Node(l.mem, 0x80) - n4.set_pri(-1) + n4 = Node(l.mem, 0x80, pri=-1) l.enqueue(n4) assert len(l) == 4 assert [a for a in l] == [n3, n1, n2, n4] -def atypes_list_iter_at_test(): +def libtypes_list_iter_at_test(): l = new_list() n1 = Node(l.mem, 0x50) l.add_tail(n1) @@ -173,26 +169,26 @@ def atypes_list_iter_at_test(): def add_node(alist, addr, name): n = Node(alist.mem, addr) - addr += n.get_type_size() + addr += n.get_size() name_addr = addr alist.mem.w_cstr(addr, name) addr += len(name) + 1 if addr & 3 != 0: addr = (addr & ~0x3) + 4 - n.set_name(name_addr) + n.name.aptr = name_addr alist.add_tail(n) return n, addr -def atypes_list_find_name_test(): +def libtypes_list_find_name_test(): l = new_list() addr = 0x60 n1, addr = add_node(l, addr, "hello") n2, addr = add_node(l, addr, "world") n3, addr = add_node(l, addr, "hello") - assert n1.get_name() == "hello" - assert n2.get_name() == "world" - assert n3.get_name() == "hello" + assert n1.name.str == "hello" + assert n2.name.str == "world" + assert n3.name.str == "hello" assert len(l) == 3 assert [a for a in l] == [n1, n2, n3] assert [a for a in l.find_names("bla")] == [] @@ -204,7 +200,7 @@ def atypes_list_find_name_test(): assert n.find_name("hello") == n3 -def atypes_list_alloc_test(): +def libtypes_list_alloc_test(): mem = MockMemory() alloc = MemoryAlloc(mem) l = List.alloc(alloc) @@ -213,7 +209,7 @@ def atypes_list_alloc_test(): l.free() -def atypes_list_alloc_min_test(): +def libtypes_list_alloc_min_test(): mem = MockMemory() alloc = MemoryAlloc(mem) l = MinList.alloc(alloc) diff --git a/test/unit/libtypes_msg.py b/test/unit/libtypes_msg.py new file mode 100644 index 00000000..e7adf312 --- /dev/null +++ b/test/unit/libtypes_msg.py @@ -0,0 +1,46 @@ +import pytest +from amitools.vamos.machine import MockMemory +from amitools.vamos.mem import MemoryAlloc +from amitools.vamos.libtypes import MsgPort, Message +from amitools.vamos.libstructs import MsgPortFlags, NodeType + + +def libtypes_msg_msgport_test(): + mem = MockMemory() + alloc = MemoryAlloc(mem) + # alloc msg port + mp1 = MsgPort.alloc(alloc) + assert mp1.name.aptr == 0 + mp1.new_port(pri=-5, flags=MsgPortFlags.PA_SOFTINT, sig_bit=5) + assert mp1.node.pri.val == -5 + assert mp1.node.type.val == NodeType.NT_MSGPORT + assert mp1.flags.val == MsgPortFlags.PA_SOFTINT + assert mp1.sig_bit.val == 5 + assert mp1.sig_task.aptr == 0 + assert len(mp1.msg_list) == 0 + # with name + mp2 = MsgPort.alloc(alloc, name="bla") + assert mp2.name.str == "bla" + # free + mp1.free() + mp2.free() + assert alloc.is_all_free() + + +def libtypes_msg_msg_test(): + mem = MockMemory() + alloc = MemoryAlloc(mem) + # alloc msg + msg = Message.alloc(alloc) + assert msg.name.aptr == 0 + msg.new_msg(pri=-7, length=10) + assert msg.pri.val == -7 + assert msg.type.val == NodeType.NT_MESSAGE + assert msg.length.val == 10 + # with name + msg2 = Message.alloc(alloc, name="bla") + assert msg2.name.str == "bla" + # free + msg.free() + msg2.free() + assert alloc.is_all_free() diff --git a/test/unit/libtypes_node.py b/test/unit/libtypes_node.py new file mode 100644 index 00000000..1d687194 --- /dev/null +++ b/test/unit/libtypes_node.py @@ -0,0 +1,122 @@ +import pytest +from amitools.vamos.machine import MockMemory +from amitools.vamos.mem import MemoryAlloc +from amitools.vamos.libtypes import Node, MinNode +from amitools.vamos.libstructs import NodeStruct, MinNodeStruct, NodeType + + +def libtypes_node_type_to_str_test(): + assert NodeType.to_str(NodeType.NT_UNKNOWN) == "NT_UNKNOWN" + with pytest.raises(ValueError): + NodeType.to_str(-1) + + +def libtypes_node_type_from_str_test(): + assert NodeType.from_str("NT_INTERRUPT") == NodeType.NT_INTERRUPT + with pytest.raises(ValueError): + NodeType.from_str("bla") + + +def libtypes_node_base_test(): + mem = MockMemory() + text = "hello, world!" + mem.w_cstr(12, text) + node = Node(mem, 0x42) + # set node + node.succ.aptr = 1234 + node.pred.aptr = 5678 + node.type.val = NodeType.NT_LIBRARY + node.pri.val = -3 + node.name.aptr = 12 + # check node + assert node.succ.aptr == 1234 + assert node.pred.aptr == 5678 + assert node.type.val == NodeType.NT_LIBRARY + assert node.pri.val == -3 + assert node.name.aptr == 12 + assert node.name.str == text + + +def libtypes_node_setup_test(): + mem = MockMemory() + text = "hello, world!" + mem.w_cstr(12, text) + node = Node( + mem, 0x42, succ=1234, pred=5678, type=NodeType.NT_DEVICE, pri=-5, name=12 + ) + # check node + assert node.succ.aptr == 1234 + assert node.pred.aptr == 5678 + assert node.type.val == NodeType.NT_DEVICE + assert node.pri.val == -5 + assert node.name.aptr == 12 + assert node.name.str == text + node.type.val = NodeType.NT_DEVICE + + +def libtypes_node_setup_min_test(): + mem = MockMemory() + node = MinNode(mem, 0x42, succ=1234, pred=5678) + # check node + assert node.succ.aptr == 1234 + assert node.pred.aptr == 5678 + + +def libtypes_node_str_test(): + mem = MockMemory() + text = "hello, world!" + mem.w_cstr(12, text) + node = Node( + mem, 0x42, succ=0x1234, pred=0x5678, type=NodeType.NT_DEVICE, pri=-5, name=12 + ) + assert str(node) == "[Node:@000042,p=005678,s=001234,NT_DEVICE,-5,'hello, world!']" + + +def libtypes_node_str_min_test(): + mem = MockMemory() + min_node = MinNode(mem, 0x80, succ=0x1234, pred=0x5678) + assert str(min_node) == "[MinNode:@000080,p=005678,s=001234]" + + +def libtypes_node_remove_test(): + mem = MockMemory() + text = "hello, world!" + mem.w_cstr(12, text) + node = Node( + mem, 0x80, succ=0x60, pred=0x100, type=NodeType.NT_DEVICE, pri=-5, name=12 + ) + node.remove() + + +def libtypes_node_remove_min_test(): + mem = MockMemory() + min_node = MinNode(mem, 0x80, succ=0x60, pred=0x100) + min_node.remove() + + +def libtypes_node_alloc_test(): + mem = MockMemory() + alloc = MemoryAlloc(mem) + node = Node.alloc(alloc) + assert node.get_size() == NodeStruct.get_size() + node.free() + assert alloc.is_all_free() + + +def libtypes_node_alloc_name_test(): + mem = MockMemory() + alloc = MemoryAlloc(mem) + node = Node.alloc(alloc, name="foobar") + assert node.get_size() == NodeStruct.get_size() + assert node.name.str == "foobar" + node.free() + assert alloc.is_all_free() + + +def libtypes_node_alloc_min_test(): + mem = MockMemory() + alloc = MemoryAlloc(mem) + node = MinNode.alloc(alloc) + assert node.get_size() == MinNodeStruct.get_size() + node.free() + assert alloc.is_all_free() diff --git a/test/unit/atypes_process.py b/test/unit/libtypes_process.py similarity index 50% rename from test/unit/atypes_process.py rename to test/unit/libtypes_process.py index 3f163d57..d347ce36 100644 --- a/test/unit/atypes_process.py +++ b/test/unit/libtypes_process.py @@ -1,48 +1,47 @@ import pytest from amitools.vamos.machine import MockMemory from amitools.vamos.mem import MemoryAlloc -from amitools.vamos.astructs import BAddr -from amitools.vamos.atypes import Process, NodeType, CLI +from amitools.vamos.libstructs import NodeType +from amitools.vamos.libtypes import Process, CLI -def atypes_process_base_test(): +def libtypes_process_base_test(): mem = MockMemory() alloc = MemoryAlloc(mem) # alloc proc name = "my_proc" - proc = Process.alloc(alloc, name) - assert proc.get_name() == name + proc = Process.alloc(alloc, name=name) + assert proc.name.str == name # proc setup - proc.setup() + proc.new_proc() node = proc.task.node - assert node.type == NodeType.NT_PROCESS + assert node.type.val == NodeType.NT_PROCESS # done proc.free() assert alloc.is_all_free() -def atypes_process_bptr_test(): +def libtypes_process_bptr_test(): mem = MockMemory() alloc = MemoryAlloc(mem) # alloc proc proc = Process.alloc(alloc) - # set list - proc.seg_list = 0x100 - assert proc.seg_list == BAddr(0x40) + # set list (as baddr) + proc.seg_list.bptr = 0x40 + assert proc.seg_list.bptr == 0x40 # check in mem seg list baddr - struct = proc.get_type_struct() - off = struct.pr_SegList_field.offset + off = proc.sdef.pr_SegList.offset addr = proc.addr + off assert mem.r32(addr) == 0x40 # setup CLI cli = CLI.alloc(alloc) - proc.cli = cli - assert type(proc.cli) is CLI - assert proc.cli == cli.addr + proc.cli.ref = cli + assert type(proc.cli.ref) is CLI + assert proc.cli.aptr == cli.addr # check in mem CLI baddr - off = struct.pr_CLI_field.offset + off = proc.sdef.pr_CLI.offset addr = proc.addr + off - assert mem.r32(addr) == BAddr.from_addr(cli.addr).get_baddr() + assert mem.r32(addr) == cli.addr >> 2 cli.free() # done proc.free() diff --git a/test/unit/libtypes_resident.py b/test/unit/libtypes_resident.py new file mode 100644 index 00000000..341cce69 --- /dev/null +++ b/test/unit/libtypes_resident.py @@ -0,0 +1,96 @@ +from amitools.vamos.loader import SegmentLoader +from amitools.vamos.machine import MockMemory +from amitools.vamos.mem import MemoryAlloc +from amitools.vamos.libstructs import ResidentFlags, NodeType +from amitools.vamos.libtypes import Resident + + +def load_lib(mem, buildlibnix): + lib_file = buildlibnix.make_lib("testnix") + alloc = MemoryAlloc(mem) + loader = SegmentLoader(alloc) + info = loader.int_load_sys_seglist(lib_file) + seg_list = info.seglist + seg = seg_list.get_segment() + addr = seg.get_addr() + size = seg.get_size() + end = seg.get_end() + return addr, size, end + + +def libtypes_resident_find_lib_test(buildlibnix): + mem = MockMemory(fill=23) + addr, size, end = load_lib(mem, buildlibnix) + # search list + res = Resident.find(mem, addr, size, only_first=False) + assert len(res) == 1 + assert res[0].is_valid() + res_obj = res[0] + assert res_obj.get_addr() > addr + assert res_obj.get_addr() < end + # search first only + res2 = Resident.find(mem, addr, size) + assert res == [res2] + assert res2.is_valid() + + +def libtypes_resident_read_lib_test(buildlibnix): + mem = MockMemory(fill=23) + addr, size, end = load_lib(mem, buildlibnix) + res = Resident.find(mem, addr, size) + assert res.match_word.val == res.RTC_MATCHWORD + assert res.match_tag.aptr == res.get_addr() + assert res.end_skip.aptr >= res.get_addr() + res.get_size() + assert res.flags.val == ResidentFlags.RTF_AUTOINIT + assert res.version.val == 0 + assert res.type.val == NodeType.NT_LIBRARY + assert res.pri.val == 0 + assert res.name.str == "testnix.library" + assert res.id_string.str == "testnix.library 1.0 (07.07.2007)" + ai = res.init.ref + assert ai + assert ai.addr == res.init.aptr + assert ai.pos_size.val > 0 + assert ai.functions.aptr > 0 + assert ai.init_struct.aptr == 0 + assert ai.init_func.aptr > 0 + assert res.is_valid() + + +def libtypes_resident_alloc_test(): + mem = MockMemory(fill=23) + alloc = MemoryAlloc(mem) + # alloc + res = Resident.alloc(alloc, name="bla.library", id_string="blub") + # free + res.free() + assert alloc.is_all_free() + + +def libtypes_resident_setup_test(): + mem = MockMemory(fill=23) + alloc = MemoryAlloc(mem) + # alloc + res2 = Resident.alloc(alloc, name="bla.library", id_string="blub") + res2.new_resident( + flags=ResidentFlags.RTF_AUTOINIT, + version=42, + type=NodeType.NT_DEVICE, + pri=-7, + init=0xDEADBEEF, + ) + # find resource + res = Resident.find(mem, 0, 1024) + assert res.match_word.val == res.RTC_MATCHWORD + assert res.match_tag.aptr == res.get_addr() + assert res.end_skip.aptr >= res.get_addr() + res.get_size() + assert res.flags.val == ResidentFlags.RTF_AUTOINIT + assert res.version.val == 42 + assert res.type.val == NodeType.NT_DEVICE + assert res.pri.val == -7 + assert res.name.str == "bla.library" + assert res.id_string.str == "blub" + assert res.init.aptr == 0xDEADBEEF + # free + res2.free() + assert alloc.is_all_free() diff --git a/test/unit/libtypes_task.py b/test/unit/libtypes_task.py new file mode 100644 index 00000000..1025a6e8 --- /dev/null +++ b/test/unit/libtypes_task.py @@ -0,0 +1,26 @@ +import pytest +from amitools.vamos.machine import MockMemory +from amitools.vamos.mem import MemoryAlloc +from amitools.vamos.libtypes import Task +from amitools.vamos.libstructs import TaskFlags, TaskState, NodeType + + +def libtypes_task_base_test(): + mem = MockMemory() + alloc = MemoryAlloc(mem) + # alloc task + name = "my_task" + task = Task.alloc(alloc, name=name) + assert task.name.str == name + # task setup + task.new_task(pri=-5, flags=TaskFlags.TF_LAUNCH) + node = task.node + assert node.type.val == NodeType.NT_TASK + assert node.pri.val == -5 + assert task.flags.val == TaskFlags.TF_LAUNCH + assert task.state.val == TaskState.TS_INVALID + assert len(task.mem_entry) == 0 + assert task.mem_entry.type.val == NodeType.NT_MEMORY + # done + task.free() + assert alloc.is_all_free() diff --git a/test/unit/machine_hwaccess.py b/test/unit/machine_hwaccess.py index dade42bb..ec43fd9e 100644 --- a/test/unit/machine_hwaccess.py +++ b/test/unit/machine_hwaccess.py @@ -16,6 +16,7 @@ def machine_hwaccess_create_test(): def machine_hwaccess_ignore_test(caplog): + caplog.set_level(logging.WARN, "hw") machine = Machine() mem = machine.get_mem() hw = HWAccess.from_mode_str(machine, "ignore") @@ -32,6 +33,7 @@ def machine_hwaccess_ignore_test(caplog): def machine_hwaccess_abort_test(caplog): + caplog.set_level(logging.WARN, "hw") machine = Machine() mem = machine.get_mem() hw = HWAccess.from_mode_str(machine, "abort") diff --git a/test/unit/profiler_main.py b/test/unit/profiler_main.py index 5b08747b..95760230 100644 --- a/test/unit/profiler_main.py +++ b/test/unit/profiler_main.py @@ -4,6 +4,7 @@ def profiler_main_disabled_test(caplog): + caplog.set_level(logging.DEBUG, "prof") mp = MainProfiler() assert mp.parse_config(None) assert not mp.add_profiler(Profiler()) @@ -13,6 +14,7 @@ def profiler_main_disabled_test(caplog): def profiler_main_config_test(caplog, tmpdir): + caplog.set_level(logging.INFO, "prof") path = str(tmpdir.join("prof.json")) mp = MainProfiler() cfg = ConfigDict( @@ -24,11 +26,13 @@ def profiler_main_config_test(caplog, tmpdir): assert mp.append mp.setup() mp.shutdown() - assert caplog.record_tuples == [] + assert caplog.record_tuples == [ + ("prof", logging.INFO, "---------- Profiling Results ----------"), + ] def profiler_main_def_profiler_test(caplog): - caplog.set_level(logging.INFO) + caplog.set_level(logging.INFO, "prof") p = Profiler() mp = MainProfiler(enabled=True) cfg = ConfigDict( @@ -45,7 +49,7 @@ def profiler_main_def_profiler_test(caplog): def profiler_main_file_test(caplog, tmpdir): - caplog.set_level(logging.DEBUG) + caplog.set_level(logging.DEBUG, "prof") path = str(tmpdir.join("prof.json")) p = Profiler() mp = MainProfiler(enabled=True) @@ -153,7 +157,7 @@ def profiler_main_test_prof_load_test(tmpdir): def profiler_main_test_prof_dump_test(caplog): - caplog.set_level(logging.INFO) + caplog.set_level(logging.INFO, "prof") cfg = ConfigDict( {"enabled": True, "output": {"dump": True, "file": None, "append": True}} ) diff --git a/test/unit/trace_mem.py b/test/unit/trace_mem.py index 76542077..450810eb 100644 --- a/test/unit/trace_mem.py +++ b/test/unit/trace_mem.py @@ -2,7 +2,7 @@ from amitools.vamos.trace import * from amitools.vamos.label import * from amitools.vamos.machine import * -from amitools.vamos.astructs import NodeStruct, LibraryStruct +from amitools.vamos.libstructs import NodeStruct, LibraryStruct from amitools.vamos.log import log_mem_int diff --git a/test/unit/trace_mgr.py b/test/unit/trace_mgr.py index 94f8c21a..f945eafd 100644 --- a/test/unit/trace_mgr.py +++ b/test/unit/trace_mgr.py @@ -2,7 +2,7 @@ from amitools.vamos.trace import TraceManager from amitools.vamos.label import * from amitools.vamos.machine import * -from amitools.vamos.astructs import NodeStruct, LibraryStruct +from amitools.vamos.libstructs import NodeStruct, LibraryStruct from amitools.vamos.cfgcore import ConfigDict from amitools.fd import read_lib_fd @@ -46,7 +46,7 @@ def check_log(chn, records): ( chn, lvl, - "W(2): 000208: 0015 Struct [@000200 +000008 node] Node+8 = ln_Type(UBYTE)+0", + "W(2): 000208: 0015 Struct [@000200 +000008 node] Node+8 = ln_Type(NodeType)+0", ), ( chn, @@ -61,12 +61,12 @@ def check_log(chn, records): ( chn, lvl, - "R(2): 0003dc: 0000 JUMP [@0003be +00001e vamostest.library] -36 [6] PrintString( str/a0 )", + "R(2): 0003dc: 0000 JUMP [@0003b8 +000024 vamostest.library] -36 [6] PrintString( str/a0 )", ), ( chn, lvl, - "R(2): 000420: 0000 Struct [@0003be +000062 vamostest.library] Library+32 = lib_OpenCnt(UWORD)+0", + "R(2): 000420: 0000 Struct [@0003b8 +000068 vamostest.library] Library+32 = lib_OpenCnt(UWORD)+0", ), ] @@ -160,6 +160,6 @@ def trace_mgr_code_line_test(caplog): ( "instr", lvl, - "@0003be +00001e vamostest.library(-36) 0003dc nop ; PrintString", + "@0003b8 +000024 vamostest.library(-36) 0003dc nop ; PrintString", ), ] diff --git a/tox.ini b/tox.ini index 71fc9539..bc55201b 100644 --- a/tox.ini +++ b/tox.ini @@ -1,11 +1,11 @@ [tox] -envlist = py{35,36,37,38} +envlist = py{36,37,38,39} skipsdist = {env:TOXBUILD:false} [travis] os = - linux: py{35,36,37,38} - osx: py{35,36,37,38} + linux: py{36,37,38,39} + osx: py{36,37,38,39} [testenv] deps= -rrequirements-test.txt From 07792d1bd63fd5262061844b5e737c4699c63211 Mon Sep 17 00:00:00 2001 From: bebbo Date: Tue, 27 Jul 2021 10:15:56 +0200 Subject: [PATCH 29/56] support more fpu insns fdmoved, fddiv, fdmul --- musashi/m68kdasm.c | 1 + musashi/m68kfpu.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/musashi/m68kdasm.c b/musashi/m68kdasm.c index fbbde31c..b05596e0 100644 --- a/musashi/m68kdasm.c +++ b/musashi/m68kdasm.c @@ -1782,6 +1782,7 @@ static void d68040_fpu(void) case 0x38: sprintf(mnemonic, "fcmp"); break; case 0x3a: sprintf(mnemonic, "ftst"); break; case 0x41: sprintf(mnemonic, "fssqrt"); break; + case 0x44: sprintf(mnemonic, "fdmoved"); break; case 0x45: sprintf(mnemonic, "fdsqrt"); break; case 0x58: sprintf(mnemonic, "fsabs"); break; case 0x5a: sprintf(mnemonic, "fsneg"); break; diff --git a/musashi/m68kfpu.c b/musashi/m68kfpu.c index f6990a53..ea782521 100644 --- a/musashi/m68kfpu.c +++ b/musashi/m68kfpu.c @@ -1366,6 +1366,7 @@ static void fpgen_rm_reg(uint16 w2) switch (opmode) { + case 0x44: // FDMOVED - maybe add rounding? case 0x00: // FMOVE { REG_FP[dst] = source; @@ -1423,6 +1424,7 @@ static void fpgen_rm_reg(uint16 w2) break; } case 0x60: // FSDIVS (JFF) (source has already been converted to floatx80) + case 0x64: // FDDIV - maybe add rounding? case 0x20: // FDIV { REG_FP[dst] = floatx80_div(REG_FP[dst], source); @@ -1445,6 +1447,7 @@ static void fpgen_rm_reg(uint16 w2) break; } case 0x63: // FSMULS (JFF) (source has already been converted to floatx80) + case 0x67: // FDMUL - maybe add rounding? case 0x23: // FMUL { REG_FP[dst] = floatx80_mul(REG_FP[dst], source); From a22e784d32b01273b5155965c682d8bfec1c7ed9 Mon Sep 17 00:00:00 2001 From: bebbo Date: Tue, 27 Jul 2021 10:16:15 +0200 Subject: [PATCH 30/56] merge enrique/master --- amitools/data/splitdata/README | 3 +-- amitools/data/splitdata/cdtv_ext.dat | Bin 18960 -> 22916 bytes amitools/data/splitdata/ks40.dat | Bin 138644 -> 142196 bytes amitools/data/splitdata/ks47.dat | Bin 0 -> 11384 bytes amitools/data/splitdata/romid.idat | Bin 8748 -> 10637 bytes 5 files changed, 1 insertion(+), 2 deletions(-) create mode 100644 amitools/data/splitdata/ks47.dat diff --git a/amitools/data/splitdata/README b/amitools/data/splitdata/README index e6a4909a..272c4894 100644 --- a/amitools/data/splitdata/README +++ b/amitools/data/splitdata/README @@ -7,8 +7,7 @@ http://www.doobreynet.co.uk Current version: -ROMsplit 1.25 -Remus Datafiles Update 1.78u2 +ROMsplit 1.28 http://www.doobreynet.co.uk/beta/index.html diff --git a/amitools/data/splitdata/cdtv_ext.dat b/amitools/data/splitdata/cdtv_ext.dat index 17fe0a1c832be1556acc2cac57d65399b34646d9..2ce6a088c6b1e244d9a91c2b41a4165d923ef7c7 100644 GIT binary patch delta 5254 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 A4FCWD delta 2523 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 delta 54596 zcmeI5e_U4O{r|6X&UN3QVxl6VVqu}tqGHXGrWG0`B`T#YDp$;?Oxfm|ZLIussIldW z6-^h|T-`OcY`I07DHRnZl@>K>R8*MgNYSv^L?uK0zOHkwbI$vHqh>ALKi~WD*y4Df zAJ>oP^?LufuIt=g@5C;7CboQ-ZR}hkQiKr3&RYDxDU98DLOhuyM0`Yu&z{EdHA1{p zBSfDfA@)cd-y+06QgJ~Wj>q7-CL!L86(TWH7!55r-Xz4MxIPZ2ydE!%?(srA*pBNn zgxEU?$GHENEFr8;9RI||1v`cK7+;=Pi3@PTuCOo$trcQI1jl#df_h<0pMwi>gxFt< z?}rQHl4>EA#o_yQ9G{GW<8x-4Fy6Qw$LBeJi@r03@sC7d^t>*N&RIfSjr$G9{d)Eb z;}cZYP{;OWVeCC!gv+E5SEdVN1ZuJCYGF?A5ystxxIvCE_T3eA+zdGrD|c++=oud6tZb0N{qriU614Ig`7Aa z1wv~)6NlqFg_wy7gt~C73b;|oYuoVv+^cySj<*Z>Qkf6~(ojHE!Ow;GDQ=(EE^MQD zF8VY{$dUy@oS%dXekn`~-%iXFqJM`lUR;3hPZ09$4k1P+;|AB^d+>01w-9~1g|TLd zFy&+s^Ens^CgYQldG5g#1tV>@a&~~`yA%Q#C!aODk zhiE>4XVsK4@T?EfPXs(`e=}O4%>{4wlfrs91>I1HV>D~`5MixL6{25@5Iqmzc)YOM z(-?S`Dx?b@zMn0uo9#+~w+JNOhksZm%#B$94~5uQjN^JCL)ig%-TxNGM^!@nt_L8Z zrk@Q)!STslBn$&h+k=YWZ_&3|7(=gfz%y@)Fz}?|fv35%%VhxG-lalZB89OyB8=oZ z2RwO$>Sr{i0&m|fLY_GmH6DxOZ8!#ayU+zo^@Cg7<6Gh=6DPJKqP-6ibDS z1!yuAAQl76KMHwbGQdm1aTSg~6xQ@&Ax=&aMr{r_nr;jEH#A#zt1zBl<$x!LW%%Hk z*26;P&H;GfV@)Q$ZxYtqC|{pi950|?ZTj9*=^CtB1s1qU!~c6+O^D(OusdAYXl^K) zwZ9y-Y|~)93$Rkr%qZCB_k#m~wYG(WwWT!*tVk4C%c8i-=3L1hmn&N-ngiDIF9}vv z6j-Q{5rLvA(Lun1g6#qWOEKO{8#GwxhPV;} z*4hB9{R07()L=C_VD;uI0o_5kvg%RbOz6L-Xur;n0xLcWtZWX}n5HPO7E-W;e4>$a zmDC=^RSLKoLcnUO@xWS~5|e1lPhE}%j6lIdjy{R1I>wPy#1VZG?-Zg^Z5pF{h3unx z1()x^;M%=Th`O0*WYp_(j93^#`K@At&5wm>&H*-Pn7y0OGteVLnNg}o45?A75qYCF6 zLSSP|C@0vA5#px#7`h$MCmY608N$A3Lc)!BJ{cv>^s%x2AsIIEAB`NFeyJRr-_{4% z7=wf;o+U(jA@nGs^hn6OHWwS))1#^u&duM}GCdMc7I@gesKF3!PeWvVr}?T;V5V_zL0>HEyUk)xZOJsAQH|D`@)EzpvmD~9JC=gUwd4R z;XAj8fhKOoSoaK{@eLt&uef`sS}NT#2i1eGamXNC4iQV+nX0 zh4^kZ7r1NUT<&b`af!+C9K24*BLy#Q1q085h*9ujK0^f@j|(ldf3}ckq@&53;b0+- zfxuaxEyo4bzQbkp)H>YRzG1v$0tZj^CIv6l!@)!A_~411!nzDieiH6kTk3-M7>+4; z!|ND$@&c*2L;pJ-SBiyg_wV@ zXW)tYLJvHLJMp@WW+UKxT%_hcN?BZz!MYE=S1=3J9wV&>7P=A7!x<7F=o#K z+<}1CUgUwdxG_G_4qc)Jt^lV=m$ONYj5BNfBF33CPzQpU%&23SS+BG+%&g5_3^Qw2 zl8+fYHVE9!Xl25g`L77$%-R#nIopC8`ucV;3XYG`H}gYag`2CUfW)v`4=>}G{kwo;7K2`lEAb7hm6FOZi`j$g9n3Id zRY)ll(2XRbwDyBj!kM)%fpR9~BD6?qF(O1am0@+_0aV|tGKQH|gD&+9tMb@Zl(<+q z9-9)pi%~;g@DM>>fD3%Cek$bgO@uo{l>fxQaU)8hJUDA_9tun_+YH6;?ZH*FocWN} zx48~?wmIPYxbWzQ3OZ7N$3_HA9=%XOM+(Yf|4%{%9VyuVt588l3bs6Ap+b%Z?RG72 zH-obvRM3%v^4P9Hj0u%Kx}ky=G|yu@>Y)OLuM#xRW0OZaRJbtW2wLE=9pz9#M+%&Ep&?Xw zr8dfxuH~k5&m!NHI+pZjS=^Ftf}NlOXSH!lYHvn~d{bJAlH-#JKR{NxrWAiOQ+oTH zC{v2-jY#0J-IGZy>0Ti&E`cRQ1b05d5tT!m6VEKEEyu#J_{mu$Lw4f9OL0RlM(qc~ z5{H|3$=PGem?^b(V(9uFTec9BT9_r3Mh|L4JT`NM;2v997B{7zc5_o2lg>=399waJ_FiVOpzen8zlE!je*tZ3?=?x2&fqQyO8WG-e-KgqqTA4W21oTrwOU+bWl{ zW6;uH9Fl^=uQwufJ+{LRNnzi<-iTCtY=;|?e(e#d3Q50)i1Z4r zZ_Q3bYMaMg1n>?wBnA8=RQMVqQh016R4~3MB>mbWQoET59Fo0X5R!iF5vl#8u0JW}OZ2{!V%R%)Uh{-);Y z|G-tRkQDlMWFt~{mbCZm6^{&#TtuooxC6~zHL%Fw$VH^~1@0_q^z7Az5gHkYBQ|;E z&yq&XUVTlISBOaesP*eY&DovcEQmR?Z3EXfFT%-lg%ehTLiTu<@C!U1Q zxMpF*V09nXfmmHR$h}kve;vq%Dwg^%vg%O4NRaxNH;YFG=VtTVZUSNh2|V@jD7uKGKE!hipUgNz&Mgzo(59#7p>UD< zsGQrvsGJ+X>5~ym&+h>j1U&0!cs2pg+=yCDC=J#_T2miYILqD>dTRsP`UpxcPIyRSq zXYE8cv7FnosGM6ZY7~Gc%_0;ylgyHq;d=s}^=Sk$ z+Wj}5b=cUKb55P@Dimd}!2j}{@E$+2TOW?kJJv%YAtxRM~X zJF=Z&CXG&wnVuyTlggtw%R-e1XG#LeE{f0lGQ zy1?fSfxty`2v?XT9hyP7gCTvZD+)8T2*WJh!;Bizm%o$sFRgFSIou)V_69E0p~8w( z2A+JUnSm#V;-Nlxe-OszQrys+LwF#Qfu;&a4XJn;1)zGipo(*5PeKg{XvT$wD6ubSLJmV05IvLq z(FJrm{v0$j1?|r@0cdKS+s||OI=9R7nVw#>QAxePS14YeSki7PvCjO#tnI=}@1 z%}`S>#Bds6d{03;wSWnlF}F=~=6P&CNIxWKPAd`6%zo$=vhKs|n~4HO0?=0VaM0$V zmB@lhhZ7}e{w(ROB@DFC$XOh;et1A!iLYpKC|ZatFOnA)anNR=#mKS`ISt)RIFr+G zJvehbHe>%wLM)k&CYpeSZ!3`#!xvP?a7aANP;H8O}!8755reWo7f^b&$4k|~D91y7oHy|hHCJo-TA_Zrd z$NWzHzYE^qOFi&#j9f^)oyx&`9XIvCgQERo0Rb%y>)ayDw2BANYQ(gPkMIBjqS%V- zDTo8njl_d9?5VX}@h+ADh@VxX^TIm67_iSspPt_OEp597?-R?j$d^oy>i{4|MRwgd&oM|p6e zZ@BAtUk8?b=fkjK;HY)HccB6VGfSaqIGAY;&-$jRrJbz&qh z#SJ~f+P{@yW}TSBFq3_;-=`6;XwGYi5aE4`M=tYXz9r&K=n}seRn8m2xq?ipa z2rx4y2`j(Hb=d6MX1$QNjd5l@-$*zEW*49U1T(8SondC}PsS68Vf_dLF9DIKzf-|6 ztPg(p@Ge*1Voq_m!#cNqxDcB+)a2Ds1kahgx~+%LntnO(Ah7D=fTtIbA3k_)^F6+Cy=R9V(mrg+u{B`_I&ZbZ(*_@<%6jzwJYtYu8` zWIWD448w|9Q+MlvoY;se`IZ%S4U$O}Z&C?UyxxYjn=9VS=n^kxHLli%wOkmrU9RBW zmZt;{{jX=UxMhXny@Ll5#p`WY>rr5$cd~3@hD-*YwF8!on%aMr z5%2(^`Vdp=NH#UKY7;aywfmqi6ui6fJmS&WVg|maruO=H4&Jw#ICx!^9K2YxSX_x8 zT391$8F-HGKs~xiY1Go%!rD`gDh1$Kds9)OOfM-XExKb65*I_UcoWjUFT!wdUQWa=bUXsqmjtL`m}{( z_7NJzOOAe6RPytKD`UW@_ic3!hh`z(n7+F7q&>Ik|hL9aE_Ft-$l>8gZds z7V^k42A-&yz`zqPEGOXIi+5b07EY=JbJ57j$?-)5#5@txf@Y?M^nDbXMyO^EJURlJ z9E-A0Jv(Af4wU@;F$zR-a>f?4 z8UZb4Yc~oQ2|!aBD<3p06S<&=<>cfkYhBK~oSb+K|3DS36=QoTZs^6Rt4f)o^-BJ} zl1@OwR;)`CcuuZ04$Vh6yI2*N&Z2%$!a+L@O-2=MG)4xEQR4xYr)b7~Lfl#a!sf>XLe|0goCzi1p`eEOJyFKVWPmkhnDRWo+f|A z1%)^v04-(^x>gICoBLgYp*X?I{URrqSI5D-DU*YzmT(d9L}McdZypLk!TT+`h$x&R zHooPA!0pEOex!h%3t~e%QMfcAwpKx-VHb4rS5E6ejU2^WoG}F++iRM$Zv1ns_SlLT zXV!W&BGEHzA4a#2nOcrC6TOfWY>-(S$33Kk!d(Z|NJMFQRGlAor-wYl6nk_t=(k zfy3r(%AGXPYt)dIp~X@IkIm}L;h4oNWSGe@=n`Mw;GpH<_9`&2y^!>YSk75e3KzI3 z`Ghm&v2_y6klOt?+Hg8Kw>zG``5afzVy|>Li~km$EH!6UB5K`R8v`w7Z#4l89+?x5 z#p~D3V7h*7b`)r+76Yx)2hH7yg?=Z7)ZEw{<*}KqQJ|#f%0 z?^}fa2n5d27Yf>4$sDwS;9bbmIL^pJr2;&T_-JrA$4Ap1&3@2p5r_ zf&pL909q4jKn$t1A5BaR=~lELOMN)6r8$31f_`a3>|Gi>bcHut|NTNw;4uExy0_^y z3_S5#IRQ^i*B9Uz*ZUqDYT>6s@?2|A45?@c5%0P?u*yQ<9pglU4 zfhJROP@;gdm~G2YBIdD`GSK9O^$awboXS9RK=nb}437;hq!jJh64zt99Oo0mDW0w3 zoULx=9-ADH!ax)MT8B8E~bJ46hn5rQEkhSKPh z!VP5x?nODfs00c{UH|Bi6Zfb36&h5{4CNO`oF&{)_KG;wEGZ2+Kj~qHGBmQ48%ouf zaV5S-CWmx!kL)M7-ytGSIe9s1L=2@p7&1*m&fliekWR!5b(Ae(@= zQTWS1>{DmCZ)vzbnS-bnEYKVqJad*~ORr@h_F4y`lKei1vxPhh1t!alyh#JhlDe;M zb|$M>Xn=lC4PsCkhXPUXRPT~hha7}1^ua@D;H(3op+P+Z&#Eg@+(G}nb#517B>3Rf z2ssKDu*s=b^a1hMwl+t3Y>wh({dTXXc<~|ySD5jA)c?C4+e-Y43!dY#VVzqY15dnx zW+UKq>JUIO(=lCmdTCBsFwHl2|IJ4Gc@UR>kFz_)`Iko~c zdJ@U8;ctSOwFPA+`eyBE^D)yowq=a7-Z{1`j@f^mV?%`uP!Z(VmJrSklw&L6n4OWq zG24lGpQX5=hgm;x99QBS+TJ;~9IbB$&9UX9JABTBv|>?Wf>{|_jOd%y2@I*e#Vq2O zjX{?X%-kFsFtfef#}f&RGntf5I0I%=3JGQ^$JT>Bt<|%lD#r#Nn58-#J)2zHp(m&M z+^7xdqo|nn;O?5mJ-E=Yg$y)#HJaH6?GM82+l*seuTvdPobBryw)m;le?-5Gmf{GN?wrykr31q`gv z@$DR}))oeqOs!@5CBHLYgVpHf)YPyfIW@5{4^7~5W$fRC82h%BLY%uo7*EB*%4CX| zq+|w~z&R8&aX!eVpj{cqLEAnn08Oo`LHrIw8Q9651p!Cqb?E=^Gz#&JHeo!6HxsJ) z`rq{s5SxT}cL@Ov9^5aoxd#_6MhP8#qtowyEP41t*MsYGg$8dK&eJ<$=4N^Vr*mx0 z_>w4G%*S{L0Z-b8Hx1p2v0o z@Ek{)Oja3NmUF|IQ_sP>1>aNfCg-pmo4+H*&#|pTzf!}w3ASKqBv3Tfm~kb(qDAM} z#D-Q5-tW;O1Uxw${Z0hU*bGo1X#U<(nOeX=lOs|YXyT zW<}2(8*uhnr^FV>f^t*BP5)?2Jcxd0pu@4$@A>&&G_0Z(t z2~6PRgqe&pTmF-q&M#W$A^YVM(8dU7-3M9cA=FSNpotgoKw>$qh9b%tJh(S-K2@}F zIDv!)7G39Io`ILyqfbx8J)doI1?v)kMUuJl4{;o{YMk#oXiY+_t|Xix$0o;@qXfi5 zTj!h7#Tj!F?LOWl0fsn6p<1^lUnt~UZK-1V6^gAx0o!!y<6R*i117{nlW$4pp~-&( z8{(mbWTTInNu(qL6gtO2w>5Mb;Q#{buvr|xTe8!=U&X(oZg>_dx!z$Dl z6)++`RxwLUJgiiXZLct{m=6JK@!ty=x)C=>&G2X2L+2(lu0s1;nSOs+zmEM|N)7&3dnB zpA@ZEZ{taBYQuUGgNNvqwLS?Jnh2KF(~4TRX|Rymc(9D<6H*&0oT0A&=28Zh)!pfX z<;;5ibS>k`j9zxTb9t19_Ft~Mb>|QxK4{^4PFH`nOVeYGWT-gzDF}=*cK26nOgS*Yr+< z*xfp{@pD|~r8Zg^c;cmno}eiY4Vl_E+R+LDc%l*G)Azv8gv+}bc;dme3_P(J51<|z zUXn=#%@|b91kIS9pasqI&@P!3DB9s{;p;>vd}Y)3=v|3lf~s|EhaZD3Zv7EFwyzQiEFhTucY%OAEBa+40SqN)!S|~v!0c4_+9W)YXs03%gibVp42n1eznodsjulW7FDjFANpGUL@ zw*^Pii43gOjXbAjeFzz+9^C$P0@}y;6+V1Fqz;o&h&sB}y0!VtkXl=mC58s--xak! zpFj+0DSnG18HThAZ!bF4eb4E}GK^S1G(hYj%W*+Ae$ElU7pX$fk6MYqor99&qb8^N z91HMZ64cxg5`DqXJ9aQbD$Pz*AfgTFRU&*(s}NVL#m}krU@j_Aty>$5Pys2esjd1I z;wzZI$um$xYDjlwaYNd#n;BB;*m!10oxNlJMxZfZgL-gd@&iL^T_NNXGhsN9Q>*)dFpd4#(NeENhZ=CDe${G9 z74g`9g_U*1aOuz*FTUe|XEoslB-IhKuNehKxKOWtPHnCTZ-XzAfC9f9M{{b|W&Uof z25+Ma-oXC2PUqDA56^Hz2RElC?oMLhiHcIRLYod9J{Dr>oB%xa^JxvPG_ z_VF|Zn)o~JM?e!F#4*kc^$sV8Gh2f;9L?xk(kpRieAIHHzFDpzRjEm0NR65j2AZ*| z6$Od}pe?LnpqW^PN;$JuG`O7k9$Pks3I*+7Nencrv4?}Uejx)*4#)rp%dmx|(Mjft z^Uy*xr#2W5q?{$-dT<7hO@lTBV9}WL+o>G1EHo(v?ZH_bGz8r~XxI`gE?EN5+K{VF zb3l`|;K+{*z}a6HD9+IT8Z`MHZb(7d>Hd zS8~vHb)&$M0JPdh4qARD2dx8@OU3YMn~D1c0*9R1rS%-VnVC>(x3QU$p#h>Oc_fyY*_E$JTov(DY# zG>3Ec%UI6YJ$QtV*}X!Xw-Cp;+)3R+-|o#1uv2>WND{~FG2BmM=6G!4tsc(VfD+Ex zAT+A4XFtZ1@KIaz`ux@L*qj}4G<91&pJVn{U}Z!C%;r~nm|?{38;i9D@xmxyDU8dY z!0HFNBdQk(XPILeXYyT~zZ5srm|68*3^VIZv=E6vrIki7L+UmgE#{y3|SCc0#{kNT=ag8B$wWQVRn1Fu3v! zB|^~ZT*4W)=)H~``dF#oAzh9F6FqyaI7-isS3eKgb<*11uAcQx)_OJ;*Xf+wUAa*n z+n?GPXx4M8-|%nF7Cm@uHvx9vW5Xuqr&}3V)>N=f^vm2(z__yN@o?&p{c9x@%LgmU zBg4->_TI~0$H1~)K;aB<;Jicu9@+F#4=jf(H>VbGCDZa4SJqBDfrIrVYzzhKwPhTv z5qKa0%UYF9z?$7Fr)I4|fe2St-Ac}tom;}dvZ_%+;*nXe6f>}_k9q>I6iD;C1HA%A zR&z4vs9z4@2r0DRb+8ng9rN5ml#qb*=iDfeoL_b?+*CEr1*u=93(}yoa9y11`q$Jl z!Lt4d!J(f2$C7jOFe>44gz*oBvbLZb{2Uov`&(i;NSn|K#1LAY^EpVH8wf|OC>TDO zMR2&@fKu{pmyVdaV0@+_E@xl;>ccoD?BXxz_zL(^SG8U zkIVWb1y!m>vQ{rU>u5)TQK+ZuUC+K46-b2!JfnYRuLa;yj&=Y%-|pdumj`8nqjY#| zwZmOS`aM9Qu6^ZWb4)t>_}k)!SJI;>v!Z;{AFJ zR<#D}0$d2!e$RjKUwkHI3!{G`YKao#Z?D$sJ)e%{^HQ1n(9aKYyI^Lb61v${d}5xT z;TKwM{4rO*JMW@@8x(t6RuH?$ z8&;r2@C+5ZAjXw*aqKB7UXd9J*I*>oBZ%H4l=~4nbsG(*3WYcUH+be7s1e@dH&ViuqgCn?7Ppor+Wp?v7<~s7)rPLQE-16w*d!+S zy{c0DF(V^*KI)c>>zYK%0zEuTR3j2)`J|a{S`TIf6}&--OYc3jby<9smq1XSN@jHFq11SBX#> zv~MYH==t$e(5rDJfqtFR;PzJAH$XQITO zeiaM36gBXA6$4Es;(DTAa!|8Ba7>H~k(=j_3x%0{Hr>U{24|)f64uovLX6Kr#P}AD zu?pjqc%Ne>1u`E6@FPeV#+}*3FaoQ&T^?4>z>%ZMQ2}S*(3IGM_EfzHBhGZVv&}*C zaH4b#V++9W28#mE%HZG~TYy$*3&2Z3AvEg_c-!$%GN~!!mT~Zo!?oVvQ$J7pKsOvJ z3f>MhD+Mntg@f0bbqMg#rw(|n!bqxg!7Iz<;EgFk1tRWfcDS<}2zcx8cWt0o>R0uC zlFY$-xi*j=1>Q;N9K75m9K6$$ICv+|(cm?@%g*ex(d3?@LxJT9O@e_pItL{t;N6cJ zQSgT0dJ5hURLD1+3cRW)@H%{bGp&)rwywbi<57#}k9EOQ)9qS79q=wsAmG7peuifD z6kqAtyp8{<38>0mebCn{D@eh@@*3V?%S-Yz_cztl}fd?}Z zb9p+BalMlQg97$jLO{f_kA7o0XewXBI76#BoY_jXp>TueM}dZB^A&D1o`lcD24M`q zdq{ikbp96oCJ18~L`W%I845@hZeSw{7zsf8D>(8jzxs`-St%}O_E{yahxWZ94%#az z95ke?n8J-~ivn#S2Q9INgLYCD=WIYKYE%t}N_%KBz7i!Sp#5SQ2Q7pfP|yt2h=MjX zmVkDqunIFYS98G?6z|aq1Uv}b2y{8y7?54aLEGNNLAy+H&;~T21{5?WC00ga%W5YBAtD1qj3HbTT)fuTDI=KbkZY4K3HbT%=2wtvv}`&mNz}^(<~B(=+SM2`F(dJIywBC^dJUHsH9Wqpyj*{a8FU=TyIzS_#LK4V7bf|EU zNWnf@p@Q<*4iG8WM=Mn56)AK(4x4?1LWL=pxpp7e1;O_3H^0?%lmPSVOdt2nbzBtzPYA# z5W)j$T89sEK|6fYdRUg#wvL!-?SkU{O?-i6g$H+}Osk`KUu0P^iD@0aXd1Za-!%+-m{X#I-QoWVaP5h(N6j$W$6k3iwE=@h<` z%w-Yi*9zJ}IJ1j*XmGS6P@9LKM=%170#ECit#cn=H+WN5UO8NYcbN;`U=!dWRQS9? z>qiSFwNf$u9nTv9jY`tP^LA6zbNqnEiRi8@2_ia7)fgRdK?v!1>M*=si0Dij&{6Tz^x z69H9U*r+v#S)FN;yucIa9*^HKt;KPMGworDrIuOqeQPo6@Dn5j-w^VA_?&U*hE>~K2kVktKcrOaPUH_~`Xfor zHOa-LL5!<%ZmV(sd0FmcmdaKiU#OCZ=zld&qVnIhGH`0u%+gTwkWY4A!lctg=r2o*SZ;7&Q-wgRu!@w{E|&`&zjc0KE+AP29& z>&)lil@)UEc2sfjPDh{XMGq>)ZC!&3IT`M3mpdENu6{OHf%kF~2XB2W2XFiG06ZCo z6nGgbfK=%kz~{t1^&GtZC;$a7vA_jSb7#xpr5wBzkWIl$orC5h;EhhgalO0jRDm}d zEl9yTF#*RBH#?)iOIyjoJGsXJPjd&4Eu)fycMb|f!MiYvgLi2i2k*wU9K8FwIe1Uw z{uI2?%|Z5h@7VzF`C<;pM3VYLW_@KzftJ$nF{>De_?Vf6R{hLQCY3aFEWO0TRnNemn7l?fg-7{Smh z>veEOu(94uK@$YnSZ_AsIKakwYYD@~`ey^f#(KMmVPiD|BfZE&$(6MpE#yoMVKcVU zE9+yFh+t#w1O{;>fnJ&DWj$%>=+$v09Gmm1IX2hh0R$WQRyn~2)D&g7)Hpvn1HB5r zQqQmnuUpQs8GJK%TMAND(^bE4A-NP_FO>MZ>QZeOfT~2zg%;6B2Gf2 z@HtupA6rap7sil8fTrex+93}J_J-Z6K}#i|0nAzW&RYZF=vf^Htqk2j4QW@L51PZ- z>6Jcbrln?9a^b-_f`;Z%hV(%+GX+n9Lcv=FJO{5khk+*z)$gfTvY_o`sM!P0 z;ZF9mQDOp~98il!3BZ%_MK}(?lLOIjRIdij!Eq$eE9}1YT~cKzhvqTxmRs;VcMFynf4z+8WjAJ4^;V zacfJD?+`hL>+{;6UzLE}yf6^3Zfxt{*iP0@!k-Bx1^S>A)ZRQHkkCD9eixri$ZWsa zkYt>UzuDHP+jH1LTxk`f0ugs1u5z^FN`1rS_{H~@1mj)hN-nMmBpi;nE$8O@f}%!( z9G4tOEu> zCE9)O(474r{@KmBiCgg};Y{2eVVsG|I8?gLbqdwSr}I>=;hH_@W%Ya8OHn5uJCv^) z-+6KYti<2(y|#<)#(@u@%Ko}s*r%jS&eez#g4LgKH+(V?vH}n1_qOpj+X(d(8vW`B zFmjjW8mr4tKy|#?4X)6mD1S|P%v?-Y-m049B zs|Q;MR#?$G1LMHi_*CFxbw!VhmA}=qI+kri6HwV-C@lW>4UT#4gQDIZjhM2*Qt0y!NuJRoQnICBPa2A-!mb6=mP#`?J{uoKFC zeVUw!8&dEtMe|eeZl1-#D?zh+KB;*C(C%Lz=$C-rZCDh5iP*)KpJHe@!Hb@(+8bfu zg@)q>K6u!%GOQ9Mc0hxk!O&tYK2LqP>)N`KKhfZ=)dHuw)7u%<;RzffHe?FKFUvT1 z_jD8Rur(?h*ShhDt8n+W1RyH#9>EQ%!aauX33%e4s1y~r_*BiE4In;)aM0Jzk6gMzrAsSP!AZl&j2I#+7VFmoLTnwwH0bF_(~v z&F}G0I4=r^CAMAym;^lQP3Qv!&q6o(;EfUDd{n^M8iibp%GoJLoC;hlt_Nqq%Q^a{ zbI?w$;-HO1zk8r5kQO%vAeopjnv5<`Tce&^=qlW_&Hy|$EBc2X&e;>~iZk^8noL{( zg?j@R_@KdXzTV0eZaqNlt#FxB%2PCGXKT>VlHNLyRjD3m;0BKQK6HaGXzJ%0?Fa!6 z9$O@VfTdnXR3GKB#o_!6f62JDr-6ZI73VSV%nf)T0Z(GYFu}9i;sfy1mX?xa29`9M zQR0XTR!A%9(z6S^JlF+48;BpIyz@4IAN338_&{@^N4h_lcL)MLA_kzj=dy)#fJk2`g@OH=%?SS=72k#dHaSh0X% zCZ5D#)cWS^Ob|~kA)HkUu`xf$jUdx$I4jC?@XiFSZzrIawP7`fZ9)m~QH6e2O}<|N z_cjx4Q0DJUFxG+l>$89z<`9s=H@_)Sfr!dXV8ntm^K_KRU$_gsx;v9|b}Vi{1#nUq z$L#VnhFNGwA;T=Rzl~uQ{${1dOz%txpE;Is7M>i3Dg}{2crr$w&l&ccRC0cOM3i||jtF|zOCUFV?%Nl_%iFW`YXH=?qD zG3mLCvzTwANy*NHn9H&VW-z3)@jcTs$8cuRA%R)#D5+#MmsUxCG7C zrwFdUbOcUZ3GgUr-(AT;do-1U_GByv?Ku=mgQikw;>9%1*;c%=oUUs#(u%pDom@vi z+l`XrgHj8;W!eGll?D#lTlpNcvno*mbQ7A~;cPq#L^-<;IW{I}C*lSav?=IE8Y+Ab zvDi}FP=^X3HJal}d_4=t7IVC(Jn@xQqX2B=b$~9#6kOW4+m`}nwf%D zgI4i9HVE1VRPvlMy?qRVrV<5Cq~LjMqd>N&Xv6!X#{g#Z_VeRy;HzJ+n35gn*jJ|NO~@wN;!l6anM>M2W@jX2W@I2Y5*=gMVpQS zQAK+U(oH?I@wlD}+SPT8Gh5ySmwqX3s1+?7pTR&2k1XM!O-H{I4=p?c1tK0=xDZ2^ zf_7~_3QPp;Iut12%ogE0@jz-i%h7~>XaGf9lEXp!LjecvLp+dzc40dQ?J|Hz1#LkK z;jBMEvvGn8+Hamuv=jfRu>uKS1uK;@`w^(}Q)X#G+|k3STHMO1`hC1lmFti_glGHu zwM>Y$^~~`%LTD3;RV*6S$La)JjE^H<2s+`{8&$YR-}!LJUr$m1 zvZ1H}#p=f;94pMMGOW@jFs$S`c-Yc_s!%g(7>9dcjfKi|gxXMG5^#nSvo%$oLpHpX zV|6mbm|~Sa2PO8fx?Y4&iJ(Bmex^2jNfM_j9|fjV&C1|Z%|nYS`Ldm@q0>e9Pbi@m z2*4o=e>|UI75;1%$11UqU@urb?wny6(}xftT1IP(@)5!yJJ)Mwr8Q;=j)O z72}2YXBC2n890o`QleNaNm!)waMJnj-b=348aPAWhoQctSr{I32K5*H=vMT!zF&5s&wx)wWBs{vI&3(v_C-p;C_Ik*HpRqta1EOqa;2~KD zd5Gg$H#|7)E&Y%iT*XL2>d8}#vrteS!kNRK6ylhaWqU#%GCu-AoX1Mg?|2!Vm8wq6>?$8yCOoktYo zEEF6cRp@5Cy~plBGrRzP`?d;i<-XA{flJ1>7NQ1`z!060%oU@&nk&XATu%+r`9;2B zn8U9Y@=DxmX&17>=OB%Q993xN5+)g8Gl5ISx7KROXhcqGwg_hya>ck152T85VFwEA zDFz&#DXG*DiSQ5VJ;hKHze`1U0VKzh3>Cu_w>XmF#BjrZBEol|v?_-C4>&C5_ zj>f1GVpI{1yYSF-G%CK2&A{=U?n6(`@&pFoFUL#&A+yJ+>uYd)oG`CKyXK)Sx{9>O zIpfQo|Jq~6xur_z&;#sbJiI>|TNMHy6$$QW!+=5!)!%f$sZh3GYcSxh%0@xkf&o{M zMg|=Ieyoy@x7GGSZlmi~@Bvr9j1Rbu0zTl7G-4+Dlq!F~Iic&iYVq9nkMk^ zWsgSzj7TuR&Q2E0Bv(cau#<2DI>4qD@d0)X?xhEqGrsns3*t(AlN|1wz{Zyy9uJV@ zGXH&l;jBU+QH5NbJNpox9*aIeA@ttnu=-_SI>7Qe`2br`?hP<^eBE5i$JedP$@nUV zXZY(nIMXQA?ppNuN)djo$sJ(bH@XAt>~225rnHd()+A!)bf60nu&=>n1lBRc+=vG- zlkCv{mx>j0HD@nq`c(jE2R8ZO^uu;L^hZ!gm?Ve(>?r!7rLGCb_YmT97eb8Dvb$%x z^v}lmjQ+411xdRXAMavG) z-~7PUfT3XE|7*aA`;pH=p7j4~z<;d)4()$@B)`ArEK$R*T;J~<&X8M0Xg~goX-^*J z(ta|;>9b1~cfOuz!&`5~)6Ais7rzrn>J#sJ?a@YeB&@m`VExqhc!iUa9ktJy8r=lg zMQ?y5zb1?{JT8<6uOC$!X$a#~Sc~s8;`*0`Z6;$S#V_JuuQG7LC3*zLs{fvLQpR81 zh8es{7xGFJETMO3@zo27kh31KO0QBT>rytOl<;SqLP8=~AstuYgY@4#rVIHitrF*; zki9(xWd$5h!LRh8n@OaZ^uV$>B*Gk5v1XUb))F$_(NO;B{ZtG}Ok= za$~-^Fh6i5ATw(3qn}j5bWLa&y1~m1Ae|6eu$=iWp%2KF^eQApCEUD-ve8(K(O#N7dUn=ySEw!{Qygd!)cVglcTR5=vE&T5!;;f~K z@SYsv&A}3d(*ti0;aAMX%TT~J#~U<<*-v7DUkNT~)$U@Rh`Aa~>bjHE9&LH(XYm@e zb`4swK&acxB|R``sud2vneEJ39|h13!kIH`e>6Zl2xl1obMs)&Fk!Ah^H$U-&_-f> zQqYPc9JCGSG9R=BBDAptP2B1RtqL^dln0=R@S-Bl*Oa z7jV#a0z^Ly2Wb12aM1dK2QQ9Spk3NdKm%u&HE_#Brzch1HPpDhV*hX>wm)E>cr5Mzf{@mBGoIJ2TB*%+~Ubh1%?R)4h; zCM=9!&fI_2&srILW-oD6ZIek~y^Ev~QT_RQ&22<}nqgoot?vnvs%r`nOKa_|~bX fXPlmKQvbdp?V6LafB3`MH(vX*6!lMNCh`9O>h<&2 diff --git a/amitools/data/splitdata/ks47.dat b/amitools/data/splitdata/ks47.dat new file mode 100644 index 0000000000000000000000000000000000000000..1b58af1fdcb4eda0c0c18274dc97acb61c9fde7b GIT binary patch literal 11384 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 Q($XAb-bEiv7J~Et1!aN-ivR!s literal 0 HcmV?d00001 diff --git a/amitools/data/splitdata/romid.idat b/amitools/data/splitdata/romid.idat index 78edbc0245870937e8a032bc50e33625c98a7e22..79b03ac038ba618073d97442e87d1bc236efb694 100644 GIT binary patch literal 10637 zcmb7}d0bTG_s1U=ahF9zM9fRF42Hw(!{8E|3L#5?Xr#s{0}=y+!>B-!X$zYDOeqyL zQHj*d3NIp;j*o@W`% zN=r#1ZurMPFQ?opBqYd}(YEb98wl}!kqjguM;rD&;rd7`^THYE6eNngKRar)j+_9EA>riNmIm{-Ykz?vh=eTL{rvxIG0Q-YfnI+> zwJ+e8I#4c&Bu~U=J$hS$TzsZeN<6n*H~BcoMCq<{;)IEqsn4?*XI@gipx4b&phaXj z$=e#0Q(O8j=q;+7|0?NS_bjNH(u;j6c7*f;9fW1wYjNSjW_p9}kw-|~pG`kS%AQ06GSDjctoY{d ziKV~p2fYNEJt4U;X3b?#HY{tZKAb!#b1-NO9oNVBsHFNlA^zXP@|l?C@y#<3!T&eV zoE}YYKi`N7`X7O1wMjPqUU@%{Xl!ON9*hOcIgMu7ec&Kpf*^p zcr(eDZ5!Q{Dz`hR@ui8yhw*{uIV z^d-JKva;RwKsWFoN=>7F|7_-o>l_S^1z>u^#%j8O_CqI8YPmPXccF96R~L641x+(&PM`>gFI1Pz32s zy0^z1IMcEp(NB;bgt!G98d6~)WKbW{3$!rz$7xtcf!D}G#BEvq!roH!VBmL*x?57Q z`Ui!hf=e4S3(hrZL5akZxE=Se-`r3N%3!p5^uRr!sXY1IzdR4~8)Rfu>7G3a^bAj% zcb@(w7W4|~M%<6O&sd_z_Zzf|QUC9j(9AYd`m%0A%+u(sLA!aH-YxRxFwkL2U;Djy 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 Uh-5}&lF2mPY^cI^qL7pS0zkI9-v9sr delta 3778 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 GMD#y60_Bwe From 9e832e6a91bde76f628b0762ecab91b102dcc190 Mon Sep 17 00:00:00 2001 From: bebbo Date: Thu, 23 Sep 2021 21:31:29 +0200 Subject: [PATCH 31/56] add fsin/fcos/fsincos --- musashi/m68kfpu.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/musashi/m68kfpu.c b/musashi/m68kfpu.c index ea782521..2d5180fc 100644 --- a/musashi/m68kfpu.c +++ b/musashi/m68kfpu.c @@ -1413,6 +1413,30 @@ static void fpgen_rm_reg(uint16 w2) USE_CYCLES(3); break; } + case 0xe: // SIN + REG_FP[dst] = double_to_fx80(sin(fx80_to_double(source))); + SET_CONDITION_CODES(REG_FP[dst]); // JFF + USE_CYCLES(400); + break; + case 0x1d: // COS + REG_FP[dst] = double_to_fx80(cos(fx80_to_double(source))); + SET_CONDITION_CODES(REG_FP[dst]); // JFF + USE_CYCLES(400); + break; + case 0x30: // SINCOS + case 0x31: // SINCOS + case 0x32: // SINCOS + case 0x33: // SINCOS + case 0x34: // SINCOS + case 0x35: // SINCOS + case 0x36: // SINCOS + case 0x37: // SINCOS + double ds = fx80_to_double(source); + REG_FP[dst] = double_to_fx80(sin(ds)); + REG_FP[opmode&7] = double_to_fx80(cos(ds)); + SET_CONDITION_CODES(REG_FP[dst]); // JFF + USE_CYCLES(400); + break; case 0x1e: // FGETEXP { sint16 temp; From 43065a0654ca078d66886bc359b61206c8bf6ff0 Mon Sep 17 00:00:00 2001 From: bebbo Date: Wed, 29 Sep 2021 09:50:13 +0200 Subject: [PATCH 32/56] support rounding to single/double for all insns. --- musashi/m68kfpu.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/musashi/m68kfpu.c b/musashi/m68kfpu.c index 2d5180fc..40bb9c64 100644 --- a/musashi/m68kfpu.c +++ b/musashi/m68kfpu.c @@ -1183,6 +1183,7 @@ static void fpgen_rm_reg(uint16 w2) int dst = (w2 >> 7) & 0x7; int opmode = w2 & 0x7f; floatx80 source; + int round; // fmovecr #$f, fp0 f200 5c0f @@ -1362,11 +1363,19 @@ static void fpgen_rm_reg(uint16 w2) source = REG_FP[src]; } - + if (opmode & 0x44) + { + round = 2; + opmode &= ~0x44; + } else if (opmode & 0x40) + { + round = 1; + opmode &= ~0x40; + } else + round = 0; switch (opmode) { - case 0x44: // FDMOVED - maybe add rounding? case 0x00: // FMOVE { REG_FP[dst] = source; @@ -1447,8 +1456,6 @@ static void fpgen_rm_reg(uint16 w2) USE_CYCLES(6); break; } - case 0x60: // FSDIVS (JFF) (source has already been converted to floatx80) - case 0x64: // FDDIV - maybe add rounding? case 0x20: // FDIV { REG_FP[dst] = floatx80_div(REG_FP[dst], source); @@ -1470,8 +1477,6 @@ static void fpgen_rm_reg(uint16 w2) USE_CYCLES(9); break; } - case 0x63: // FSMULS (JFF) (source has already been converted to floatx80) - case 0x67: // FDMUL - maybe add rounding? case 0x23: // FMUL { REG_FP[dst] = floatx80_mul(REG_FP[dst], source); @@ -1541,6 +1546,16 @@ static void fpgen_rm_reg(uint16 w2) default: fatalerror("fpgen_rm_reg: unimplemented opmode %02X at %08X\n", opmode, REG_PC-4); } + if (round == 1) + { + // round to single + REG_FP[dst] = double_to_fx80((float)fx80_to_double(REG_FP[dst])); + } else if (round == 2) + { + // round to double + REG_FP[dst] = double_to_fx80(fx80_to_double(REG_FP[dst])); + } + } static void fmove_reg_mem(uint16 w2) From 9f12c69c8d2b4f9cdf83e3ef6ab341b11125acde Mon Sep 17 00:00:00 2001 From: bebbo Date: Wed, 29 Sep 2021 09:52:17 +0200 Subject: [PATCH 33/56] fix compilation problems on some systems --- musashi/m68kfpu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/musashi/m68kfpu.c b/musashi/m68kfpu.c index 40bb9c64..74bb1fd7 100644 --- a/musashi/m68kfpu.c +++ b/musashi/m68kfpu.c @@ -1440,12 +1440,14 @@ static void fpgen_rm_reg(uint16 w2) case 0x35: // SINCOS case 0x36: // SINCOS case 0x37: // SINCOS + { double ds = fx80_to_double(source); REG_FP[dst] = double_to_fx80(sin(ds)); REG_FP[opmode&7] = double_to_fx80(cos(ds)); SET_CONDITION_CODES(REG_FP[dst]); // JFF USE_CYCLES(400); break; + } case 0x1e: // FGETEXP { sint16 temp; From 5cd39846f21489155fc269768dc6d0a925bd48fd Mon Sep 17 00:00:00 2001 From: bebbo Date: Wed, 29 Sep 2021 09:57:10 +0200 Subject: [PATCH 34/56] implement READ_EA_64 mode 0 --- musashi/m68kfpu.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/musashi/m68kfpu.c b/musashi/m68kfpu.c index 74bb1fd7..1db87658 100644 --- a/musashi/m68kfpu.c +++ b/musashi/m68kfpu.c @@ -570,6 +570,12 @@ static uint64 READ_EA_64(int ea) switch (mode) { + case 0: + { + h1 = REG_D[reg]; + h2 = REG_D[reg + 1]; + return (uint64)(h1) << 32 | (uint64)(h2); + } case 2: // (An) { uint32 ea = REG_A[reg]; From ed1c9457caad1f09b4cd028e6490d9f30f8b05e5 Mon Sep 17 00:00:00 2001 From: bebbo Date: Wed, 29 Sep 2021 10:37:33 +0200 Subject: [PATCH 35/56] fix rounding handling --- musashi/m68kfpu.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/musashi/m68kfpu.c b/musashi/m68kfpu.c index 1db87658..d963d7f8 100644 --- a/musashi/m68kfpu.c +++ b/musashi/m68kfpu.c @@ -570,12 +570,6 @@ static uint64 READ_EA_64(int ea) switch (mode) { - case 0: - { - h1 = REG_D[reg]; - h2 = REG_D[reg + 1]; - return (uint64)(h1) << 32 | (uint64)(h2); - } case 2: // (An) { uint32 ea = REG_A[reg]; @@ -1369,7 +1363,7 @@ static void fpgen_rm_reg(uint16 w2) source = REG_FP[src]; } - if (opmode & 0x44) + if ((opmode & 0x44) == 0x44) { round = 2; opmode &= ~0x44; @@ -1448,6 +1442,7 @@ static void fpgen_rm_reg(uint16 w2) case 0x37: // SINCOS { double ds = fx80_to_double(source); +printf("sincos -> %d %d\n", dst, opmode&7); REG_FP[dst] = double_to_fx80(sin(ds)); REG_FP[opmode&7] = double_to_fx80(cos(ds)); SET_CONDITION_CODES(REG_FP[dst]); // JFF From 2f36e8338c5ffcfb7b4bd82e0da6dd33c369b03e Mon Sep 17 00:00:00 2001 From: bebbo Date: Wed, 29 Sep 2021 10:41:14 +0200 Subject: [PATCH 36/56] remove debug output --- musashi/m68kfpu.c | 1 - 1 file changed, 1 deletion(-) diff --git a/musashi/m68kfpu.c b/musashi/m68kfpu.c index d963d7f8..e656f3ba 100644 --- a/musashi/m68kfpu.c +++ b/musashi/m68kfpu.c @@ -1442,7 +1442,6 @@ static void fpgen_rm_reg(uint16 w2) case 0x37: // SINCOS { double ds = fx80_to_double(source); -printf("sincos -> %d %d\n", dst, opmode&7); REG_FP[dst] = double_to_fx80(sin(ds)); REG_FP[opmode&7] = double_to_fx80(cos(ds)); SET_CONDITION_CODES(REG_FP[dst]); // JFF From c37870ae664caa15790612860c6227e6a64436f6 Mon Sep 17 00:00:00 2001 From: bebbo Date: Thu, 25 Nov 2021 18:35:48 +0100 Subject: [PATCH 37/56] implement fmod --- musashi/m68kfpu.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/musashi/m68kfpu.c b/musashi/m68kfpu.c index e656f3ba..ae744b29 100644 --- a/musashi/m68kfpu.c +++ b/musashi/m68kfpu.c @@ -1465,6 +1465,13 @@ static void fpgen_rm_reg(uint16 w2) USE_CYCLES(43); break; } + case 0x21: // FMOD + { + REG_FP[dst] = floatx80_rem(REG_FP[dst], source); + SET_CONDITION_CODES(REG_FP[dst]); + USE_CYCLES(43); + break; + } case 0x24: // FSGLDIV { REG_FP[dst] = double_to_fx80((float)fx80_to_double(floatx80_div(REG_FP[dst], source))); From 1eada7acb5eea87c2ce75b13f15dfb4c408583b7 Mon Sep 17 00:00:00 2001 From: bebbo Date: Thu, 13 Jan 2022 09:18:47 +0100 Subject: [PATCH 38/56] raise bus error on 68000/10 on odd addresses for w/l --- musashi/mem.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/musashi/mem.c b/musashi/mem.c index 54214055..112faf2c 100644 --- a/musashi/mem.c +++ b/musashi/mem.c @@ -143,6 +143,8 @@ unsigned int m68k_read_memory_8(unsigned int address) unsigned int m68k_read_memory_16(unsigned int address) { + if (CPU_TYPE <= M68K_CPU_TYPE_68010 && (address & 1)) + m68k_pulse_bus_error(); uint page = address >> 16; if (page < NUM_PAGES) { uint val = r_func[page][1](address, r_ctx[page][1]); @@ -158,6 +160,8 @@ unsigned int m68k_read_memory_16(unsigned int address) unsigned int m68k_read_memory_32(unsigned int address) { + if (CPU_TYPE <= M68K_CPU_TYPE_68010 && (address & 1)) + m68k_pulse_bus_error(); uint page = address >> 16; if (page < NUM_PAGES) { uint val = r_func[page][2](address, r_ctx[page][2]); @@ -186,6 +190,8 @@ void m68k_write_memory_8(unsigned int address, unsigned int value) void m68k_write_memory_16(unsigned int address, unsigned int value) { + if (CPU_TYPE <= M68K_CPU_TYPE_68010 && (address & 1)) + m68k_pulse_bus_error(); uint page = address >> 16; if (page < NUM_PAGES) { w_func[page][1](address, value, w_ctx[page][1]); @@ -199,6 +205,8 @@ void m68k_write_memory_16(unsigned int address, unsigned int value) void m68k_write_memory_32(unsigned int address, unsigned int value) { + if (CPU_TYPE <= M68K_CPU_TYPE_68010 && (address & 1)) + m68k_pulse_bus_error(); uint page = address >> 16; if (page < NUM_PAGES) { w_func[page][2](address, value, w_ctx[page][2]); From 87aeed8eb1603f2f3b2309efcbeb52b5c2f3db12 Mon Sep 17 00:00:00 2001 From: bebbo Date: Mon, 11 Apr 2022 14:52:05 +0200 Subject: [PATCH 39/56] fix merge after in deallocate --- amitools/vamos/lib/lexec/Alloc.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/amitools/vamos/lib/lexec/Alloc.py b/amitools/vamos/lib/lexec/Alloc.py index 384ea33b..cb8615b6 100644 --- a/amitools/vamos/lib/lexec/Alloc.py +++ b/amitools/vamos/lib/lexec/Alloc.py @@ -191,6 +191,7 @@ def deallocate(ctx, mh_addr, blk_addr, num_bytes, check=False): # sanity check if blk_addr < mh.lower or (blk_addr + num_bytes - 1) > mh.upper: log_exec.error("deallocate: block outside of mem header!") + return if check: validate(ctx, mh) @@ -200,6 +201,7 @@ def deallocate(ctx, mh_addr, blk_addr, num_bytes, check=False): # sanity check if mh.free != 0: log_exec.error("deallocate: internal error: first=0 but free!=0!") + return # create new and only mem chunk in deallocated range mc = MemChunk(0, num_bytes, blk_addr) @@ -253,7 +255,7 @@ def deallocate(ctx, mh_addr, blk_addr, num_bytes, check=False): log_exec.debug("grow cur: %s", mc) # no merging possible -> create a new chunk between last and cur else: - next_addr = mc.addr if mc is not None else 0 + next_addr = mc.next if mc is not None else 0 mc_new = MemChunk(next_addr, num_bytes, blk_addr) mc_new.write(ctx) if mc_last: From d32b2750c56725d9a13fb0d77b1aa69ba63f0ac3 Mon Sep 17 00:00:00 2001 From: bebbo Date: Sun, 23 Apr 2023 17:49:35 +0200 Subject: [PATCH 40/56] use partial reads --- amitools/vamos/lib/dos/FileHandle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amitools/vamos/lib/dos/FileHandle.py b/amitools/vamos/lib/dos/FileHandle.py index c321aa56..35d845e7 100644 --- a/amitools/vamos/lib/dos/FileHandle.py +++ b/amitools/vamos/lib/dos/FileHandle.py @@ -60,7 +60,7 @@ def write(self, data): def read(self, len): try: - d = self.obj.read(len) + d = self.obj.read1(len) return d except IOError: return -1 From 522ec24c3a1c3faad86755f40f6179947337239d Mon Sep 17 00:00:00 2001 From: bebbo Date: Sat, 29 Apr 2023 21:19:14 +0200 Subject: [PATCH 41/56] fix cycles for muls and mulu --- machine/musashi/m68k_in.c | 53 +++++++++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/machine/musashi/m68k_in.c b/machine/musashi/m68k_in.c index b00b9851..1a72063e 100644 --- a/machine/musashi/m68k_in.c +++ b/machine/musashi/m68k_in.c @@ -721,10 +721,10 @@ moves 8 . . 0000111000...... A+-DXWL... . S S S S . 14 5 moves 16 . . 0000111001...... A+-DXWL... . S S S S . 14 5 5 5 moves 32 . . 0000111010...... A+-DXWL... . S S S S . 16 5 5 5 move16 32 . . 1111011000100... .......... . . . . U . . . . 4 TODO: correct timing -muls 16 . d 1100...111000... .......... U U U U U 54 32 27 27 27 -muls 16 . . 1100...111...... A+-DXWLdxI U U U U U 54 32 27 27 27 -mulu 16 . d 1100...011000... .......... U U U U U 54 30 27 27 27 -mulu 16 . . 1100...011...... A+-DXWLdxI U U U U U 54 30 27 27 27 +muls 16 . d 1100...111000... .......... U U U U U 38 32 27 27 27 +muls 16 . . 1100...111...... A+-DXWLdxI U U U U U 38 32 27 27 27 +mulu 16 . d 1100...011000... .......... U U U U U 38 30 27 27 27 +mulu 16 . . 1100...011...... A+-DXWLdxI U U U U U 38 30 27 27 27 mull 32 . d 0100110000000... .......... . . U U U . . 43 43 43 mull 32 . . 0100110000...... A+-DXWLdxI . . U U U . . 43 43 43 nbcd 8 . d 0100100000000... .......... U U U U U 6 6 6 6 6 @@ -7442,7 +7442,17 @@ M68KMAKE_OP(move16, 32, ., .) M68KMAKE_OP(muls, 16, ., d) { uint* r_dst = &DX; - uint res = MASK_OUT_ABOVE_32(MAKE_INT_16(DY) * MAKE_INT_16(MASK_OUT_ABOVE_16(*r_dst))); + uint x = MAKE_INT_16(DY); + uint c = 0; + for (uint y = x, f = 0; y; y>>=1) { + if ((y&1) != f) { + c += 2; + f = 1 - f; + } + } + USE_CYCLES(c); + + uint res = MASK_OUT_ABOVE_32(x * MAKE_INT_16(MASK_OUT_ABOVE_16(*r_dst))); *r_dst = res; @@ -7456,7 +7466,17 @@ M68KMAKE_OP(muls, 16, ., d) M68KMAKE_OP(muls, 16, ., .) { uint* r_dst = &DX; - uint res = MASK_OUT_ABOVE_32(MAKE_INT_16(M68KMAKE_GET_OPER_AY_16) * MAKE_INT_16(MASK_OUT_ABOVE_16(*r_dst))); + uint x = MAKE_INT_16(M68KMAKE_GET_OPER_AY_16); + uint c = 0; + for (uint y = x, f = 0; y; y>>=1) { + if ((y&1) != f) { + c += 2; + f = 1 - f; + } + } + USE_CYCLES(c); + + uint res = MASK_OUT_ABOVE_32(x * MAKE_INT_16(MASK_OUT_ABOVE_16(*r_dst))); *r_dst = res; @@ -7470,7 +7490,15 @@ M68KMAKE_OP(muls, 16, ., .) M68KMAKE_OP(mulu, 16, ., d) { uint* r_dst = &DX; - uint res = MASK_OUT_ABOVE_16(DY) * MASK_OUT_ABOVE_16(*r_dst); + uint x = MASK_OUT_ABOVE_16(DY); + uint c = 0; + for (uint y = x; y; y>>=1) { + if ((y&1)) { + c += 2; + } + } + USE_CYCLES(c); + uint res = x * MASK_OUT_ABOVE_16(*r_dst); *r_dst = res; @@ -7484,7 +7512,16 @@ M68KMAKE_OP(mulu, 16, ., d) M68KMAKE_OP(mulu, 16, ., .) { uint* r_dst = &DX; - uint res = M68KMAKE_GET_OPER_AY_16 * MASK_OUT_ABOVE_16(*r_dst); + uint x = M68KMAKE_GET_OPER_AY_16; + uint c = 0; + for (uint y = x; y; y>>=1) { + if ((y&1)) { + c += 2; + } + } + USE_CYCLES(c); + + uint res = x * MASK_OUT_ABOVE_16(*r_dst); *r_dst = res; From b1eb54f13b07c5bb0ec902c40d05946c67f8cd77 Mon Sep 17 00:00:00 2001 From: bebbo Date: Sat, 29 Apr 2023 21:44:57 +0200 Subject: [PATCH 42/56] fix cycles for muls and mulu - only if CPU_TYPE_IS_010_LESS --- machine/musashi/m68k_in.c | 53 ++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/machine/musashi/m68k_in.c b/machine/musashi/m68k_in.c index 1a72063e..a946edee 100644 --- a/machine/musashi/m68k_in.c +++ b/machine/musashi/m68k_in.c @@ -7443,14 +7443,16 @@ M68KMAKE_OP(muls, 16, ., d) { uint* r_dst = &DX; uint x = MAKE_INT_16(DY); - uint c = 0; - for (uint y = x, f = 0; y; y>>=1) { - if ((y&1) != f) { - c += 2; - f = 1 - f; + if(CPU_TYPE_IS_010_LESS(CPU_TYPE)) { + uint c = 0; + for (uint y = x, f = 0; y; y>>=1) { + if ((y&1) != f) { + c += 2; + f = 1 - f; + } } + USE_CYCLES(c); } - USE_CYCLES(c); uint res = MASK_OUT_ABOVE_32(x * MAKE_INT_16(MASK_OUT_ABOVE_16(*r_dst))); @@ -7467,15 +7469,16 @@ M68KMAKE_OP(muls, 16, ., .) { uint* r_dst = &DX; uint x = MAKE_INT_16(M68KMAKE_GET_OPER_AY_16); - uint c = 0; - for (uint y = x, f = 0; y; y>>=1) { - if ((y&1) != f) { - c += 2; - f = 1 - f; + if(CPU_TYPE_IS_010_LESS(CPU_TYPE)) { + uint c = 0; + for (uint y = x, f = 0; y; y>>=1) { + if ((y&1) != f) { + c += 2; + f = 1 - f; + } } + USE_CYCLES(c); } - USE_CYCLES(c); - uint res = MASK_OUT_ABOVE_32(x * MAKE_INT_16(MASK_OUT_ABOVE_16(*r_dst))); *r_dst = res; @@ -7491,13 +7494,15 @@ M68KMAKE_OP(mulu, 16, ., d) { uint* r_dst = &DX; uint x = MASK_OUT_ABOVE_16(DY); - uint c = 0; - for (uint y = x; y; y>>=1) { - if ((y&1)) { - c += 2; + if(CPU_TYPE_IS_010_LESS(CPU_TYPE)) { + uint c = 0; + for (uint y = x; y; y>>=1) { + if ((y&1)) { + c += 2; + } } + USE_CYCLES(c); } - USE_CYCLES(c); uint res = x * MASK_OUT_ABOVE_16(*r_dst); *r_dst = res; @@ -7513,13 +7518,15 @@ M68KMAKE_OP(mulu, 16, ., .) { uint* r_dst = &DX; uint x = M68KMAKE_GET_OPER_AY_16; - uint c = 0; - for (uint y = x; y; y>>=1) { - if ((y&1)) { - c += 2; + if(CPU_TYPE_IS_010_LESS(CPU_TYPE)) { + uint c = 0; + for (uint y = x; y; y>>=1) { + if ((y&1)) { + c += 2; + } } + USE_CYCLES(c); } - USE_CYCLES(c); uint res = x * MASK_OUT_ABOVE_16(*r_dst); From 16036ab5662b56e654e11c7bd0e859e9c0c2bc38 Mon Sep 17 00:00:00 2001 From: bebbo Date: Mon, 1 May 2023 13:54:52 +0200 Subject: [PATCH 43/56] more shift cost fixing --- machine/musashi/m68k_in.c | 62 +++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 25 deletions(-) diff --git a/machine/musashi/m68k_in.c b/machine/musashi/m68k_in.c index a946edee..cfbacf4e 100644 --- a/machine/musashi/m68k_in.c +++ b/machine/musashi/m68k_in.c @@ -1868,7 +1868,7 @@ M68KMAKE_OP(asr, 8, s, .) uint src = MASK_OUT_ABOVE_8(*r_dst); uint res = src >> shift; - if(shift != 0) + if(shift != 0 && CPU_TYPE_IS_010_LESS(CPU_TYPE)) USE_CYCLES(shift<> shift; - if(shift != 0) + if(shift != 0 && CPU_TYPE_IS_010_LESS(CPU_TYPE)) USE_CYCLES(shift<> shift; - if(shift != 0) + if(shift != 0 && CPU_TYPE_IS_010_LESS(CPU_TYPE)) USE_CYCLES(shift<> shift; - if(shift != 0) + if(shift != 0 ) { - USE_CYCLES(shift<> shift; - if(shift != 0) + if(shift != 0 && CPU_TYPE_IS_010_LESS(CPU_TYPE)) USE_CYCLES(shift<> shift; - if(shift != 0) + if(shift != 0 && CPU_TYPE_IS_010_LESS(CPU_TYPE)) USE_CYCLES(shift<> shift; - if(shift != 0) + if(shift != 0 && CPU_TYPE_IS_010_LESS(CPU_TYPE)) USE_CYCLES(shift< Date: Mon, 11 Dec 2023 21:12:02 +0100 Subject: [PATCH 44/56] catch exception in tell for stdin/out --- amitools/vamos/lib/dos/FileHandle.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/amitools/vamos/lib/dos/FileHandle.py b/amitools/vamos/lib/dos/FileHandle.py index 4d54539c..1462e8cc 100644 --- a/amitools/vamos/lib/dos/FileHandle.py +++ b/amitools/vamos/lib/dos/FileHandle.py @@ -135,7 +135,10 @@ def getbuf(self): return self.unch def tell(self): - return self.obj.tell() + try: + return self.obj.tell() + except IOError: + return -1 def seek(self, pos, whence): try: From 2906cebb710b56c89045d09770fe3448daf80b43 Mon Sep 17 00:00:00 2001 From: bebbo Date: Mon, 5 Feb 2024 21:07:54 +0100 Subject: [PATCH 45/56] make pip install work see https://github.com/bebbo/amiga-gcc/issues/382 --- machine/pycpu.pyx | 90 ++++++++++++++++++++++++++++++++++++++++++--- machine/pymem.pyx | 8 ++-- machine/pytraps.pyx | 14 ++++++- pyproject.toml | 2 +- setup.py | 9 ++--- 5 files changed, 106 insertions(+), 17 deletions(-) diff --git a/machine/pycpu.pyx b/machine/pycpu.pyx index 7eaa57c9..9a382537 100644 --- a/machine/pycpu.pyx +++ b/machine/pycpu.pyx @@ -2,6 +2,47 @@ from libc.stdlib cimport malloc, free +# cpu type constants +cpdef enum CPUType: + INVALID = 0 + M68000 = 1 + M68010 = 2 + M68EC020 = 3 + M68020 = 4 + M68EC030 = 5 + M68030 = 6 + M68EC040 = 7 + M68LC040 = 8 + M68040 = 9 + SCC68070 = 10 + +cpdef cpu_type_from_str(name): + try: + return CPUType[name] + except KeyError: + if name in ("68000", "000", "00"): + return CPUType.M68000 + elif name in ("68020", "020", "20"): + return CPUType.M68020 + elif name in ("68030", "030", "30"): + return CPUType.M68030 + elif name in ("68040", "040", "40"): + return CPUType.M68040 + else: + raise ValueError("Invalid CPUType: '%s'" % name) + +cpdef cpu_type_to_str(cpu_type): + if cpu_type == CPUType.M68000: + return "68000" + elif cpu_type == CPUType.M68020: + return "68020" + elif cpu_type == CPUType.M68030: + return "68030" + elif cpu_type == CPUType.M68040: + return "68040" + else: + return None + # m68k.h cdef extern from "m68k.h": ctypedef enum m68k_register_t: @@ -41,15 +82,15 @@ cdef extern from "m68k.h": # wrapper cdef object pc_changed_func -cdef void pc_changed_func_wrapper(unsigned int new_pc): +cdef void pc_changed_func_wrapper(unsigned int new_pc) noexcept: pc_changed_func(new_pc) cdef object reset_instr_func -cdef void reset_instr_func_wrapper(): +cdef void reset_instr_func_wrapper() noexcept: reset_instr_func() cdef object instr_hook_func -cdef void instr_hook_func_wrapper(unsigned int pc): +cdef void instr_hook_func_wrapper(unsigned int pc) noexcept: instr_hook_func() # public CPUContext @@ -89,10 +130,10 @@ cdef class CPUContext: # public CPU class cdef class CPU: - cdef unsigned int cpu_type + cdef readonly CPUType cpu_type - def __cinit__(self, cpu_type): - m68k_set_cpu_type(cpu_type) + def __cinit__(self, CPUType cpu_type): + m68k_set_cpu_type(cpu_type) m68k_init() self.cpu_type = cpu_type @@ -205,3 +246,40 @@ cdef class CPU: def set_cpu_context(self, CPUContext ctx): m68k_set_context(ctx.get_data()) + +# register constants +cpdef enum Register: + D0 = 0 + D1 = 1 + D2 = 2 + D3 = 3 + D4 = 4 + D5 = 5 + D6 = 6 + D7 = 7 + + A0 = 8 + A1 = 9 + A2 = 10 + A3 = 11 + A4 = 12 + A5 = 13 + A6 = 14 + A7 = 15 + + PC = 16 + SR = 17 + SP = 18 + USP = 19 + ISP = 20 + MSP = 21 + SFC = 22 + DFC = 23 + VBR = 24 + CACR = 25 + CAAR = 26 + PREF_ADDR = 27 + PREF_DATA = 28 + PPC = 29 + IR = 30 + CPU_TYPE = 31 diff --git a/machine/pymem.pyx b/machine/pymem.pyx index 020efa56..e2db01e5 100644 --- a/machine/pymem.pyx +++ b/machine/pymem.pyx @@ -51,7 +51,7 @@ cdef check_mem_exc(): mem_callback_exc = None raise exc[0], exc[1], exc[2] -cdef void trace_func_wrapper(int mode, int width, uint addr, uint val, void *ctx): +cdef void trace_func_wrapper(int mode, int width, uint addr, uint val, void *ctx) noexcept: cdef object py_func = ctx try: py_func(chr(mode), width, addr, val) @@ -60,7 +60,7 @@ cdef void trace_func_wrapper(int mode, int width, uint addr, uint val, void *ctx mem_callback_exc = sys.exc_info() m68k_end_timeslice() -cdef void invalid_func_wrapper(int mode, int width, uint addr, void *ctx): +cdef void invalid_func_wrapper(int mode, int width, uint addr, void *ctx) noexcept: cdef object py_func = ctx try: py_func(chr(mode), width, addr) @@ -69,7 +69,7 @@ cdef void invalid_func_wrapper(int mode, int width, uint addr, void *ctx): mem_callback_exc = sys.exc_info() m68k_end_timeslice() -cdef uint special_read_func_wrapper(uint addr, void *ctx): +cdef uint special_read_func_wrapper(uint addr, void *ctx) noexcept: cdef object py_func = ctx try: return py_func(addr) @@ -79,7 +79,7 @@ cdef uint special_read_func_wrapper(uint addr, void *ctx): m68k_end_timeslice() return 0 -cdef void special_write_func_wrapper(uint addr, uint value, void *ctx): +cdef void special_write_func_wrapper(uint addr, uint value, void *ctx) noexcept: cdef object py_func = ctx try: py_func(addr, value) diff --git a/machine/pytraps.pyx b/machine/pytraps.pyx index cd5799f1..181feb0f 100644 --- a/machine/pytraps.pyx +++ b/machine/pytraps.pyx @@ -13,7 +13,7 @@ cdef object trap_exc_func from cpython.exc cimport PyErr_Print -cdef void trap_wrapper(uint opcode, uint pc, void *data): +cdef void trap_wrapper(uint opcode, uint pc, void *data) noexcept: cdef object py_func = data try: py_func(opcode, pc) @@ -61,3 +61,15 @@ cdef class Traps: if tid in self.func_map: return self.func_map[tid] +# constants +# aline callback +cpdef enum ALineMode: + NONE = 0 + EXCEPT = 1 + RTS = 2 + +# traps +cpdef enum TrapType: + DEFAULT = 0 + ONE_SHOT = 1 + AUTO_RTS = 2 diff --git a/pyproject.toml b/pyproject.toml index 48ad93e0..8af73fc5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["setuptools", "setuptools-scm", "cython"] +requires = ["setuptools", "setuptools-scm", "cython >= 3.0.0"] build-backend = "setuptools.build_meta" [project] diff --git a/setup.py b/setup.py index e16fcff5..1367d39b 100644 --- a/setup.py +++ b/setup.py @@ -3,14 +3,13 @@ import subprocess from setuptools import setup, Extension -from pkg_resources import parse_version +from packaging import version from distutils.command.build_ext import build_ext from distutils.command.clean import clean from distutils.core import Command from distutils import ccompiler from distutils.dir_util import remove_tree from distutils import log -from pkg_resources import parse_version # has cython? try: @@ -33,8 +32,8 @@ from Cython import __version__ as cyver print("cython version:", cyver) - if parse_version(cyver) < parse_version("0.25"): - print("cython is too old < 0.25! please update first!") + if version.parse(cyver) < version.parse("3.0"): + print("cython is too old < 3.0! please update first!") sys.exit(1) except ImportError: print("cython is too old! please update first!") @@ -209,7 +208,7 @@ def run(self): # use cython? if use_cython: sourcefiles.append(cython_file) - extensions = cythonize(extensions) + extensions = cythonize(extensions, language_level="3str") else: sourcefiles.append(ext_file) From 76ae6339bc5698d99cc871b51de98fee30e292ec Mon Sep 17 00:00:00 2001 From: bebbo Date: Sun, 24 Mar 2024 10:38:31 +0100 Subject: [PATCH 46/56] init tc_SPLower and tc_SPUpper --- amitools/vamos/lib/dos/Process.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/amitools/vamos/lib/dos/Process.py b/amitools/vamos/lib/dos/Process.py index dda6514d..9d358e7f 100644 --- a/amitools/vamos/lib/dos/Process.py +++ b/amitools/vamos/lib/dos/Process.py @@ -298,6 +298,11 @@ def init_task_struct(self, input_fh, output_fh): # set home dir self.this_task.access.w_s("pr_HomeDir", self.home_lock.b_addr << 2) varlist = self.get_local_vars() + + # init stack + self.this_task.access.w_s("pr_Task.tc_SPLower", self.stack.lower) + self.this_task.access.w_s("pr_Task.tc_SPUpper", self.stack.upper) + # Initialize the list of local shell variables varlist.access.w_s("mlh_Head", varlist.addr + 4) varlist.access.w_s("mlh_Tail", 0) From 8f7200b0f8e8bab02ea33a9bc114270c090d0cfb Mon Sep 17 00:00:00 2001 From: bebbo Date: Sun, 24 Mar 2024 10:38:49 +0100 Subject: [PATCH 47/56] fix version check --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 1367d39b..99a0f003 100644 --- a/setup.py +++ b/setup.py @@ -32,8 +32,8 @@ from Cython import __version__ as cyver print("cython version:", cyver) - if version.parse(cyver) < version.parse("3.0"): - print("cython is too old < 3.0! please update first!") + if version.parse(cyver) < version.parse("0.29"): + print("cython is too old < 0.29! please update first!", cyver) sys.exit(1) except ImportError: print("cython is too old! please update first!") From 6b6f0a62ede020778e1634b4bec54a76ca38101c Mon Sep 17 00:00:00 2001 From: bebbo Date: Sun, 24 Mar 2024 17:47:29 +0100 Subject: [PATCH 48/56] implement WaitForChar --- amitools/vamos/lib/DosLibrary.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/amitools/vamos/lib/DosLibrary.py b/amitools/vamos/lib/DosLibrary.py index c4a7b084..aa0334ab 100644 --- a/amitools/vamos/lib/DosLibrary.py +++ b/amitools/vamos/lib/DosLibrary.py @@ -2,6 +2,7 @@ import ctypes import re import os +import select from amitools.vamos.machine.regs import * from amitools.vamos.libcore import LibImpl @@ -594,6 +595,22 @@ def Close(self, ctx): self.setioerr(ctx, 0) return self.DOSTRUE + def WaitForChar(self, ctx): + # file,timeout)(d1/d2) + fh_b_addr = ctx.cpu.r_reg(REG_D1) + fh = self.file_mgr.get_by_b_addr(fh_b_addr, False) + if select.select([fh.obj], [], [], 0.)[0]: + return self.DOSTRUE + + ms = ctx.cpu.r_reg(REG_D2) + if ms > 0: + time.sleep(ms * 1e-3) + + if select.select([fh.obj], [], [], 0.)[0]: + return self.DOSTRUE + + return self.DOSFALSE + def Read(self, ctx): fh_b_addr = ctx.cpu.r_reg(REG_D1) buf_ptr = ctx.cpu.r_reg(REG_D2) From ef0505c1ef45119db6fd2c4098ab24a56bdfd434 Mon Sep 17 00:00:00 2001 From: bebbo Date: Sun, 24 Mar 2024 17:47:50 +0100 Subject: [PATCH 49/56] implement some bsdsocket functions --- amitools/vamos/lib/BsdSocketLibrary.py | 270 +++++++++++++++++++++++++ amitools/vamos/lib/LibList.py | 2 + amitools/vamos/libmgr/setup.py | 1 + 3 files changed, 273 insertions(+) create mode 100644 amitools/vamos/lib/BsdSocketLibrary.py diff --git a/amitools/vamos/lib/BsdSocketLibrary.py b/amitools/vamos/lib/BsdSocketLibrary.py new file mode 100644 index 00000000..0c5d4822 --- /dev/null +++ b/amitools/vamos/lib/BsdSocketLibrary.py @@ -0,0 +1,270 @@ +from amitools.vamos.libcore.impl import LibImpl +from amitools.vamos.machine.regs import REG_D0, REG_D1, REG_D2, REG_A0, REG_A1, REG_A2, REG_A3 + +import select +import socket as s +from amitools.vamos.astructs.astructdef import AmigaStructDef, AmigaClassDef +from amitools.vamos.astructs.astruct import AmigaStruct +from amitools.vamos.astructs.string import CSTR +from amitools.vamos.astructs.pointer import APTR_VOID +from amitools.vamos.astructs.scalar import LONG, UBYTE, UWORD, ULONG +from amitools.vamos.astructs.access import AccessStruct +from sys import byteorder + +class BsdSocketLibrary(LibImpl): + + def setup_lib(self, ctx, base_addr): + self.alloc = ctx.alloc + self.cnt = 0 + # track opened sockets + self.socks = {} + # get host by name + self.hostByName = None + + def finish_lib(self, ctx): + self.cnt = None + + def open_lib(self, ctx, open_cnt): + self.cnt = open_cnt + + def close_lib(self, ctx, open_cnt): + self.cnt = open_cnt + + def get_version(self): + return 4 + + def get_cnt(self): + return self.cnt + + def putSock(self, sock): + n = 0 + while True: + if not n in self.socks: + self.socks[n] = sock + return n + n = n + 1 + + def socket(self, ctx): + # domain = ctx.cpu.r_reg(REG_D0) + t = ctx.cpu.r_reg(REG_D1) + if t == 1: + t = s.SOCK_STREAM + elif t == 2: + t = s.SOCK_DGRAM + else: + t = s.SOCK_RAW + # protocol = ctx.cpu.r_reg(REG_D2) + sock = s.socket(s.AF_INET, t) + if sock != None: + return self.putSock(sock) + return 0 + + def CloseSocket(self, ctx): + n = ctx.cpu.r_reg(REG_D0) + if n in self.socks: + sock = self.socks[n] + sock.close() + del self.socks[n] + + def gethostbyname(self, ctx): + name_ptr = ctx.cpu.r_reg(REG_A0) + name = ctx.mem.r_cstr(name_ptr) + h = s.gethostbyname(name) + if h != None: + ip = 0 + for x in h.split("."): + ip = ip << 8 + ip += int(x) + # print(name, h, ip) + + if self.hostByName == None: + self.hostByName = HostEntClass.alloc(self.alloc) + + self.hostByName.h_name.set(name_ptr) + self.hostByName.h_aliases.set(0) + self.hostByName.h_addrtype.set(0) + self.hostByName.h_length.set(1) + self.hostByName.h_addr_list.set(self.hostByName._addr + 20) + self.hostByName.h_addr.set(self.hostByName._addr + 24) + self.hostByName.h_addr_val.set(ip) + return self.hostByName._addr + + return 0 + + def bind(self, ctx): + sockn = ctx.cpu.r_reg(REG_D0) + if not sockn in self.socks: + return -1 + + soalen = ctx.cpu.r_reg(REG_D1) + if soalen < 8: + return -2; + + soar = ctx.cpu.r_reg(REG_A0) + soa = SockAddrClass(ctx.mem, soar) + + # no bind necessary + if soa.sin_port.get() == 0: + return 0 + + sock = self.socks[sockn] + ip = soa.sin_addr.get() + s = self.ip2s(ip) + if sock.bind((s, soa.sin_port.get())) == None: + return -3 + + return 0 + + def connect(self, ctx): + n = ctx.cpu.r_reg(REG_D0) + if not n in self.socks: + return -1 + + soalen = ctx.cpu.r_reg(REG_D1) + if soalen < 8: + return -2; + + soar = ctx.cpu.r_reg(REG_A0) + soa = SockAddrClass(ctx.mem, soar) + + # port necessary + if soa.sin_port.get() == 0: + return -3 + + sock = self.socks[n] + ip = soa.sin_addr.get() + s = self.ip2s(ip) + # print("connect to:", ip, s, soa.sin_port.get()) + sock.connect((s, soa.sin_port.get())) + return 0 + + def ip2s(self, ip): + return str((ip >> 24) & 0xff) + "." + str((ip >> 16) & 0xff) + "." + str((ip >> 8) & 0xff) + "." + str(ip & 0xff) + + def send(self, ctx): + n = ctx.cpu.r_reg(REG_D0) + if not n in self.socks: + return -1 + + buf_ptr = ctx.cpu.r_reg(REG_A0) + size = ctx.cpu.r_reg(REG_D1) + flags = ctx.cpu.r_reg(REG_D2) + data = ctx.mem.r_block(buf_ptr, size) + + sock = self.socks[n] + return sock.send(data, flags) + + def recv(self, ctx): + n = ctx.cpu.r_reg(REG_D0) + if not n in self.socks: + return -1 + + buf_ptr = ctx.cpu.r_reg(REG_A0) + size = ctx.cpu.r_reg(REG_D1) + flags = ctx.cpu.r_reg(REG_D2) + + sock = self.socks[n] + read = sock.recv(size, flags) + sz = len(read) + ctx.mem.w_block(buf_ptr, read) + return sz + + def listFromFdSet(self, mem, addr, sz): + fdset = ULongULongClass(mem, addr) + l = fdset.l0.get() + (fdset.l1.get() << 32) + r = [] + for i in range (0, sz): + if l & (1 << i) != 0 and i in self.socks: + r.append(self.socks[i]) + return r + + def markFdSet(self, mem, lst, addr, sz): + if addr != 0: + s = set(lst) + fdset = ULongULongClass(mem, addr) + l = 0 + for i in range (0, sz): + if i in self.socks and self.socks[i] in s: + l = l | (1 << i) + #print("set ULongULong", l) + fdset.l0.set(l) + fdset.l1.set(l >> 32) + + def WaitSelect(self, ctx): + sz = ctx.cpu.r_reg(REG_D0) + rfds = ctx.cpu.r_reg(REG_A0) + wfds = ctx.cpu.r_reg(REG_A1) + xfds = ctx.cpu.r_reg(REG_A2) + timevalp = ctx.cpu.r_reg(REG_A3) + signals = ctx.cpu.r_reg(REG_D1) + + # clear signals + if signals != 0: + sig = ULongULongStruct(ctx.mem, signals) + sig.l0.set(0) + + rlist, wlist, xlist = [], [], [] + if rfds != 0: + rlist = self.listFromFdSet(ctx.mem, rfds, sz) + if wfds != 0: + wlist = self.listFromFdSet(ctx.mem, wfds, sz) + if xfds != 0: + xlist = self.listFromFdSet(ctx.mem, xfds, sz) + + r, w, x = None, None, None + if timevalp != 0: + timeval = ULongULongClass(ctx.mem, timevalp) + timeout = timeval.l0.get() + timeval.l1.get() * 1e-6 + #print("WaitSelect timeout", timeout, rlist, wlist, xlist) + r,w,x = select.select(rlist, wlist, xlist, timeout) + else: + #print("WaitSelect no timeout", rlist, wlist, xlist) + r,w,x = select.select(rlist, wlist, xlist) + + self.markFdSet(ctx.mem, r, rfds, sz) + self.markFdSet(ctx.mem, w, wfds, sz) + self.markFdSet(ctx.mem, x, xfds, sz) + +@AmigaStructDef +class ULongULongStruct(AmigaStruct): + _format = [ + (ULONG, "l0"), + (UBYTE, "l1"), + ] + +@AmigaClassDef +class ULongULongClass(ULongULongStruct): + pass + +@AmigaStructDef +class SockAddrStruct(AmigaStruct): + _format = [ + (UBYTE, "sin_len"), + (UBYTE, "sin_family"), + (UWORD, "sin_port"), + (ULONG, "sin_addr"), + (ULONG, "sin_zero0"), + (ULONG, "sin_zero1"), + ] + +@AmigaClassDef +class SockAddrClass(SockAddrStruct): + pass + +@AmigaStructDef +class HostEntStruct(AmigaStruct): + _format = [ + (CSTR, "h_name"), + (APTR_VOID, "h_aliases"), + (LONG, "h_addrtype"), + (LONG, "h_length"), + (APTR_VOID, "h_addr_list"), + # internal + (LONG, "h_addr"), + (LONG, "h_addr_val"), + ] + +@AmigaClassDef +class HostEntClass(HostEntStruct): + pass + \ No newline at end of file diff --git a/amitools/vamos/lib/LibList.py b/amitools/vamos/lib/LibList.py index 98b1c222..37dcf82c 100644 --- a/amitools/vamos/lib/LibList.py +++ b/amitools/vamos/lib/LibList.py @@ -12,6 +12,7 @@ from .UtilityLibrary import UtilityLibrary from .VamosTestLibrary import VamosTestLibrary from .VamosTestDevice import VamosTestDevice +from .BsdSocketLibrary import BsdSocketLibrary vamos_libs = { "dos.library": DosLibrary, @@ -26,6 +27,7 @@ "mathtrans.library": MathTransLibrary, "timer.device": TimerDevice, "utility.library": UtilityLibrary, + "bsdsocket.library": BsdSocketLibrary, "vamostest.library": VamosTestLibrary, "vamostestdev.device": VamosTestDevice, } diff --git a/amitools/vamos/libmgr/setup.py b/amitools/vamos/libmgr/setup.py index 3aaf28d6..3b87391c 100644 --- a/amitools/vamos/libmgr/setup.py +++ b/amitools/vamos/libmgr/setup.py @@ -59,6 +59,7 @@ def setup(self): odg_base, ) self.lib_mgr.add_ctx("exec.library", self.exec_ctx) + self.lib_mgr.add_ctx("bsdsocket.library", self.exec_ctx) self.lib_mgr.add_ctx("dos.library", self.dos_ctx) # add all vamos libs for name in vamos_libs: From 4bae1c83834f3d1827d5f184640e9bc4c8619079 Mon Sep 17 00:00:00 2001 From: bebbo Date: Sun, 24 Mar 2024 19:08:08 +0100 Subject: [PATCH 50/56] free hostByName struct - fix memory leak --- amitools/vamos/lib/BsdSocketLibrary.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/amitools/vamos/lib/BsdSocketLibrary.py b/amitools/vamos/lib/BsdSocketLibrary.py index 0c5d4822..ce69ff45 100644 --- a/amitools/vamos/lib/BsdSocketLibrary.py +++ b/amitools/vamos/lib/BsdSocketLibrary.py @@ -8,8 +8,6 @@ from amitools.vamos.astructs.string import CSTR from amitools.vamos.astructs.pointer import APTR_VOID from amitools.vamos.astructs.scalar import LONG, UBYTE, UWORD, ULONG -from amitools.vamos.astructs.access import AccessStruct -from sys import byteorder class BsdSocketLibrary(LibImpl): @@ -28,6 +26,9 @@ def open_lib(self, ctx, open_cnt): self.cnt = open_cnt def close_lib(self, ctx, open_cnt): + if self.hostByName != None: + self.hostByName.free() + self.hostByName = None self.cnt = open_cnt def get_version(self): From c6ea9edef796c89c504f7131f6889a3c1f3486f0 Mon Sep 17 00:00:00 2001 From: bebbo Date: Mon, 25 Mar 2024 12:58:38 +0100 Subject: [PATCH 51/56] fake reading vhposr --- amitools/vamos/machine/hwaccess.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/amitools/vamos/machine/hwaccess.py b/amitools/vamos/machine/hwaccess.py index aa02fb50..721ec50d 100644 --- a/amitools/vamos/machine/hwaccess.py +++ b/amitools/vamos/machine/hwaccess.py @@ -1,5 +1,6 @@ from amitools.vamos.log import log_hw from amitools.vamos.error import InvalidMemoryAccessError +import time class HWAccessError(InvalidMemoryAccessError): @@ -60,6 +61,10 @@ def cia_w8(self, addr, val): raise HWAccessError("W", 0, addr) def cc_r16(self, addr): + if addr == 0xdff006: + r = time.time_ns() // 256 # fake vhpos with time 1 / (25 * 640 * 256) ~= 244ns -> use 256 + log_hw.info("Custom Chip read vhposr @%06x -> %0x4", addr, r) + return r log_hw.warning("Custom Chip read word @%06x", addr) if self.mode == self.MODE_ABORT: raise HWAccessError("R", 1, addr) From d107c2369cd596d87d739abaa0f91839f05bcaca Mon Sep 17 00:00:00 2001 From: bebbo Date: Mon, 25 Mar 2024 12:58:53 +0100 Subject: [PATCH 52/56] simpler WaitForChar --- amitools/vamos/lib/DosLibrary.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/amitools/vamos/lib/DosLibrary.py b/amitools/vamos/lib/DosLibrary.py index aa0334ab..23adbac0 100644 --- a/amitools/vamos/lib/DosLibrary.py +++ b/amitools/vamos/lib/DosLibrary.py @@ -599,15 +599,9 @@ def WaitForChar(self, ctx): # file,timeout)(d1/d2) fh_b_addr = ctx.cpu.r_reg(REG_D1) fh = self.file_mgr.get_by_b_addr(fh_b_addr, False) - if select.select([fh.obj], [], [], 0.)[0]: - return self.DOSTRUE - ms = ctx.cpu.r_reg(REG_D2) - if ms > 0: - time.sleep(ms * 1e-3) - - if select.select([fh.obj], [], [], 0.)[0]: - return self.DOSTRUE + if select.select([fh.obj], [], [], ms * 1e-3)[0]: + return self.DOSTRUE return self.DOSFALSE From 049772789ccadadb9223f9a35531286256108213 Mon Sep 17 00:00:00 2001 From: bebbo Date: Mon, 25 Mar 2024 17:05:48 +0100 Subject: [PATCH 53/56] use time_ns() for vhposr --- amitools/vamos/machine/hwaccess.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amitools/vamos/machine/hwaccess.py b/amitools/vamos/machine/hwaccess.py index 721ec50d..ee77270d 100644 --- a/amitools/vamos/machine/hwaccess.py +++ b/amitools/vamos/machine/hwaccess.py @@ -62,7 +62,7 @@ def cia_w8(self, addr, val): def cc_r16(self, addr): if addr == 0xdff006: - r = time.time_ns() // 256 # fake vhpos with time 1 / (25 * 640 * 256) ~= 244ns -> use 256 + r = 0xffff & (time.time_ns() // 256) # fake vhpos with time 1 / (25 * 640 * 256) ~= 244ns -> use 256 log_hw.info("Custom Chip read vhposr @%06x -> %0x4", addr, r) return r log_hw.warning("Custom Chip read word @%06x", addr) From d385a46c8fec92663634b8dd685d21906d8b66ab Mon Sep 17 00:00:00 2001 From: bebbo Date: Thu, 18 Apr 2024 17:45:37 +0200 Subject: [PATCH 54/56] added DOSBase->dl_TimeReq->tr_node.io_Device + GetSysTime --- amitools/vamos/lib/DosLibrary.py | 11 +++++++++++ amitools/vamos/lib/TimerDevice.py | 12 +++++++----- amitools/vamos/libstructs/dos.py | 16 ++++++++++++++++ 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/amitools/vamos/lib/DosLibrary.py b/amitools/vamos/lib/DosLibrary.py index 23adbac0..db607f7a 100644 --- a/amitools/vamos/lib/DosLibrary.py +++ b/amitools/vamos/lib/DosLibrary.py @@ -44,6 +44,7 @@ from .dos.CSource import * from .dos.Item import * from amitools.vamos.dos import run_command, run_sub_process +from amitools.vamos.libstructs.dos import TimeRequestStruct class DosLibrary(LibImpl): @@ -96,8 +97,18 @@ def setup_lib(self, ctx, base_addr): self.file_mgr = FileManager( ctx.path_mgr, ctx.exec_lib.port_mgr, ctx.alloc, ctx.mem ) + + self.timerDevice = ctx.exec_lib.lib_mgr.open_lib("timer.device", 0) + self.timeRequest = ctx.alloc.alloc_struct(TimeRequestStruct, label="TimeRequest") + self.timeRequest.access.w_s("tr_node.io_Device", self.timerDevice) + self.access.w_s("dl_TimeReq", self.timeRequest.addr) def finish_lib(self, ctx): + + # close TimerDevice, free timeRequest + ctx.exec_lib.lib_mgr.close_lib(self.timerDevice) + ctx.alloc.free_struct(self.timeRequest) + # finish file manager self.file_mgr.finish() # free dos list diff --git a/amitools/vamos/lib/TimerDevice.py b/amitools/vamos/lib/TimerDevice.py index 24a8d562..bf7e9814 100644 --- a/amitools/vamos/lib/TimerDevice.py +++ b/amitools/vamos/lib/TimerDevice.py @@ -1,7 +1,7 @@ from amitools.vamos.libcore import LibImpl from amitools.vamos.machine.regs import REG_A0 from amitools.vamos.astructs import AccessStruct -from amitools.vamos.libstructs import DateStampStruct +from amitools.vamos.libstructs.dos import TimeValStruct from datetime import datetime @@ -12,9 +12,11 @@ def ReadEClock(self, ctx): dt = datetime.now() - # abuse DateStampStruct - tv = AccessStruct(ctx.mem, DateStampStruct, struct_addr=eclockval) - tv.ds_Days = dt.microsecond / 1000000 - tv.ds_Minute = dt.microsecond % 1000000 + tv = AccessStruct(ctx.mem, TimeValStruct, struct_addr=eclockval) + tv.tv_secs = dt.microsecond / 1000000 + tv.tv_micros = dt.microsecond % 1000000 return 50 + + def GetSysTime(self, ctx): + self.ReadEClock(ctx) diff --git a/amitools/vamos/libstructs/dos.py b/amitools/vamos/libstructs/dos.py index 08eaf183..9fdbdedb 100644 --- a/amitools/vamos/libstructs/dos.py +++ b/amitools/vamos/libstructs/dos.py @@ -26,6 +26,7 @@ LibraryStruct, MessageStruct, ) +from amitools.vamos.libstructs.exec_ import IORequestStruct @AmigaStructDef @@ -226,6 +227,21 @@ class DosLibraryStruct(AmigaStruct): (APTR_VOID, "dl_IntuitionBase"), ] +@AmigaStructDef +class TimeValStruct(AmigaStruct): + _format = [ + (ULONG, "tv_secs"), + (ULONG, "tv_micro"), + ] + + +@AmigaStructDef +class TimeRequestStruct(AmigaStruct): + _format = [ + (IORequestStruct, "tr_node"), + (TimeValStruct, "tr_time"), + ] + @AmigaStructDef class FileInfoBlockStruct(AmigaStruct): From 14df1be75a2fa8ba186ad6156f2e64e7ce2a4857 Mon Sep 17 00:00:00 2001 From: bebbo Date: Tue, 30 Apr 2024 17:43:38 +0200 Subject: [PATCH 55/56] don't append arg line to stdin --- amitools/vamos/lib/DosLibrary.py | 10 ++++++++-- amitools/vamos/lib/dos/Process.py | 3 ++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/amitools/vamos/lib/DosLibrary.py b/amitools/vamos/lib/DosLibrary.py index db607f7a..ed3feb2b 100644 --- a/amitools/vamos/lib/DosLibrary.py +++ b/amitools/vamos/lib/DosLibrary.py @@ -97,7 +97,7 @@ def setup_lib(self, ctx, base_addr): self.file_mgr = FileManager( ctx.path_mgr, ctx.exec_lib.port_mgr, ctx.alloc, ctx.mem ) - + self.timerDevice = ctx.exec_lib.lib_mgr.open_lib("timer.device", 0) self.timeRequest = ctx.alloc.alloc_struct(TimeRequestStruct, label="TimeRequest") self.timeRequest.access.w_s("tr_node.io_Device", self.timerDevice) @@ -1506,7 +1506,13 @@ def ReadItem(self, ctx): csrc.read_s(ctx.alloc, csrc_ptr) input_fh = None else: - input_fh = ctx.process.get_input() + p = ctx.process + input_fh = p.get_input() + + if input_fh is not None and p.arg_str is not None: + input_fh.setbuf(p.arg_str) + p.arg_str = None + csrc = FileCSource(input_fh) # no pointer if buff_ptr == 0: diff --git a/amitools/vamos/lib/dos/Process.py b/amitools/vamos/lib/dos/Process.py index 9d358e7f..218217b8 100644 --- a/amitools/vamos/lib/dos/Process.py +++ b/amitools/vamos/lib/dos/Process.py @@ -157,7 +157,8 @@ def unload_binary(self): # ----- args ----- def init_args(self, arg_str, fh): # Tripos makes the input line available as buffered input for ReadItem() - fh.setbuf(arg_str) + # fh.setbuf(arg_str) # NOOOOOO!!!! + self.arg_str = arg_str # alloc and fill arg buffer self.arg_len = len(arg_str) name = self.bin_basename + "_args" From 666c1ec75d6aad4d5f9a041990668865822ca33b Mon Sep 17 00:00:00 2001 From: bebbo Date: Sun, 26 May 2024 13:59:43 +0200 Subject: [PATCH 56/56] Revert "don't append arg line to stdin" This reverts commit 14df1be75a2fa8ba186ad6156f2e64e7ce2a4857. --- amitools/vamos/lib/DosLibrary.py | 10 ++-------- amitools/vamos/lib/dos/Process.py | 3 +-- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/amitools/vamos/lib/DosLibrary.py b/amitools/vamos/lib/DosLibrary.py index ed3feb2b..db607f7a 100644 --- a/amitools/vamos/lib/DosLibrary.py +++ b/amitools/vamos/lib/DosLibrary.py @@ -97,7 +97,7 @@ def setup_lib(self, ctx, base_addr): self.file_mgr = FileManager( ctx.path_mgr, ctx.exec_lib.port_mgr, ctx.alloc, ctx.mem ) - + self.timerDevice = ctx.exec_lib.lib_mgr.open_lib("timer.device", 0) self.timeRequest = ctx.alloc.alloc_struct(TimeRequestStruct, label="TimeRequest") self.timeRequest.access.w_s("tr_node.io_Device", self.timerDevice) @@ -1506,13 +1506,7 @@ def ReadItem(self, ctx): csrc.read_s(ctx.alloc, csrc_ptr) input_fh = None else: - p = ctx.process - input_fh = p.get_input() - - if input_fh is not None and p.arg_str is not None: - input_fh.setbuf(p.arg_str) - p.arg_str = None - + input_fh = ctx.process.get_input() csrc = FileCSource(input_fh) # no pointer if buff_ptr == 0: diff --git a/amitools/vamos/lib/dos/Process.py b/amitools/vamos/lib/dos/Process.py index 218217b8..9d358e7f 100644 --- a/amitools/vamos/lib/dos/Process.py +++ b/amitools/vamos/lib/dos/Process.py @@ -157,8 +157,7 @@ def unload_binary(self): # ----- args ----- def init_args(self, arg_str, fh): # Tripos makes the input line available as buffered input for ReadItem() - # fh.setbuf(arg_str) # NOOOOOO!!!! - self.arg_str = arg_str + fh.setbuf(arg_str) # alloc and fill arg buffer self.arg_len = len(arg_str) name = self.bin_basename + "_args"