From 4890d9103eb6bdb72fc37a7913d8604e26ec7981 Mon Sep 17 00:00:00 2001 From: Sylvain BERTRAND Date: Tue, 4 Jan 2022 21:15:13 +0000 Subject: [PATCH] initial commit: a mess of assembly code --- FAQ | 35 + IMPORTANT_OR_FMAP_WONT_RUN | 2 + README | 13 + x86_64_sse2_x87/BUILD | 3 + x86_64_sse2_x87/bss.s | 4 + x86_64_sse2_x87/data.s | 17 + x86_64_sse2_x87/doc/HOWTO_CODE_ASM | 28 + x86_64_sse2_x87/doc/INTERNALS | 22 + x86_64_sse2_x87/doc/TODO | 4 + x86_64_sse2_x87/doc/abbrevations | 41 + x86_64_sse2_x87/doc/coding_style | 46 + x86_64_sse2_x87/doc/fasm_cheat_sheet | 71 + x86_64_sse2_x87/doc/identifier_mangling | 8 + x86_64_sse2_x87/doc/linker | 3 + x86_64_sse2_x87/doc/memcpy_memset | 24 + x86_64_sse2_x87/doc/structs | 18 + x86_64_sse2_x87/fasm/docs/fasmg.txt | 1101 +++++ x86_64_sse2_x87/fasm/docs/manual.txt | 2343 +++++++++ x86_64_sse2_x87/fasm/examples/8051/8051.inc | 861 ++++ x86_64_sse2_x87/fasm/examples/8051/hex.inc | 75 + x86_64_sse2_x87/fasm/examples/8051/invert.asm | 46 + x86_64_sse2_x87/fasm/examples/8051/make.cmd | 1 + x86_64_sse2_x87/fasm/examples/avr/avr.inc | 897 ++++ x86_64_sse2_x87/fasm/examples/avr/counter.asm | 54 + x86_64_sse2_x87/fasm/examples/avr/m16def.inc | 738 +++ x86_64_sse2_x87/fasm/examples/avr/make.cmd | 1 + x86_64_sse2_x87/fasm/examples/jvm/Test.asm | 134 + x86_64_sse2_x87/fasm/examples/jvm/bytecode.inc | 797 ++++ x86_64_sse2_x87/fasm/examples/jvm/jclass.inc | 254 + x86_64_sse2_x87/fasm/examples/jvm/make.cmd | 1 + x86_64_sse2_x87/fasm/examples/x86/hello.asm | 14 + .../fasm/examples/x86/include/80186.inc | 390 ++ .../fasm/examples/x86/include/80286.inc | 88 + .../fasm/examples/x86/include/80287.inc | 17 + .../fasm/examples/x86/include/80386.inc | 2771 +++++++++++ .../fasm/examples/x86/include/80387.inc | 160 + .../fasm/examples/x86/include/80486.inc | 63 + x86_64_sse2_x87/fasm/examples/x86/include/8086.inc | 1623 +++++++ x86_64_sse2_x87/fasm/examples/x86/include/8087.inc | 572 +++ .../fasm/examples/x86/include/ext/adx.inc | 21 + .../fasm/examples/x86/include/ext/aes.inc | 43 + .../fasm/examples/x86/include/ext/avx.inc | 1301 +++++ .../fasm/examples/x86/include/ext/avx2.inc | 540 +++ .../fasm/examples/x86/include/ext/avx512.inc | 10 + .../examples/x86/include/ext/avx512_4vnniw.inc | 31 + .../examples/x86/include/ext/avx512_bitalg.inc | 29 + .../fasm/examples/x86/include/ext/avx512_ifma.inc | 15 + .../fasm/examples/x86/include/ext/avx512_vbmi.inc | 22 + .../fasm/examples/x86/include/ext/avx512_vbmi2.inc | 65 + .../fasm/examples/x86/include/ext/avx512_vnni.inc | 14 + .../examples/x86/include/ext/avx512_vpopcntdq.inc | 16 + .../fasm/examples/x86/include/ext/avx512bw.inc | 494 ++ .../fasm/examples/x86/include/ext/avx512cd.inc | 29 + .../fasm/examples/x86/include/ext/avx512dq.inc | 463 ++ .../fasm/examples/x86/include/ext/avx512er.inc | 39 + .../fasm/examples/x86/include/ext/avx512f.inc | 2800 +++++++++++ .../fasm/examples/x86/include/ext/avx512pf.inc | 62 + .../fasm/examples/x86/include/ext/avx512vl.inc | 8 + .../fasm/examples/x86/include/ext/bmi1.inc | 97 + .../fasm/examples/x86/include/ext/bmi2.inc | 106 + .../fasm/examples/x86/include/ext/cet_ibt.inc | 8 + .../fasm/examples/x86/include/ext/cet_ss.inc | 106 + .../fasm/examples/x86/include/ext/f16c.inc | 29 + .../fasm/examples/x86/include/ext/fma.inc | 30 + .../fasm/examples/x86/include/ext/fsgsbase.inc | 24 + .../fasm/examples/x86/include/ext/gfni.inc | 53 + .../fasm/examples/x86/include/ext/hle.inc | 16 + .../fasm/examples/x86/include/ext/invpcid.inc | 15 + .../fasm/examples/x86/include/ext/mmx.inc | 160 + .../fasm/examples/x86/include/ext/movbe.inc | 44 + .../fasm/examples/x86/include/ext/movdir64b.inc | 23 + .../fasm/examples/x86/include/ext/movdiri.inc | 19 + .../fasm/examples/x86/include/ext/mpx.inc | 196 + .../fasm/examples/x86/include/ext/pclmulqdq.inc | 14 + .../fasm/examples/x86/include/ext/ptwrite.inc | 18 + .../fasm/examples/x86/include/ext/rdrand.inc | 10 + .../fasm/examples/x86/include/ext/rdseed.inc | 10 + .../fasm/examples/x86/include/ext/rdtscp.inc | 4 + .../fasm/examples/x86/include/ext/rtm.inc | 39 + .../fasm/examples/x86/include/ext/smx.inc | 4 + .../fasm/examples/x86/include/ext/sse.inc | 457 ++ .../fasm/examples/x86/include/ext/sse2.inc | 603 +++ .../fasm/examples/x86/include/ext/sse3.inc | 72 + .../fasm/examples/x86/include/ext/sse4.1.inc | 204 + .../fasm/examples/x86/include/ext/sse4.2.inc | 54 + .../fasm/examples/x86/include/ext/ssse3.inc | 26 + .../fasm/examples/x86/include/ext/vaes.inc | 37 + .../fasm/examples/x86/include/ext/vmx.inc | 65 + .../fasm/examples/x86/include/ext/vpclmulqdq.inc | 32 + .../fasm/examples/x86/include/ext/xsave.inc | 35 + .../fasm/examples/x86/include/format/coff.inc | 485 ++ .../fasm/examples/x86/include/format/coffms.inc | 769 +++ .../fasm/examples/x86/include/format/elf32.inc | 654 +++ .../fasm/examples/x86/include/format/elf64.inc | 666 +++ .../fasm/examples/x86/include/format/elfexe.inc | 326 ++ .../fasm/examples/x86/include/format/format.inc | 328 ++ .../fasm/examples/x86/include/format/macho.inc | 1175 +++++ .../fasm/examples/x86/include/format/mz.inc | 222 + .../fasm/examples/x86/include/format/pe.inc | 1078 +++++ x86_64_sse2_x87/fasm/examples/x86/include/p5.inc | 39 + x86_64_sse2_x87/fasm/examples/x86/include/p6.inc | 65 + x86_64_sse2_x87/fasm/examples/x86/include/x64.inc | 3549 ++++++++++++++ x86_64_sse2_x87/fasm/examples/x86/life.asm | 209 + x86_64_sse2_x87/fasm/examples/x86/make.cmd | 9 + x86_64_sse2_x87/fasm/examples/x86/mandel.asm | 94 + x86_64_sse2_x87/fasm/examples/x86/multiseg.asm | 29 + x86_64_sse2_x87/fasm/examples/x86/usedpmi.asm | 99 + x86_64_sse2_x87/fasm/examples/x86/win32.asm | 46 + x86_64_sse2_x87/fasm/examples/x86/win64.asm | 45 + x86_64_sse2_x87/fasm/examples/x86/win64avx.asm | 114 + x86_64_sse2_x87/fasm/fasmg | Bin 0 -> 63641 bytes x86_64_sse2_x87/fasm/fasmg.exe | Bin 0 -> 64512 bytes x86_64_sse2_x87/fasm/fasmg.jg8x.zip | Bin 0 -> 530735 bytes x86_64_sse2_x87/fasm/fasmg.x64 | Bin 0 -> 68212 bytes x86_64_sse2_x87/fasm/license.txt | 24 + x86_64_sse2_x87/fasm/readme.txt | 20 + x86_64_sse2_x87/fasm/source/assembler.inc | 3352 +++++++++++++ x86_64_sse2_x87/fasm/source/calm.inc | 2604 ++++++++++ x86_64_sse2_x87/fasm/source/conditions.inc | 667 +++ x86_64_sse2_x87/fasm/source/console.inc | 479 ++ x86_64_sse2_x87/fasm/source/directives.inc | 5013 ++++++++++++++++++++ x86_64_sse2_x87/fasm/source/dos/fasmg.asm | 651 +++ x86_64_sse2_x87/fasm/source/dos/malloc.inc | 373 ++ x86_64_sse2_x87/fasm/source/dos/selfhost.inc | 22 + x86_64_sse2_x87/fasm/source/dos/system.inc | 388 ++ x86_64_sse2_x87/fasm/source/errors.inc | 177 + x86_64_sse2_x87/fasm/source/expressions.inc | 4202 ++++++++++++++++ x86_64_sse2_x87/fasm/source/floats.inc | 1182 +++++ x86_64_sse2_x87/fasm/source/libc/fasmg.asm | 499 ++ x86_64_sse2_x87/fasm/source/libc/selfhost.inc | 60 + x86_64_sse2_x87/fasm/source/libc/system.inc | 224 + x86_64_sse2_x87/fasm/source/linux/fasmg.asm | 513 ++ x86_64_sse2_x87/fasm/source/linux/import32.inc | 162 + x86_64_sse2_x87/fasm/source/linux/selfhost.inc | 189 + x86_64_sse2_x87/fasm/source/linux/system.inc | 298 ++ x86_64_sse2_x87/fasm/source/linux/x64/32on64.inc | 124 + x86_64_sse2_x87/fasm/source/linux/x64/fasmg.asm | 535 +++ x86_64_sse2_x87/fasm/source/linux/x64/malloc.inc | 417 ++ x86_64_sse2_x87/fasm/source/linux/x64/selfhost.inc | 120 + x86_64_sse2_x87/fasm/source/linux/x64/system.inc | 264 ++ x86_64_sse2_x87/fasm/source/macos/fasmg | Bin 0 -> 69966 bytes x86_64_sse2_x87/fasm/source/macos/fasmg.asm | 504 ++ x86_64_sse2_x87/fasm/source/macos/fasmg.o.asm | 498 ++ x86_64_sse2_x87/fasm/source/macos/selfhost.inc | 50 + x86_64_sse2_x87/fasm/source/macos/system.inc | 224 + x86_64_sse2_x87/fasm/source/macos/x64/fasmg | Bin 0 -> 74118 bytes x86_64_sse2_x87/fasm/source/macos/x64/fasmg.asm | 535 +++ x86_64_sse2_x87/fasm/source/macos/x64/selfhost.inc | 41 + x86_64_sse2_x87/fasm/source/macos/x64/system.inc | 264 ++ x86_64_sse2_x87/fasm/source/map.inc | 237 + x86_64_sse2_x87/fasm/source/messages.inc | 52 + x86_64_sse2_x87/fasm/source/output.inc | 746 +++ x86_64_sse2_x87/fasm/source/reader.inc | 451 ++ x86_64_sse2_x87/fasm/source/symbols.inc | 1561 ++++++ x86_64_sse2_x87/fasm/source/tables.inc | 633 +++ x86_64_sse2_x87/fasm/source/variables.inc | 309 ++ x86_64_sse2_x87/fasm/source/version.inc | 1 + x86_64_sse2_x87/fasm/source/windows/dll/fasmg.asm | 254 + .../fasm/source/windows/dll/localptr.inc | 28 + .../fasm/source/windows/dll/selfhost.inc | 266 ++ x86_64_sse2_x87/fasm/source/windows/dll/system.inc | 213 + x86_64_sse2_x87/fasm/source/windows/fasmg.asm | 529 +++ x86_64_sse2_x87/fasm/source/windows/kernel32.inc | 96 + x86_64_sse2_x87/fasm/source/windows/selfhost.inc | 171 + x86_64_sse2_x87/fasm/source/windows/system.inc | 217 + x86_64_sse2_x87/fmap.s | 42 + x86_64_sse2_x87/inflate/zlib/bss.s | 36 + x86_64_sse2_x87/inflate/zlib/data.s | 12 + x86_64_sse2_x87/inflate/zlib/text.s | 124 + x86_64_sse2_x87/ld.simple | 64 + x86_64_sse2_x87/libc/bss.s | 9 + x86_64_sse2_x87/libc/data.s | 20 + x86_64_sse2_x87/libc/text.s | 102 + x86_64_sse2_x87/linux.s | 47 + x86_64_sse2_x87/math.s | 76 + x86_64_sse2_x87/pbf/blk/hdr/text.s | 42 + .../pbf/blk/primblk/primgrp/dense/bss.s | 44 + .../pbf/blk/primblk/primgrp/dense/data.s | 8 + .../pbf/blk/primblk/primgrp/dense/text.s | 588 +++ .../pbf/blk/primblk/primgrp/relations/bss.s | 40 + .../pbf/blk/primblk/primgrp/relations/data.s | 8 + .../pbf/blk/primblk/primgrp/relations/text.s | 660 +++ x86_64_sse2_x87/pbf/blk/primblk/primgrp/text.s | 156 + x86_64_sse2_x87/pbf/blk/primblk/primgrp/ways/bss.s | 33 + .../pbf/blk/primblk/primgrp/ways/data.s | 7 + .../pbf/blk/primblk/primgrp/ways/text.s | 544 +++ x86_64_sse2_x87/pbf/blk/primblk/strtbl/bss.s | 6 + x86_64_sse2_x87/pbf/blk/primblk/strtbl/data.s | 4 + x86_64_sse2_x87/pbf/blk/primblk/strtbl/text.s | 70 + x86_64_sse2_x87/pbf/blk/primblk/text.s | 109 + x86_64_sse2_x87/pbf/blk/text.s | 29 + x86_64_sse2_x87/pbf/blob/bss.s | 6 + x86_64_sse2_x87/pbf/blob/data.s | 5 + x86_64_sse2_x87/pbf/blob/hdr/text.s | 120 + x86_64_sse2_x87/pbf/blob/text.s | 132 + x86_64_sse2_x87/pbf/bss.s | 47 + x86_64_sse2_x87/pbf/data.s | 37 + x86_64_sse2_x87/pbf/features/bss.s | 89 + x86_64_sse2_x87/pbf/features/data.s | 40 + x86_64_sse2_x87/pbf/features/motorway.s | 92 + x86_64_sse2_x87/pbf/features/text.s | 493 ++ x86_64_sse2_x87/pbf/features/tile.s | 665 +++ x86_64_sse2_x87/pbf/features/tile_column_new.s | 81 + .../pbf/features/zoom/lvl12/nodes/text.s | 7 + .../pbf/features/zoom/lvl12/relations/text.s | 6 + x86_64_sse2_x87/pbf/features/zoom/lvl12/text.s | 6 + .../pbf/features/zoom/lvl12/ways/text.s | 77 + x86_64_sse2_x87/pbf/features/zoom/text.s | 4 + x86_64_sse2_x87/pbf/text.s | 410 ++ x86_64_sse2_x87/text.s | 325 ++ x86_64_sse2_x87/util_macros.s | 221 + 211 files changed, 68135 insertions(+) create mode 100644 FAQ create mode 100644 IMPORTANT_OR_FMAP_WONT_RUN create mode 100644 README create mode 100644 x86_64_sse2_x87/BUILD create mode 100644 x86_64_sse2_x87/bss.s create mode 100644 x86_64_sse2_x87/data.s create mode 100644 x86_64_sse2_x87/doc/HOWTO_CODE_ASM create mode 100644 x86_64_sse2_x87/doc/INTERNALS create mode 100644 x86_64_sse2_x87/doc/TODO create mode 100644 x86_64_sse2_x87/doc/abbrevations create mode 100644 x86_64_sse2_x87/doc/coding_style create mode 100644 x86_64_sse2_x87/doc/fasm_cheat_sheet create mode 100644 x86_64_sse2_x87/doc/identifier_mangling create mode 100644 x86_64_sse2_x87/doc/linker create mode 100644 x86_64_sse2_x87/doc/memcpy_memset create mode 100644 x86_64_sse2_x87/doc/structs create mode 100644 x86_64_sse2_x87/fasm/docs/fasmg.txt create mode 100644 x86_64_sse2_x87/fasm/docs/manual.txt create mode 100644 x86_64_sse2_x87/fasm/examples/8051/8051.inc create mode 100644 x86_64_sse2_x87/fasm/examples/8051/hex.inc create mode 100644 x86_64_sse2_x87/fasm/examples/8051/invert.asm create mode 100644 x86_64_sse2_x87/fasm/examples/8051/make.cmd create mode 100644 x86_64_sse2_x87/fasm/examples/avr/avr.inc create mode 100644 x86_64_sse2_x87/fasm/examples/avr/counter.asm create mode 100644 x86_64_sse2_x87/fasm/examples/avr/m16def.inc create mode 100644 x86_64_sse2_x87/fasm/examples/avr/make.cmd create mode 100644 x86_64_sse2_x87/fasm/examples/jvm/Test.asm create mode 100644 x86_64_sse2_x87/fasm/examples/jvm/bytecode.inc create mode 100644 x86_64_sse2_x87/fasm/examples/jvm/jclass.inc create mode 100644 x86_64_sse2_x87/fasm/examples/jvm/make.cmd create mode 100644 x86_64_sse2_x87/fasm/examples/x86/hello.asm create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/80186.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/80286.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/80287.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/80386.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/80387.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/80486.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/8086.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/8087.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/adx.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/aes.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/avx.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/avx2.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512_4vnniw.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512_bitalg.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512_ifma.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512_vbmi.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512_vbmi2.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512_vnni.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512_vpopcntdq.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512bw.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512cd.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512dq.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512er.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512f.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512pf.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512vl.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/bmi1.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/bmi2.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/cet_ibt.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/cet_ss.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/f16c.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/fma.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/fsgsbase.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/gfni.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/hle.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/invpcid.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/mmx.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/movbe.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/movdir64b.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/movdiri.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/mpx.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/pclmulqdq.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/ptwrite.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/rdrand.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/rdseed.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/rdtscp.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/rtm.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/smx.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/sse.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/sse2.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/sse3.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/sse4.1.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/sse4.2.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/ssse3.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/vaes.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/vmx.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/vpclmulqdq.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/ext/xsave.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/format/coff.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/format/coffms.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/format/elf32.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/format/elf64.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/format/elfexe.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/format/format.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/format/macho.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/format/mz.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/format/pe.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/p5.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/p6.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/include/x64.inc create mode 100644 x86_64_sse2_x87/fasm/examples/x86/life.asm create mode 100644 x86_64_sse2_x87/fasm/examples/x86/make.cmd create mode 100644 x86_64_sse2_x87/fasm/examples/x86/mandel.asm create mode 100644 x86_64_sse2_x87/fasm/examples/x86/multiseg.asm create mode 100644 x86_64_sse2_x87/fasm/examples/x86/usedpmi.asm create mode 100644 x86_64_sse2_x87/fasm/examples/x86/win32.asm create mode 100644 x86_64_sse2_x87/fasm/examples/x86/win64.asm create mode 100644 x86_64_sse2_x87/fasm/examples/x86/win64avx.asm create mode 100644 x86_64_sse2_x87/fasm/fasmg create mode 100644 x86_64_sse2_x87/fasm/fasmg.exe create mode 100644 x86_64_sse2_x87/fasm/fasmg.jg8x.zip create mode 100755 x86_64_sse2_x87/fasm/fasmg.x64 create mode 100644 x86_64_sse2_x87/fasm/license.txt create mode 100644 x86_64_sse2_x87/fasm/readme.txt create mode 100644 x86_64_sse2_x87/fasm/source/assembler.inc create mode 100644 x86_64_sse2_x87/fasm/source/calm.inc create mode 100644 x86_64_sse2_x87/fasm/source/conditions.inc create mode 100644 x86_64_sse2_x87/fasm/source/console.inc create mode 100644 x86_64_sse2_x87/fasm/source/directives.inc create mode 100644 x86_64_sse2_x87/fasm/source/dos/fasmg.asm create mode 100644 x86_64_sse2_x87/fasm/source/dos/malloc.inc create mode 100644 x86_64_sse2_x87/fasm/source/dos/selfhost.inc create mode 100644 x86_64_sse2_x87/fasm/source/dos/system.inc create mode 100644 x86_64_sse2_x87/fasm/source/errors.inc create mode 100644 x86_64_sse2_x87/fasm/source/expressions.inc create mode 100644 x86_64_sse2_x87/fasm/source/floats.inc create mode 100644 x86_64_sse2_x87/fasm/source/libc/fasmg.asm create mode 100644 x86_64_sse2_x87/fasm/source/libc/selfhost.inc create mode 100644 x86_64_sse2_x87/fasm/source/libc/system.inc create mode 100644 x86_64_sse2_x87/fasm/source/linux/fasmg.asm create mode 100644 x86_64_sse2_x87/fasm/source/linux/import32.inc create mode 100644 x86_64_sse2_x87/fasm/source/linux/selfhost.inc create mode 100644 x86_64_sse2_x87/fasm/source/linux/system.inc create mode 100644 x86_64_sse2_x87/fasm/source/linux/x64/32on64.inc create mode 100644 x86_64_sse2_x87/fasm/source/linux/x64/fasmg.asm create mode 100644 x86_64_sse2_x87/fasm/source/linux/x64/malloc.inc create mode 100644 x86_64_sse2_x87/fasm/source/linux/x64/selfhost.inc create mode 100644 x86_64_sse2_x87/fasm/source/linux/x64/system.inc create mode 100644 x86_64_sse2_x87/fasm/source/macos/fasmg create mode 100644 x86_64_sse2_x87/fasm/source/macos/fasmg.asm create mode 100644 x86_64_sse2_x87/fasm/source/macos/fasmg.o.asm create mode 100644 x86_64_sse2_x87/fasm/source/macos/selfhost.inc create mode 100644 x86_64_sse2_x87/fasm/source/macos/system.inc create mode 100644 x86_64_sse2_x87/fasm/source/macos/x64/fasmg create mode 100644 x86_64_sse2_x87/fasm/source/macos/x64/fasmg.asm create mode 100644 x86_64_sse2_x87/fasm/source/macos/x64/selfhost.inc create mode 100644 x86_64_sse2_x87/fasm/source/macos/x64/system.inc create mode 100644 x86_64_sse2_x87/fasm/source/map.inc create mode 100644 x86_64_sse2_x87/fasm/source/messages.inc create mode 100644 x86_64_sse2_x87/fasm/source/output.inc create mode 100644 x86_64_sse2_x87/fasm/source/reader.inc create mode 100644 x86_64_sse2_x87/fasm/source/symbols.inc create mode 100644 x86_64_sse2_x87/fasm/source/tables.inc create mode 100644 x86_64_sse2_x87/fasm/source/variables.inc create mode 100644 x86_64_sse2_x87/fasm/source/version.inc create mode 100644 x86_64_sse2_x87/fasm/source/windows/dll/fasmg.asm create mode 100644 x86_64_sse2_x87/fasm/source/windows/dll/localptr.inc create mode 100644 x86_64_sse2_x87/fasm/source/windows/dll/selfhost.inc create mode 100644 x86_64_sse2_x87/fasm/source/windows/dll/system.inc create mode 100644 x86_64_sse2_x87/fasm/source/windows/fasmg.asm create mode 100644 x86_64_sse2_x87/fasm/source/windows/kernel32.inc create mode 100644 x86_64_sse2_x87/fasm/source/windows/selfhost.inc create mode 100644 x86_64_sse2_x87/fasm/source/windows/system.inc create mode 100644 x86_64_sse2_x87/fmap.s create mode 100644 x86_64_sse2_x87/inflate/zlib/bss.s create mode 100644 x86_64_sse2_x87/inflate/zlib/data.s create mode 100644 x86_64_sse2_x87/inflate/zlib/text.s create mode 100644 x86_64_sse2_x87/ld.simple create mode 100644 x86_64_sse2_x87/libc/bss.s create mode 100644 x86_64_sse2_x87/libc/data.s create mode 100644 x86_64_sse2_x87/libc/text.s create mode 100644 x86_64_sse2_x87/linux.s create mode 100644 x86_64_sse2_x87/math.s create mode 100644 x86_64_sse2_x87/pbf/blk/hdr/text.s create mode 100644 x86_64_sse2_x87/pbf/blk/primblk/primgrp/dense/bss.s create mode 100644 x86_64_sse2_x87/pbf/blk/primblk/primgrp/dense/data.s create mode 100644 x86_64_sse2_x87/pbf/blk/primblk/primgrp/dense/text.s create mode 100644 x86_64_sse2_x87/pbf/blk/primblk/primgrp/relations/bss.s create mode 100644 x86_64_sse2_x87/pbf/blk/primblk/primgrp/relations/data.s create mode 100644 x86_64_sse2_x87/pbf/blk/primblk/primgrp/relations/text.s create mode 100644 x86_64_sse2_x87/pbf/blk/primblk/primgrp/text.s create mode 100644 x86_64_sse2_x87/pbf/blk/primblk/primgrp/ways/bss.s create mode 100644 x86_64_sse2_x87/pbf/blk/primblk/primgrp/ways/data.s create mode 100644 x86_64_sse2_x87/pbf/blk/primblk/primgrp/ways/text.s create mode 100644 x86_64_sse2_x87/pbf/blk/primblk/strtbl/bss.s create mode 100644 x86_64_sse2_x87/pbf/blk/primblk/strtbl/data.s create mode 100644 x86_64_sse2_x87/pbf/blk/primblk/strtbl/text.s create mode 100644 x86_64_sse2_x87/pbf/blk/primblk/text.s create mode 100644 x86_64_sse2_x87/pbf/blk/text.s create mode 100644 x86_64_sse2_x87/pbf/blob/bss.s create mode 100644 x86_64_sse2_x87/pbf/blob/data.s create mode 100644 x86_64_sse2_x87/pbf/blob/hdr/text.s create mode 100644 x86_64_sse2_x87/pbf/blob/text.s create mode 100644 x86_64_sse2_x87/pbf/bss.s create mode 100644 x86_64_sse2_x87/pbf/data.s create mode 100644 x86_64_sse2_x87/pbf/features/bss.s create mode 100644 x86_64_sse2_x87/pbf/features/data.s create mode 100644 x86_64_sse2_x87/pbf/features/motorway.s create mode 100644 x86_64_sse2_x87/pbf/features/text.s create mode 100644 x86_64_sse2_x87/pbf/features/tile.s create mode 100644 x86_64_sse2_x87/pbf/features/tile_column_new.s create mode 100644 x86_64_sse2_x87/pbf/features/zoom/lvl12/nodes/text.s create mode 100644 x86_64_sse2_x87/pbf/features/zoom/lvl12/relations/text.s create mode 100644 x86_64_sse2_x87/pbf/features/zoom/lvl12/text.s create mode 100644 x86_64_sse2_x87/pbf/features/zoom/lvl12/ways/text.s create mode 100644 x86_64_sse2_x87/pbf/features/zoom/text.s create mode 100644 x86_64_sse2_x87/pbf/text.s create mode 100644 x86_64_sse2_x87/text.s create mode 100644 x86_64_sse2_x87/util_macros.s diff --git a/FAQ b/FAQ new file mode 100644 index 0000000..9ad3270 --- /dev/null +++ b/FAQ @@ -0,0 +1,35 @@ +Q: Why assembly? You will have to write again code for a different cpu +instruction sets! + +A: Better rewrite several times the code for different cpu instruction sets +than being dependent on _very few_ crazy complex compilers and their often +crazy complex language syntax (and we don't mention the sanity and/or honesty +of their developers... mostly the corporations/governments which mandates +them). +-------------------------------------------------------------------------------- +Q: Why assembly? Do you believe coding straight assembly will make your program +faster? + +A: Not at all. The reason to code assembly is not speed but independence from +compilers and "control". _In theory_, you cannot reasonably "beat" an +optimizing compiler on big and complex algorithms or on the "total load +average" of a system (server). If you do, it is anectodal, or your optimizing +compiler is actually trash. +-------------------------------------------------------------------------------- +Q: Why assembly? Writing assembly is slower and not cost-effective. + +A: if you were not living in a cave in the last decades or had enough +perspective, you would know that real "libre"/open source software could not +care less of those metrics which are not pertinent to code good software... +ahem, being independent of those metrics is actually one of its ultimate super +powers. +With enough perspective, the most obvious is the following: over the life cycle +of many, but not all, pieces of software, the coding/debugging time is a grain +of sand, namely it is less of a penalty to be assembly coded than being +dependent on SDKs and/or framework which are quite unstable on the long run. +-------------------------------------------------------------------------------- +Q: Assembly will probably make this software very lean and stable in time once +all reasonable and pertinent features are in. How to keep racketeering our +clients? + +A: it will be very, very hard and it is probably illegal in the first place. diff --git a/IMPORTANT_OR_FMAP_WONT_RUN b/IMPORTANT_OR_FMAP_WONT_RUN new file mode 100644 index 0000000..425e45b --- /dev/null +++ b/IMPORTANT_OR_FMAP_WONT_RUN @@ -0,0 +1,2 @@ +We are using huge virtual memory spaces. You must change linux protection policy: +Set the content of /proc/vm/overcommit_memory to "1" (see linux kernel documentation). diff --git a/README b/README new file mode 100644 index 0000000..d3abcac --- /dev/null +++ b/README @@ -0,0 +1,13 @@ +This is the assembly implementation (various ISAs in the far future) of our +custom OpenStreetMap infrastructure specifications, which we call "fmap". + +The self-bootstrap-able assembler fasm is included (for linux/windows x86_64). + +This is/was a "writting assembly" training ground, expect excessive and +overkill mess of assembly coding. + +The x86_64 with sse2/x87 implementation will probably move to x86_64 avx2 with +proper taylor series for web mercator maths. + +Currently it does build ONLY a tile database, which will be used for concurent +tile rendering. diff --git a/x86_64_sse2_x87/BUILD b/x86_64_sse2_x87/BUILD new file mode 100644 index 0000000..b9b6531 --- /dev/null +++ b/x86_64_sse2_x87/BUILD @@ -0,0 +1,3 @@ +1 - Assemble fmap.s with the bundled fasm assembler (fasmg.x64) +2 - Link the ELF64 object to a dynamic ELF64 executable (GNU binutils linker + script provided). diff --git a/x86_64_sse2_x87/bss.s b/x86_64_sse2_x87/bss.s new file mode 100644 index 0000000..1b45800 --- /dev/null +++ b/x86_64_sse2_x87/bss.s @@ -0,0 +1,4 @@ +; vim: set filetype=fasm foldmethod=marker : +align 64 +pbf_ctx rb pbf.ctx_t.bytes_n +abi.before_exit_func rq 1 diff --git a/x86_64_sse2_x87/data.s b/x86_64_sse2_x87/data.s new file mode 100644 index 0000000..681abf3 --- /dev/null +++ b/x86_64_sse2_x87/data.s @@ -0,0 +1,17 @@ +; vim: set filetype=fasm foldmethod=marker commentstring=;%s : +msg: namespace msg + args: namespace args + usage db 'fmap [OPTIONS...] PBF_FILE_PATH TILE_DB_DIRECTORY_PATH',10,\ + 'OPTIONS:',10,\ + 9,'-z zoom_level (default=16)',10,\ + 9,'-swlat bounding box south west latitute in nanodegrees(default=none)',10,\ + 9,'-swlon bounding box south west longitude in nanodegrees (default=none)',10,\ + 9,'-nelat bounding box north east latitute in nanodegrees (default=none)',10,\ + 9,'-nelon bounding box north east longitude in nanodegrees (default=none)',10,0 + zoom.err_val_missing db 'ERROR:COMMAND ARGUMENTS:missing zoom (-z) value',10,0 + swlat.err_val_missing db 'ERROR:COMMAND ARGUMENTS:missing bounding box south west latitude (-swlat) value',10,0 + swlon.err_val_missing db 'ERROR:COMMAND ARGUMENTS:missing bounding box south west longitude (-swlon) value',10,0 + nelat.err_val_missing db 'ERROR:COMMAND ARGUMENTS:missing bounding box north east latitude (-nelat) value',10,0 + nelon.err_val_missing db 'ERROR:COMMAND ARGUMENTS:missing bounding box north east longitude (-nelon) value',10,0 + end namespace +end namespace diff --git a/x86_64_sse2_x87/doc/HOWTO_CODE_ASM b/x86_64_sse2_x87/doc/HOWTO_CODE_ASM new file mode 100644 index 0000000..fdfc9e8 --- /dev/null +++ b/x86_64_sse2_x87/doc/HOWTO_CODE_ASM @@ -0,0 +1,28 @@ +This is an opinion: +------------------- +Assembly is code path "major". For instance, for spreadsheets, an algorithm is +either row major or column major, the idea is roughly the same here. + +The primary coding passes would be dedicated to code the fast code paths with +colder/slower code paths moved away. Since in complex programs it is hard to +predict how the flow control will end up being written, everything should go on +the heap/stack to make coding simpler than maximizing the usage of registers +right away. + +Then, in the secondary code passes, from bottom and hot code paths to top or +cold code paths, "maximized" usage of registers would be injected where it +feels appropriate. The less you load/store from the memory hierarchy, the +better. + +There is also code cache locality to keep in mind. Namely beyond +fast/hot/slow/cold code paths, putting them as close as possible to increase +the chance to fit in the code memory cache or help the prefetcher seems to be a +good idea. + +---- + +Maximizing the usage of registers right away does not seems to be a good idea: +some registers could be booked in "higher" and "colder" code paths and moving +their usage to "deeper" and "hotter" code paths can become some significant +amount of work. Indeed, the booking/de-booking of registers has to be +propagated along the whole tree of code paths. diff --git a/x86_64_sse2_x87/doc/INTERNALS b/x86_64_sse2_x87/doc/INTERNALS new file mode 100644 index 0000000..47b847a --- /dev/null +++ b/x86_64_sse2_x87/doc/INTERNALS @@ -0,0 +1,22 @@ +PBF: +Processing passes: + 1st -------- + The first pass will unserialize the pbf in memory, namely will build the following data + structures: + - An array of nodes. + - An array of ways. The ids of the way nodes (called references in this context) are + resolved to their pointers in the array of nodes. Missing nodes get a 0 pointer. + - An array of relations. In this case though, the ids of relation members are not resolved + to pointers yet, only the ids are stored, pointer resolution will happen in the 2nd pass, + since we could have references to not yet parsed relations, see below. + + 2nd -------- + The second pass will resolve the ids in the unserialized relations to pointers. Namely based + on the type of the relation member, the right index (nodes/ways/relations) will be selected + and search for a pointer. If we have a missing node/way/relation, we store the 0 pointer. + + 3rd -------- + The third pass will select the features based on the zoom level and compute their + intersections with the tiles from user provided bounding box. Each feature intersecting a + tile will be fully copied there, there is no "smart" feature clipping to the tile + boundaries. The database format is braindead. diff --git a/x86_64_sse2_x87/doc/TODO b/x86_64_sse2_x87/doc/TODO new file mode 100644 index 0000000..f59c848 --- /dev/null +++ b/x86_64_sse2_x87/doc/TODO @@ -0,0 +1,4 @@ + * Once all our hardware is AVX2, move x87 math to high performance AVX2 math. + Don't forget to try to look for the taylor series of x|->arsinh(tan(x)) once + going AVX2 for web mercator projection (actually the taylor series directly + from the our range of degrees would be greatly appreciated) diff --git a/x86_64_sse2_x87/doc/abbrevations b/x86_64_sse2_x87/doc/abbrevations new file mode 100644 index 0000000..1801336 --- /dev/null +++ b/x86_64_sse2_x87/doc/abbrevations @@ -0,0 +1,41 @@ +addr ADDRess +bbox Bounding BOX +blk BLocK +cl(s) Cache Line(S) +cmp CoMPare +coord(s) COORDinate(S) +ctx ConTeXt +db DataBase +dim DIMension (usually x,y,u,v) +dir DIRectory +dst(s) DeStinaTion(S) +fn(s) FunctioN(S) +geo GEOgraphic coordinates (latitute/longitude) +grp GRouP +id IDentifier +idx(s) InDeX(eS) (0 based index) +lat(s) LATitude(S) +lon(s) LONgitude(S) +max MAXimum +mem MEMory +n couNt +num NUMber (often 1 based index) +nw North West +of(s) OFfset(S) +prim PRIMitive +primblk PRIMitive BLocK +primgrp PRIMitive GRouP +p(s) Pointer(S) +s Signed +se South East +src(s) SouRCe(S) +str STRing +strtbl STRing TaBLe +sz SiZe +tbl TaBLe +u Unsigned +val(s) VALue(S) +var(s) VARiable(S) +vec(s) VECtor(S) +vtbl(s) Virtual TaBLe(S) +wm Web Mercator, coords diff --git a/x86_64_sse2_x87/doc/coding_style b/x86_64_sse2_x87/doc/coding_style new file mode 100644 index 0000000..3e6cb71 --- /dev/null +++ b/x86_64_sse2_x87/doc/coding_style @@ -0,0 +1,46 @@ +x86_64 C ABI, the registers rbx, r12-r15 and rbp belongs to the caller, +hence the callee must preserve their values. +x86_64 C ABI for basic argument passing: rdi, rsi, rdx, rcx, r8 ,r9 +The x86_64 linux syscall: + - rcx and r11 clobbered by the syscall hardware instruction + - syscall number in rax + - arguments in: rdi, rsi, rdx, r10, r8, r9 (for the C ABI r10->rcx) + +Use rbp as last, since it may be used to save the stack pointer for external +calls. Use enter/leave, try to use leave right before a ret. + +Try to align the stack only for external calls. + +We are not too keen on augmenting code density with instructions which do +auto-adjust rsi/rdi. + +The order of source files matters for code generation: + - fast code paths + - colder code paths should be moved away from fast code paths + - once a code path is "slow", don't bother register optimize it + +A "finish" pointer (to avoid using the reserved "end" keyword), points to the +byte past the last valid byte of a memory section. + +We dynamically load libc symbols to mitigate text book planned obsolescence +from GNU symbol versioning. + +fasm --------------------------------------------------------------------------- +The balance of usage between "local labels" (starting with a .) and +creating specific embedded namespaces: it depends on the depth and number of +labels, it is a soft line. Commonly, "local labels" (starting with a .) do +deal with a depth of 1: if a "local label" needs "local labels" itself, namely +with a depth of 2, it won't work using "one dot", you will need to repeat +the top label and use more than one dot in your label. If the labels are too +"long", define a non local label, or switch the namespace with "namespace". + +__In the case of symbols of expression class, not in the case of symbols +of [labeled] instruction classes__, namespaces enforce strong upward depth +isolation. Walking namespaces depth upward and browsing the direct/first depth +children of these upper namespaces is not happening by default. You need a +symbol of expression class associated to a namespace to be allowed to browse it. + +Only use case sensitive names. + +Be careful of macros using register aliases as arguments, it does not +work in many cases. Use the internal definition of registers. diff --git a/x86_64_sse2_x87/doc/fasm_cheat_sheet b/x86_64_sse2_x87/doc/fasm_cheat_sheet new file mode 100644 index 0000000..5b47f01 --- /dev/null +++ b/x86_64_sse2_x87/doc/fasm_cheat_sheet @@ -0,0 +1,71 @@ +"line" tokens + +-/*=<>()[]{}:?!,.|&~#`\ + number + name + string +A "line" is re-constructed from several source lines, based on string tokens (if they contain +verbatim line feeds, 0x0a) and verbatim '\',0x0a sequences. + +One "line" is usually one "command", but the ":" "command" (first form of the "label" command) +introduces a "command/line break", then other "command(s)" can follow in the same "line". Could be a +sequence of ":" "commands" and a final "command" not introducing a "command break". +ex: + label0: label1: label2: mov rax, 42 + label0: label1: label2: label foo (second form of the "label" command which does not + introduce a "command/line break". + label0: label1: + +internal objects-> + * identifier-> + It is a second level syntactic construct: name token(s) separated using the '.' + token. + CHOICE OF TERMINOLOGY: an identifier "CAN REFERS" to a specific instance if, this + identifier can actually "RESOLVE" in its interpretation context to this specific + "symbol class" instance which must exists. An identifier "WILL REFER" to a specific + "symbol class" instance if it "CAN REFER" to such instance, and based on contextual + rules (beyond the interpretation context), this very instance is selected among all + existing instances of the symbol classes. + * symbol classes-> + * An identifier in its interpretation context can resolve to an instance of each + symbol class. Namely there are several instance slots, one for each "symbol class" + . To know which one will be refered depends on its location in the + "command" and to which "symbol classes" belongs the instances the first + identifierS in the "command" can refer to. + + * expression classes-> + * symbolic value class-> + * usually instanciated with a "equ" "command" or "define" "command" + * stacked ("restore"/"reequ" "commands") + * "equ": this is text meant to go in an expression verbatim AND: + this text (NOT A STRING TOKEN) is token-ized, grammatically + parsed, and for each identifiers, a link is stored on the its + interpretation context at instanciation time. + Where ever the identifiers of this symbolic value class instance + will be evaluated, their interpretation contexts will be those + at instanciation time. + * "define": same than "equ" but identifiers will be evaluated + in the interpretation context located where it is used, not at + instanciation time. + * standard class-> + * usually instanciated with a '=' command + * numerical + * stacked ('restore'/'=:' commands) + * instruction class-> + * in a "command", an identifier will refer to an instruction class + instance only if: + * it can refers to an instruction class instance + * this identifier is the first in the "command" + * "macro" + * while executing a macro, identifiers contained in parameters + will use their interpretation context at macro invocation time. + * labeled instruction class-> + * in a "command", an identifier will refer to a labeled instruction + class instance only if: + * it can refer to a labeled instruction class instance + * the first identifier in the "command" cannot refer to + an instruction class instance. + * it is the 2nd identifier in the "command" + * interpretation context-> + * namespace + the "namespace" command only switch the namespace, it + does not create the various instances in order to browse it. diff --git a/x86_64_sse2_x87/doc/identifier_mangling b/x86_64_sse2_x87/doc/identifier_mangling new file mode 100644 index 0000000..9296243 --- /dev/null +++ b/x86_64_sse2_x87/doc/identifier_mangling @@ -0,0 +1,8 @@ +name mangling, can have several "suffixes" on the same identifier: +_t suffix means type +_p suffix means pointer +_of suffix means offset +_d suffix means 32bits double word +_w suffix means 16bits word +_b suffix means 8bits byte + diff --git a/x86_64_sse2_x87/doc/linker b/x86_64_sse2_x87/doc/linker new file mode 100644 index 0000000..1a89c4f --- /dev/null +++ b/x86_64_sse2_x87/doc/linker @@ -0,0 +1,3 @@ +We use our own GNU binutils linker script because the "original" is beyond +convoluted and we may end-up with self-modifiying code and we can have +writeable data in code segment. diff --git a/x86_64_sse2_x87/doc/memcpy_memset b/x86_64_sse2_x87/doc/memcpy_memset new file mode 100644 index 0000000..87fc85a --- /dev/null +++ b/x86_64_sse2_x87/doc/memcpy_memset @@ -0,0 +1,24 @@ +2021/12/06 + +CPU designers did f*ck up hard on those: +ERMS = "Enhanced Repeat Move String" and FSRM = "Fast Short Repeat Move String" + +On AMD CPUs, those optimized hardware "memcpy/memset" are only available from Zen3... yes Zen3. On +Intel CPUs only ERMS has been available for nearly a decade, FSRM has been available for just a few +years. ARM decided to add specialized hardware instructions only recently, and those instructions +are not yet available in their shipped CPUs (it seems to be just a reaction to Zen3 supporting it). + +BUT: +Internet is reporting that the implementation of FSRM on Intel CPUs is efficient only in the very +latest CPUs. Namely, it can be available, but "bad". No feedback yet available regarding AMD Zen3 +implementation. Our "best" AMD cpu is Zen2, hence does not have those features. + +Additionnaly, for ERMS we expect no cache pollution since the copy are supposely "big", but we could +not find any information about this. +For FSRM, not idea how it works regarding cache pollution, because for "short" strings, user code +may quickly need them or not, read/write|write only|read only, which can be finely controlled using +prefetch and non-temporal instructions. + +THEN: +What seems to be the less worse compromise is using reasonably unrolled sse, and that with proper +prefetch and non-temporal instructions where appropriate. diff --git a/x86_64_sse2_x87/doc/structs b/x86_64_sse2_x87/doc/structs new file mode 100644 index 0000000..156f79a --- /dev/null +++ b/x86_64_sse2_x87/doc/structs @@ -0,0 +1,18 @@ +fasm "language" is very powerfull because it can be used to code an assembler for nearly any ISAs +(Instruction Set Architecture: x86_64/risc-v/etc) and any file formats (elf/coff/etc). + +The main pitfall is to lose ourself into the usage of such "language" for our own assembly written +programs: "struct" definition and usage are strong attractors to such pitfall. + +Namely, use the power of fasm "language" to write assemblers, but using it "too much" for assembly +written programs would be some kind of mistake. + +"structs" are a way to describe a memory layout and give some facilities to access such layout, +originally from the C language. + +We should not try to "recode"/"use" C-like "structs" in fasm, even though it was done by fasm author +(fasm "language" is powerfull enough to do so). See his personal code repository ("struct.inc" file +which is basically a fasm-syntax-like parser!). + +All that to say, we keep memory layout definitions (~structs) kind of simple and brutal on +purpose. diff --git a/x86_64_sse2_x87/fasm/docs/fasmg.txt b/x86_64_sse2_x87/fasm/docs/fasmg.txt new file mode 100644 index 0000000..32750a5 --- /dev/null +++ b/x86_64_sse2_x87/fasm/docs/fasmg.txt @@ -0,0 +1,1101 @@ + + + +What is flat assembler g? + +It is an assembly engine designed as a successor of the one used in +flat assembler 1, one of the recognized assemblers for x86 processors. +This is a bare engine that by itself has no ability to recognize and +encode instructions of any processor, however it has the ability to +become an assembler for any CPU architecture. It has a macroinstruction +language that is substantially improved compared to the one provided by +flat assembler 1 and it allows to easily implement instruction encoders +in form of customizable macroinstructions. + The source code of this tool can be compiled with flat assembler 1, +but it is also possible to use flat assembler g itself to compile it. +The source contains clauses that include different header files depending +on the assembler used. When flat assembler g compiles itself, it uses +the provided set of headers that implement x86 instructions and formats +with a syntax mostly compatible with flat assembler 1. + The example programs for x86 architecture that come in this package are +the selected samples that originally came with flat assembler 1 and they +use sets of headers that implement instruction encoders and output formatters +required to assemble them just like the original flat assembler did. + To demonstrate how the instruction sets of different architectures +may be implemented, there are some example programs for the microcontrollers, +8051 and AVR. They have been kept simple and therefore they do not provide +a complete framework for programming such CPUs, though they may provide +a solid base for the creation of such environments. + There is also an example of assembling the JVM bytecode, which is +a conversion of the sample originally created for flat assembler 1. For this +reason it is somewhat crude and does not fully utilize the capabilities +offered by the new engine. However it is good at visualising the structure +of a class file. + + + +How does this work? + +The essential function of flat assembler g is to generate output defined +by the instructions in the source code. Given the one line of text as +shown below, the assembler would generate a single byte with the stated +value: + + db 90h + + The macroinstructions can be defined to generate some specific +sequences of data depending on the provided parameters. They may correspond +to the instructions of chosen machine language, as in the following example, +but they could as well be defined to generate other kinds of data, for +various purposes. + + macro int number + if number = 3 + db 0CCh + else + db 0CDh, number + end if + end macro + + int 20h ; generates two bytes + + The assembly as seen this way may be considered a kind of interpreted +language, and the assembler certainly has many characteristics of the +interpreter. However it also shares certain aspects with a compiler. +It is possible for an instruction to use the value which is defined +later in the source and may depend on the instructions that come before +that definition, as demonstrated by the following sample. + + macro jmpi target + if target-($+2) < 80h & target-($+2) >= -80h + db 0EBh + db target-($+1) + else + db 0E9h + dw target-($+2) + end if + end macro + + jmpi start + db 'some data' + start: + + The "jmpi" defined above produces the code of jump instruction as +in 8086 architecture. Such code contains the relative offset of the +target of a jump, stored in either single byte or 16-bit word. +The relative offset is computed as a difference between the address +of the target and the address of the next instruction. The special +symbol "$" provides the address of current instruction and it is +used to calculate the relative offset and determine whether it may +fit in a single byte. + Therefore the code generated by "jmpi start" in the above sample +depends on the value of an address labeled as "start", and this +in turn depends on the length of the output of all the instructions +that precede it, including the said jump. This creates a loop of +dependencies and the assembler needs to find a solution that +fulfills all the constraints created by the source text. This would +not be possible if assembler was just an imperative interpreter. +Its language is thus in some aspects declarative. + Finding a solution for such circular dependencies may resemble +solving an equation, and it is even possible to construct an example +where flat assembler g is indeed capable of solving one: + + x = (x-1)*(x+2)/2-2*(x+1) + db x + + The circular reference has been reduced here to a single definition +that references itself to construct the value. The flat assembler g +is able to find a solution in this case, though in many others it may +fail. The method used by this assembler is to perform multiple passes +over the source text and then try to predict all the values with the +knowledge gathered this way. This approach is in most cases good enough +for the assembly of machine codes, but rarely suffices to solve the +complex equations and the above sample is one of the exceptions. + + + +What are the means of parsing the arguments of an instruction? + +Not all instructions have a simple syntax like then ones in the +previous examples. To aid in the processing of arguments that may +contain special constructions, flat assembler g provides a few +capable tools, demonstrated below on the examples that implement +selected few instructions of the Z80 processor. The rules governing +the use of presented features are found in the manual. + When an instruction has a very small set of allowed arguments, +each one of them can be treated separately with the "match" +construction: + + macro EX? first,second + match (=SP?), first + match =HL?, second + db 0E3h + else match =IX?, second + db 0DDh,0E3h + else match =IY?, second + db 0FDh,0E3h + else + err "incorrect second argument" + end match + else match =AF?, first + match =AF'?, second + db 08h + else + err "incorrect second argument" + end match + else match =DE?, first + match =HL?, second + db 0EBh + else + err "incorrect second argument" + end match + else + err "incorrect first argument" + end match + end macro + + EX (SP),HL + EX (SP),IX + EX AF,AF' + EX DE,HL + + The "?" character appears in many places to mark the names as +case-insensitive and all these occurrences could be removed to +further simplify the example. + When the set of possible values of an argument is larger but +has some regularities, the textual substitutions can be defined +to replace some of the symbols with carefully chosen constructions +that can then be recognized and parsed: + + A? equ [:111b:] + B? equ [:000b:] + C? equ [:001b:] + D? equ [:010b:] + E? equ [:011b:] + H? equ [:100b:] + L? equ [:101b:] + + macro INC? argument + match [:r:], argument + db 100b + r shl 3 + else match (=HL?), argument + db 34h + else match (=IX?+d), argument + db 0DDh,34h,d + else match (=IY?+d), argument + db 0FDh,34h,d + else + err "incorrect argument" + end match + end macro + + INC A + INC B + INC (HL) + INC (IX+2) + +This approach has a trait that may not always be desirable: +it allows to use an expression like "[:0:]" directly in an argument. +But it is possible to prevent exploiting the syntax in such way +by using a prefix in the "match" construction: + + REG.A? equ [:111b:] + REG.B? equ [:000b:] + REG.C? equ [:001b:] + REG.D? equ [:010b:] + REG.E? equ [:011b:] + REG.H? equ [:100b:] + REG.L? equ [:101b:] + + macro INC? argument + match [:r:], REG.argument + db 100b + r shl 3 + else match (=HL?), argument + db 34h + else match (=IX?+d), argument + db 0DDh,34h,d + else match (=IY?+d), argument + db 0FDh,34h,d + else + err "incorrect argument" + end match + end macro + + In case of an argument structured like "(IX+d)" it could sometimes +be desired to allow other algebraically equivalent forms of the +expression, like "(d+IX)" or "(c+IX+d)". Instead of parsing every +possible variant individually, it is possible to let the assembler +evaluate the expression while treating the selected symbol in a distinct +way. When a symbol is declared as an "element", it has no value and +when it is used in an expression, it is treated algebraically like +a variable term in a polynomial. + + element HL? + element IX? + element IY? + + macro INC? argument + match [:r:], REG.argument + db 100b + r shl 3 + else match (a), argument + if a eq HL + db 34h + else if a relativeto IX + db 0DDh,34h,a-IX + else if a relativeto IY + db 0FDh,34h,a-IY + else + err "incorrect argument" + end if + else + err "incorrect argument" + end match + end macro + + INC (3*8+IX+1) + + virtual at IX + x db ? + y db ? + end virtual + + INC (y) + + There is a small problem with the above macroinstruction. A parameter +may contain any text and when such value is placed into an expression, +it may induce erratic behavior. For example if "INC (1|0)" was processed, +it would turn the "a eq HL" expression into "1|0 eq HL" and this logical +expression is correct and true even though the argument was malformed. +Such unfortunate side-effect is a consequence of macroinstructions +operating on a simple principle of text substitution (and the best way +to avoid such problems is to use CALM instead). Here, to prevent it +from happening, a local variable may be used as a proxy holding the value +of an argument: + + macro INC? argument + match [:r:], REG.argument + db 100b + r shl 3 + else match (a), argument + local value + value = a + if value eq HL + db 34h + else if value relativeto IX + db 0DDh,34h,a-IX + else if value relativeto IY + db 0FDh,34h,a-IY + else + err "incorrect argument" + end if + else + err "incorrect argument" + end match + end macro + + There is an additional advantage of such proxy variable, thanks to +the fact that its value is computed before the macroinstruction begins +to generate any output. When an expression contains a symbol like "$", +it may give different values depending where it is calculated and +the use of proxy variable ensures that the value taken is the one +obtained by evaluating the argument before generating the code of +an instruction. + When the set of symbols allowed in expressions is larger, it is +better to have a single construction to process an entire family +of them. An "element" declaration may associate an additional value +with a symbol and this information can then be retrieved with +the "metadata" operator applied to a linear polynomial that contains +given symbol as a variable. The following example is another +variant of the previous macroinstruction that demonstrates the use +of this feature: + + element register + element A? : register + 111b + element B? : register + 000b + element C? : register + 001b + element D? : register + 010b + element E? : register + 011b + element H? : register + 100b + element L? : register + 101b + + element HL? + element IX? + element IY? + + macro INC? argument + local value + match (a), argument + value = a + if value eq HL + db 34h + else if value relativeto IX + db 0DDh,34h,a-IX + else if value relativeto IY + db 0FDh,34h,a-IY + else + err "incorrect argument" + end if + else match any more, argument + err "incorrect argument" + else + value = argument + if value eq value element 1 & value metadata 1 relativeto register + db 100b + (value metadata 1 - register) shl 3 + else + err "incorrect argument" + end if + end match + end macro + + The "any more" pattern is there to catch any argument that +contains a complex expressions consisting of more than one token. +This prevents the use of syntax like "INC A+0" or "INC A+B-A". +But in case of some of the instructions sets, the inclusion of such +constraint may depend on a personal preference. + The "value eq value element 1" condition ensures that the value does not +contain any terms other than the name of a register. Even when an argument +is forced to contain no more than a single token, it is still possible +that is has a complex value, for instance if there were definitions like +"X = A + B" or "Y = 2 * A". Both "INC X" and "INC Y" would then cause +the operator "element 1" to return the value "A", which differs from the +value checked in either case. + If an instruction takes a variable number of arguments, a simple +way to recognize its various forms is to declare an argument with "&" +modifier to pass the complete contents of the arguments to "match": + + element CC + + NZ? := CC + 000b + Z? := CC + 001b + NC? := CC + 010b + C? := CC + 011b + PO := CC + 100b + PE := CC + 101b + P := CC + 110b + M := CC + 111b + + macro CALL? arguments& + local cc,nn + match condition =, target, arguments + cc = condition - CC + nn = target + db 0C4h + cc shl 3 + else + nn = arguments + db 0CDh + end match + dw nn + end macro + + CALL 0 + CALL NC,2135h + +This approach also allows to handle other, more difficult cases, like when +the arguments may contain commas or are delimited in different ways. + + + +How are the labels processed? + +A standard way of defining a label is by following its name with ":" (this +also acts like a line break and any other command, including another label, +may follow in the same line). Such label simply defines a symbol with +the value equal to the current address, which initially is zero and increases +when any bytes are added into the output. + In some variants of assembly language it may be desirable to allow label +to precede an instruction without an additional ":" inbetween. It is then +necessary to create a labeled macroinstruction that after defining a label +passes processing to the original macroinstruction with the same name: + + struc INC? argument + .: + INC argument + end struc + + start INC A + INC B + +This has to be done for every instruction that needs to allow this kind +of syntax. A simple loop like the following one would suffice: + + iterate instruction, EX,INC,CALL + struc instruction? argument + .: instruction argument + end struc + end iterate + +Every built-in instruction that defines data already has the labeled variant. + By defining a labeled instruction that has "?" in place of name it is +possible to intercept every line that starts with an identifier that is not +a known instruction and is therefore assumed to be a label. The following one +would allow a label without ":" to begin any line in the source text (it also +handles the special cases so that labels followed with ":" or with "=" and +a value would still work): + + struc ? tail& + match :, tail + .: + else match : instruction, tail + .: instruction + else match == value, tail + . = value + else + .: tail + end match + end struc + +Obviously, it is no longer needed to define any specific labeled +macrointructions when a global effect of this kind is applied. A variant +should be chosen depending on the type of syntax that needs to be allowed. + Intercepting even the labels defined with ":" may become useful when the +value of current address requires some additional processing before being +assigned to a label - for example when a processor uses addresses with a +unit larger than a byte. The intercepting macroinstruction might then look +like this: + + struc ? tail& + match :, tail + label . at $ shr 1 + else match : instruction, tail + label . at $ shr 1 + instruction + else + . tail + end match + end struc + + The value of current address that is used to define labels may be altered +with "org". If the labels need to be differentiated from absolute values, +a symbol defined with "element" may be used to form an address: + + element CODEBASE + org CODEBASE + 0 + + macro CALL? argument + local value + value = argument + if value relativeto CODEBASE + db 0CDh + dw value - CODEBASE + else + err "incorrect argument" + end if + end macro + + To define labels in an address space that is not going to be reflected in +the output, a "virtual" block should be declared. The following sample +prepares macroinstructions "DATA" and "CODE" to switch between generating +program instructions and data labels. Only the instruction codes would go to +the output: + + element DATA + DATA_OFFSET = 2000h + element CODE + CODE_OFFSET = 1000h + + macro DATA? + _END + virtual at DATA + DATA_OFFSET + end macro + + macro CODE? + _END + org CODE + CODE_OFFSET + end macro + + macro _END? + if $ relativeto DATA + DATA_OFFSET = $ - DATA + end virtual + else if $ relativeto CODE + CODE_OFFSET = $ - CODE + end if + end macro + + postpone + _END + end postpone + + CODE + +The "postpone" block is used here to ensure that the "virtual" block +always gets closed correctly, even if source text ends with data +definitions. + Within the environment prepared by the above sample any instruction +would be able to distinguish data labels from the ones defined within +program. For example a branching instruction could be made to accept +an argument being either a label within a program or an absolute value, +but to disallow any label of data: + + macro CALL? argument + local value + value = argument + if value relativeto CODE + db 0CDh + dw value - CODE + else if value relativeto 0 + db 0CDh + dw value + else + err "incorrect argument" + end if + end macro + + DATA + + variable db ? + + CODE + + routine: + +In this context either "CALL routine" or "CALL 1000h" would be allowed, +while "CALL variable" would not be. + When the labels have values that are not absolute numbers, it is +possible to generate relocations for instructions that use them. +A special "virtual" block may be used to store the offsets of values +inside the program that need to be relocated when its base changes: + + virtual at 0 + Relocations:: + rw RELOCATION_COUNT + end virtual + + RELOCATION_INDEX = 0 + + postpone + RELOCATION_COUNT := RELOCATION_INDEX + end postpone + + macro WORD? value + if value relativeto CODE + store $ - CODE : 2 at Relocations : RELOCATION_INDEX shl 1 + RELOCATION_INDEX = RELOCATION_INDEX + 1 + dw value - CODE + else + dw value + end if + end macro + + macro CALL? argument + local value + value = argument + if value relativeto CODE | value relativeto 0 + db 0CDh + word value + else + err "incorrect argument" + end if + end macro + +The table of relocations that is created this way can then be accessed +with "load". The following two lines could be used to put the table +in its entirety somewhere in the output: + + load RELOCATIONS : RELOCATION_COUNT shl 1 from Relocations : 0 + dw RELOCATIONS + +The "load" reads the whole table into a single string, then "dw" writes it +into output (padded to multiple of a word, but in this case the string never +requires such padding). + For more complex types of relocations additional modifier may need to be +employed. For example, if upper and lower portions of an address needed to be +stored in separate places (likely across two instructions) and relocated +separately, necessary modifiers could be implemented as follows: + + element MOD.HIGH + element MOD.LOW + + HIGH? equ MOD.HIGH + + LOW? equ MOD.LOW + + + macro BYTE? value + if value relativeto MOD.HIGH + CODE + ; register HIGH relocation + db (value - MOD.HIGH - CODE) shr 8 + else if value relativeto MOD.LOW + CODE + ; register LOW relocation + db (value - MOD.LOW - CODE) and 0FFh + else if value relativeto MOD.HIGH + db (value - MOD.HIGH) shr 8 + else if value relativeto MOD.LOW + db (value - MOD.LOW) and 0FFh + else + db value + end if + end macro + +The commands that would register relocation have been omitted for clarity, +in this case not only offset within code but some additional information would +need to registered in appropriate structures. With such preparation, relocatable +units in code might be generated like: + + BYTE HIGH address + BYTE LOW address + +Such approach allows to easily enable syntax with modifiers in any instruction +that internally uses "byte" macroinstruction when generating code. + + + +How can multiple sections of file be generated in parallel? + +This assembly engine has a single main output that has to be generated +sequentially. This may seem problematic when the file needs to contains +distinct sections for code and data, collected from interleaved pieces that +may be spread across multiple source files. There are, however, a couple of +methods to handle it, all based in one way or another on forward-referencing +capabilities of the assembler. + A natural approach is to define contents of auxiliary section in "virtual" +block and copy it to appropriate position in the output with a single +operation. When a "virtual" block is labeled, it can be re-opened multiple +times to append more data to it. + + include '8086.inc' + org 100h + jmp CodeSection + + DataSection: + + virtual + Data:: + end virtual + + postpone + virtual Data + load Data.OctetString : $ - $$ from $$ + end virtual + end postpone + + db Data.OctetString + + CodeSection: + + virtual Data + Hello db "Hello!",24h + end virtual + + mov ah,9 + mov dx,Hello + int 21h + + virtual Data + ExitCode db 37h + end virtual + + mov ah,4Ch + mov al,[ExitCode] + int 21h + +This leads to a relatively simple syntax even without help of additional +macros. + Another method could be to put the pieces of the section into macros and +execute them all at the required position in source. A disadvantage of such +approach is that tracing errors in definitions might become a bit cumbersome. + The techniques that allow to easily append to a section generated in +parallel can also be very useful to generate data structures like relocation +tables. Instead of "store" commands used earlier when demonstrating +the concept, regular data directives could be used inside a re-opened +"virtual" block to create relocation records. + + + +What options are there to parse other kinds of syntax? + +In some cases a command that assembler needs to parse may begin with +something different than a name of instruction or a label. It may be +that a name is preceded by a special character, like "." or "!", +or that it is an entirely different kind of construction. It is then +necessary to use "macro ?" to intercept whole lines of source text +and process any special syntax of such kind. + For example, if it was required to allow a command written as ".CODE", +it would not be possible to implement it directly as a macroinstruction, +because initial dot causes the symbol to be interpreted as a local one +and globally defined instruction could never be executed this way. +The intercepting macroinstruction provides a solution: + + macro ? line& + match .=CODE?, line + CODE + else match .=DATA?, line + DATA + else + line + end match + end macro + +The lines that contain either ".CODE" or ".DATA" text are processed here +in such a way, that they invoke the global macroinstruction with +corresponding name, while all other intercepted lines are executed without +changes. This method allows to filter out any special syntax and let +the assembler process the regular instructions as usual. + Sometimes unconventional syntax is expected only in a specific area +of source text, like inside a block with defined boundaries. The +parsing macroinstruction should then be applied only in this place, +and removed with "purge" when the block ends: + + macro concise + macro ? line& + match =end =concise, line + purge ? + else match dest+==src, line + ADD dest,src + else match dest-==src, line + SUB dest,src + else match dest==src, line + LD dest,src + else match dest++, line + INC dest + else match dest--, line + DEC dest + else match any, line + err "syntax error" + end match + end macro + end macro + + concise + C=0 + B++ + A+=2 + end concise + +A macroinstruction defined this way does not intercept lines that contain +directives controlling the flow of the assembly, like "if" or "repeat", and +they can still be used freely inside such a block. This would change if +the declaration was in the form "macro ?! line&". Such a variant would +intercept every line with no exception. + Another option to catch special commands might be to use "struc ?" +to intercept only lines that do not start with a known instruction +(the initial symbol is then treated as label). Since this one only tests +unknown commands, it should cause less overhead on the assembly: + + struc (head) ? tail& + match .=CODE?, head + CODE tail + else + head tail + end match + end struc + + All these approaches hide a subtle trap. A label defined with ":" may be +followed by another instruction in the same line. If that next instruction +(which here becomes hidden in the "tail" parameter) is a control directive +like "if", putting it inside the "else" clause is going to cause broken nesting +of control blocks. A possible solution is to somehow invoke "tail" contents +outside of "match" block. One way could be to call a special macro: + + struc (head) ? tail& + local invoker + match .=CODE?, head + macro invoker + CODE tail + end macro + else + macro invoker + head tail + end macro + end match + invoker + end struc + +A simpler option is to call the original line directly and when override +is needed, cause it to be ignored with help of another line interceptor +(disposing of itself immediately after): + + struc (head) ? tail& + match .=CODE?, head + CODE tail + macro ? line& + purge ? + end macro + end match + head tail + end struc + +However, a much better way of avoiding this kinds of pitfalls is to use +CALM instructions instead of standard macros. There it is possible to +process arguments and assemble the original or modified line without +use of any control directives. CALM instructions also offer a much better +performance, which might be especially important in case of interceptors +that get called for nearly every line in source text. + + + +How to define an instruction sharing a name with one of the core directives? + + It may happen that a language can be in general easily implemented with +macros, but it needs to include a command with the same name as one of +the directives of assembler. While it is possible to override any +instruction with a macro, macros themself may require an access to +the original directive. To allow the same name call a different instruction +depending on the context, the implemented language may be interpreted +within a namespace that contains overriding macro, while all the macros +requiring access to original directive would have to temporarily switch +to another namespace where it has not have been overridden. This would +require every such macro to pack its contents in a "namespace" block. + But there is another trick, related to how texts of macro parameters +or symbolic variables preserve the context under which the symbols within +them should be interpreted (this includes the base namespace and +the parent label for symbols starting with dot). + Unlike the two mentioned occurences, the text of a macro normally does +not carry such extra information, but if a macro is constructed in such way +that it contains text that was once carried within a parameter to another +macro or within a symbolic variable, then this text retains the information +about context even when it becomes a part of a newly defined macro. +For example: + + macro definitions end? + namespace embedded + struc LABEL? size + match , size + .: + else + label . : size + end match + end struc + macro E#ND? name + end namespace + match any, name + ENTRYPOINT := name + end match + macro ?! line& + end macro + end macro + end macro + + definitions end + + start LABEL + END start + +The parameter given to "definitions" macro may appear to do nothing, as it +replaces every instance of "end" with exactly the same word - but the text +that comes from the parameter is equipped with additional information about +context, and this attribute is then preserved when the text becomes a part +of a new macro. Thanks to that, macro "LABEL" can be used in a namespace +where "end" instruction has taken a different meaning, but the instances +of "end" within its body still refer to the symbol in the outer namespace. + In this example the parameter has been made case-insensitive, and thus +it would replace even the "END" in "macro" statement that is supposed to +define a symbol in "embedded" namespace. For this reason the identifier +has been split with a concatenation operator to prevent it from being +recognized as parameter. This would not be necessary if the parameter +was case-sensitive (as more usual). + The same effect can be achieved through use of symbolic variables instead +of macro parameters, with help of "match" to extract the text of a symbolic +variable: + + define link end + match end, link + namespace embedded + struc LABEL? size + match , size + .: + else + label . : size + end match + end struc + macro END? name + end namespace + match any, name + ENTRYPOINT := name + end match + macro ?! line& + end macro + end macro + end match + + start LABEL + END start + +This would not work without passing the text through symbolic variable, +because parameters defined by control directives like "match" do not +add context information to the text unless it was already there. + CALM instructions allow for another approach to this kind of problems. +If a customized instruction set is defined entirely in form of CALM, +they may not even need an access to original control directives. +However, if CALM instruction needs to assemble a directive that might not +be accessible, the symbolic variable passed to "assemble" should be +defined with appropriate context for the instruction symbol. + + + +How to convert a macroinstruction to CALM? + +A classic macroinstruction consists of lines of text that are preprocessed +(by replacing names of parameters with their corresponding values) every time +the instruction is called and these preprocessed lines are passed to assembly. +For example this macroinstruction generates just a single line to be assembled, +and it does it by replacing "number" with the text given by the only argument +to the instruction: + + macro octet value* + db value + end macro + +A CALM instruction can be viewed as customized preprocessor, which needs to +be written in a special language. It is able to use various commands to +process the arguments and generate lines to be assembled. On the basic +level, it is also able to simulate what standard preprocessor does - with +help of "arrange" command. After preprocessing the line, it also needs to +explicitly pass it to the assembly with an "assemble" command: + + calminstruction octet value* + arrange value, =db value + assemble value + end calminstruction + +This gives the same result as the original macroinstruction, as it performs +the same kind of preprocessing. However, unlike the text of macroinstruction +a pattern given to "arrange" needs to explicitly state which name tokens are +to be replaced with their values and which ones (prepended with "=") should +be left untouched. The tokens that are copied from the pattern are stripped of +any context information, just like the text of macroinstruction is normally not +carrying any (while the values that came from arguments retain the recognition +context in which the instruction was started). + This is the most straightforward method of conversion and a simple sequence +of "arrange" and "assemble" commands could be made to generate the same lines as +by the original macroinstruction. But there is one exception - when a "local" +command is executed by macroinstruction, it creates a preprocessed parameter +with a special value that points to a symbol in the namespace unique to given +instance of the instruction. + + macro pointer + local next + dd next + next: + end macro + +In case of CALM there is no such namespace available, the local namespace of +a CALM instruction is shared among all its instances. Therefore, if a new unique +symbol is needed every time the instruction is called, it has to be constructed +manually. An obvious method might be to append a unique number to the name: + + global_uid = 0 + + calminstruction pointer + compute global_uid, global_uid + 1 + local command + arrange command, =dd =next#global_uid + assemble command + arrange command, =next#global_uid: + assemble command + end calminstruction + +Here "arrange" is given a variable that has a numeric value and it has to +replace it with a text. This works only when the value is a plan non-negative +number, in such case "arrange" converts it to a text token that contains decimal +representation of that number. The lines passed to assembly are therefore +going to contains identifiers like "next#1". + While incrementation of the global counter could be done by preparing +a standard assembly command like "global_uid = global_uid + 1" with "arrange" +and passing it to assembly, "compute" command allows to do it directly in the +CALM processor. Moreover, it is then not affected by anything that alters +the context of assembly. If the instruction was defined as unconditional and +used inside a skipped IF block, the "compute" would still perform its task, +because execution of CALM commands is - just like standard preprocessing - done +independently from the main flow of the assembly. Also, references to +the "global_uid" always point to the same symbol - the one that was in scope +when the CALM instruction was defined and compiled. Therefore incrementing +the value with "compute" is more reliable and predictable. + In a similar manner, the assembly of line defining the label can be replaced +with a "publish" command. Here the value of the label (which should be equal +to the address after the line containing "dd" is assembled) needs to be computed +first, because "publish" only performs the assignment of a value to the symbol: + + global_uid = 0 + + calminstruction pointer + compute global_uid, global_uid + 1 + local symbol, command + arrange symbol, =next#global_uid + arrange command, =dd symbol + assemble command + local address + compute address, $ + publish symbol:, address + end calminstruction + +Because the CALM instruction itself is conditional, the "publish" inside is +effectively conditional, too. Therefore it works correctly as a replacement +for the assembly of line with a label. + While a global counter has several advantages, it can be interfered with, +so sometimes use of a local counter might be preferable. However, the local +namespace of CALM instruction is not normally not accessible from outside, so +it is a bit harder to give an initial value to such counter. One way could be +to check whether the counter has already been initialized with some value using +"take" command: + + calminstruction pointer + local id + take id, id + jyes increment + compute id, 0 + increment: + compute id, id + 1 + local symbol, command + arrange symbol, =next#id + arrange command, =dd symbol + assemble command + local address + compute address, $ + publish symbol:, address + end calminstruction + +But this adds commands that are executed every time the instruction is called. +A better solution makes use of the ability to define custom instructions +processed during the definition of CALM instruction: + + calminstruction calminstruction?.init? var*, val:0 + compute val, val + publish var, val + end calminstruction + + calminstruction pointer + local id + init id, 0 + compute id, id + 1 + local symbol, command + arrange symbol, =next#id + arrange command, =dd symbol + assemble command + local address + compute address, $ + publish symbol:, address + end calminstruction + +The custom statement "init" is called at the time when the CALM instruction is +defined (it does not generate any commands to be executed by the defined +instruction - it would itself have to use "assemble" commands to generate +statements to be compiled). It is given the name of variable from the local +scope of the CALM instruction, and it uses "publish" to assign an initial +numeric value to that variable. + To initialize local variable with a symbolic value, even simpler custom +instruction would suffice: + + calminstruction calminstruction?.initsym? var*, val& + publish var, val + end calminstruction + +The text of "val" argument carries the recognition context of the definition +of CALM instruction that contains the "initsym" statement, therefore it allows +to prepare a text for "assemble" containing references to local symbols: + + calminstruction be32? value + local command + initsym command, dd value + compute value, value bswap 4 + assemble command + end calminstruction + +Again, after this intruction is compiled, it contains just two actual commands, +"compute" and "assemble", and the value of local symbol "command" is a text +that is interpreted in the same local context and refers to the same symbol +"value" as the "compute" does. + This example also demonstrates another advantage of CALM over standard +macroinstructions: its strict semantics prevent various kinds of unwanted +behavior that is allowed by a simple substitution of text. The text of "value" +is going to be evaluated by "compute" as a numeric sub-expression, signalling +an error on any unexpected syntax. Therefore it should be favorable to process +arguments entirely through CALM commands and only use "assemble" for final +simple statements. \ No newline at end of file diff --git a/x86_64_sse2_x87/fasm/docs/manual.txt b/x86_64_sse2_x87/fasm/docs/manual.txt new file mode 100644 index 0000000..a5b97f2 --- /dev/null +++ b/x86_64_sse2_x87/fasm/docs/manual.txt @@ -0,0 +1,2343 @@ + + +flat assembler g +User Manual + + +This document describes the syntax of flat assembler g language, with basic +examples. It was written with an assumption that it would be read sequentially +and at any moment it uses only the concepts and constructions that have been +introduced earlier. However it should be possible to jump right to the +section that interests the reader, and then go back to earlier parts only when +it is needed in order to better understand the later ones. + + +Table of contents + +0. Executing the assembler +1. Fundamental syntax rules +2. Symbol identifiers +3. Basic symbol definitions +4. Expression values +5. Symbol classes +6. Generating data +7. Conditional assembly +8. Macroinstructions +9. Labeled macroinstructions +10. Symbolic variables and recognition context +11. Repeating blocks of instructions +12. Matching parameters +13. Output areas +14. Source and output control +15. CALM instructions + + +0. Executing the assembler + +To start assembly from the command line it is necessary to provide at least one +parameter, the name of a source file, and optionally a second one - +name of the destination file. If the assembly is successful, the generated +output is written into the destination and a short summary is displayed, +otherwise an information about errors is shown. The maximum number of presented +errors can be controlled with an additional "-e" switch (by default no more than +one error is presented). The "-p" switch controls the maximum number of passes +the assembler is going to attempt. This limit is by default set to 100. +The "-r" switch allows to set up the limit of the recursion stack, that is the +maximum allowed depth of entering macroinstructions and including additional +source files. The "-v" switch can enable showing all the lines from this stack +when reporting an error (by default the assembler tries to select only the +lines that are likely the most informative, but this simple heuristic may not +always be correct). If "-v" switch is used with value 2, it in addition makes +all the messages displayed by commands from the source text to be shown in real +time (in every consecutive pass). The "-i" switch allows to insert any command at +the beginning of processed source. + + +1. Fundamental syntax rules + +Every command in the assembly language occupies a single line of text. +If a line contains the semicolon character, everything from that character up +to the end of the line is treated as a comment and ignored by the assembler. +The main part of a line (i.e. excluding the comment) may end with the backslash +character and in such case the next line from the source text is going to be +appended to this one. This allows to split any command across multiple lines, +when needed. From now on we will refer to a source line as an entity obtained +by stripping comments and joining the lines of text connected with backslash +characters. + The text of source line is divided into syntactical units called tokens. +There is a number of special characters that become separate tokens all by +themselves. Any of the characters listed below is such a syntactical unit: + + +-/*=<>()[]{}:?!,.|&~#`\ + +Any contiguous (i.e. not broken by whitespace) sequence of characters other than +the above ones becomes a single token, which can be a name or a number. +The exception to this rule is when a sequence starts with the single or the double +quote character. This defines a quoted string and it may contain any of the +special characters, whitespace and even semicolons, as it ends only when the +same character that was used to start it is encountered. The quotes that are +used to enclose the string do not become a part of the string themselves. +If it is needed to define a string containing the same character that is used to +enclose it, the character needs to be doubled inside the string - only one copy +of the character will become a part of the string, and the sequence will +continue. + Numbers are distinguished from names by the fact that they either +begin with a decimal digit, or with the "$" character followed by any hexadecimal +digit. This means that a token can be considered numeric even when it is not a +valid number. To be a correct one it must be one of the following: a decimal +number (optionally with the letter "d" attached at the end), a binary number +followed by the letter "b", an octal number followed by the letter "o" or "q", or a +hexadecimal number either prepended with "$" or "0x", or followed by the character +"h". Because the first digit of a hexadecimal number can be a letter, it may be +needed to prepend it with the digit zero in order to make it recognizable as a number. +For example, "0Ah" is a valid number, while "Ah" is just a name. + + +2. Symbol identifiers + +Any name can become a defined symbol by having some meaning (a value) assigned to it. +One of the simplest methods of creating a symbol with a given value is to use +the "=" command: + + a = 1 + +The ":" command defines a label, that is a symbol with a value equal to the +current address in the generated output. At the beginning of the source text this +address is always zero, so when the following two commands are the first ones +in the source file, they define symbols that have identical values: + + first: + second = 0 + +Labels defined with ":" command are special constructs in assembly language, +since they allow any other command (including another label definition) to +follow in the same line. This is the only kind of command that allows this. + What comes before the ":" or "=" character in such definition is a symbol +identifier. It can be a simple name, like in the above samples, but it may +also contain some additional modifiers, described below. + When a name in a symbol definition has the "?" character appended to it (with +no whitespace between them), the symbol is case-insensitive (otherwise it would +be defined as case-sensitive). This means that the value of such +symbol may be referred to (as in an expression to the right of the "=" character) +by the name being any variant of the original name that differs only in the case +of letters. Only the cases of the 26 letters of the English alphabet are +allowed to differ, though. + It is possible to define a case-sensitive symbol that clashes with a +case-insensitive one. Then the case-sensitive symbol takes precedence and the more +general one is used only when corresponding case-sensitive symbol is not defined. +This can be remedied by using the "?" modifier, since it always means that the name +followed by it refers to the case-insensitive symbol. + + tester? = 0 + tester = 1 + TESTER = 2 + x = tester ; x = 1 + y = Tester ; y = 0 + z = TESTER ; z = 2 + t = tester? ; t = 0 + + Every symbol has its own namespace of descendants, called child namespace. When two +names are connected with a dot (with no whitespace in between), such identifier refers to +an entity named by the second one in the namespace of descendants to the symbol specified +by the first one. This operation can be repeated many times within a single identifier, +allowing to refer to descendants of descendants in a chain of any length. + + space: + space.x = 1 + space.y = 2 + space.color: + space.color.r = 0 + space.color.g = 0 + space.color.b = 0 + +Any of the names in such chain may optionally be followed by the "?" character +to mark that it refers to a case-insensitive symbol. If "?" is inserted in +the middle of the name (effectively splitting it into separate tokens) such +identifier is considered a syntactical error. + When an identifier starts with a dot (in other words: when the name of the parent +symbol is empty), it refers to the symbol in the namespace of the most recent +regular label defined before current line. This allows to rewrite the above sample +like this: + + space: + .x = 1 + .y = 2 + .color: + .color.r = 0 + .color.g = 0 + .color.b = 0 + +After the "space" label is defined, it becomes the most recently defined normal +label, so the following ".x" refers to the "space.x" symbol and then the ".color" +refers to the "space.color". + The "namespace" command followed by a symbol identifier changes the base +namespace for a section of source text. It must be paired with the +"end namespace" command later in the source to mark the end of such block. +This can be used to again rewrite the above sample in a different way: + + space: + namespace space + x = 1 + y = 2 + color: + .r = 0 + .g = 0 + .b = 0 + end namespace + +When a name is not preceded by a dot, and as such it does not have explicitly +specified in what namespace the symbol resides, the assembler looks for defined +symbol in the current namespace, and if none is found, in the consecutive namespaces +of parent symbols, starting from the namespace containing the parent symbol of +current namespace. If no defined symbol with such name is found, it is assumed that +the name refers to the symbol in the current namespace (and unless there is "?" +character after such name, it is assumed that the symbol is case-sensitive). +A definition that does not specify the namespace where the new symbol should be +created, always makes a new symbol in the current base namespace. + + global = 0 + regional = 1 + namespace regional + regional = 2 ; regional.regional = 2 + x = global ; regional.x = 0 + regional.x = regional ; regional.regional.x = 2 + global.x = global ; global.x = 0 + end namespace + +The comments in the above sample show equivalent definitions with respect +to the original base namespace. Note that when a name is used to specify the +namespace, the assembler looks for a defined symbol with such name to lookup in +its namespace, but when it is a name of a symbol to be defined, it is always +created within the current base namespace. + When the final dot of an identifier is not followed by any name, it refers +to the parent symbol of the namespace that would be searched for a symbol if +there was a name after this dot. Adding such dot at the end of an identifier may +appear redundant, but it can be used to alter the way the definition of a symbol +works, because it forces the assembler to look for an already existing symbol that +it can alter instead of squarely creating a new one in the current namespace. +For instance, if in the fourth line of the previous example "regional." was put +in place of "regional", it would rewrite a value of the original "regional" +symbol instead of making a new symbol in the child namespace. Similarly, +a definition formed this way may assign a new value to a symbol regardless of +whether it was previously defined as case-insensitive or not. + If an identifier is just a single dot, by the above rules it refers to the most +recent label that did not start with a dot. This can be applied to rewrite +the earlier example in yet another way: + + space: + namespace . + x = 1 + y = 2 + color: + namespace . + r = 0 + g = 0 + b = 0 + end namespace + end namespace + +It also demonstrates how namespace sections can be nested one within another. + The "#" may be inserted anywhere inside an identifier without changing its +meaning. When "#" is the only character separating two name tokens, it causes +them to be interpreted as a single name formed by concatenating the tokens. + + variable = 1 + varia#ble = var#iable + 2 ; variable = 3 + +This can also be applied to numbers. + Inside a block defined with "namespace" there is initially no label that would +be considered base for identifiers starting with dot (however the label that +served this purpose earlier is brought back to use after "end namespace"). +A similar thing also happens in the beginning of the source text, before any +label has been defined. This is connected to a couple of additional rules +concerning use of dots in identifiers. + When an identifier starts with a dot, but there is no label that would be +a parent for it, the identifier refers to the descendant of a special symbol +that resides in the current namespace but has no name. If an identifier starts +with a sequence of two or more dots, the identifier refers to the descedant of +a similar unnamed symbol, but it is a distinct one for any given number of dots. +While the namespace accessed with a single starting dot changes every time a new +regular label is defined, the special namespace accessed with two or more dots +in the beginning of an identifier remains the same: + + first: + .child = 1 + ..other = 0 + second: + .child = 2 + ..another = ..other + +In this example the meaning of the ".child" identifier changes from place to +place, but the "..other" identifier means the same everywhere. + When two names inside an identifier are connected with a sequence of two or +more dots, the identifier refers to the descendant of such special unnamed +symbol in the namespace specified by the identifier before that sequence of +dots. The unnamed child namespace is chosen depending on a number of dots and +in this case the number of required dots is increased by one. The following +example demonstrates the two methods of identifying such symbol: + + namespace base + ..other = 1 + end namespace + + result = base.#..other + +The "#" character has been inserted into the last identifier for a better +readability, but the plain sequence of three dots would do the same. + The unnamed symbol that hosts a special namespace can itself be accessed +when an identifier ends with a sequence of two or more dots - thanks to the +rule that an identifier which ends in a dot refers to the parent symbol of +the namespace that would be accessed if there was a name after this dot. So +in the context of the previous example the "base..." (or "base.#..") would +refer to the unnamed parent of the namespace where the "other" symbol resides, +and it would be the same symbol as identified by simple ".." inside the +namespace of the "base" symbol. + Any identifier can be prepended with a "?" character and such modifier has +an effect when it is used in a context where identifier could mean something +different than a label or variable to be defined. This modifier then +suppresses any other interpretation. For example, identifier starting with "?" +is not going to be treated as an instruction, even if it is the first symbol +on the line. This can be used to define a variable that shares a name with +an existing command: + + ?namespace = 0 + +If such modified identifier is used in a place where it is evaluated and not +defined, it still refers to the same symbol it would refer to in a definition. +Therefore, unless identifier also uses a dot, it always refers to a symbol +in the current namespace. + A number can be used in a role of a name inside an identifier, but not when +it is placed at the beginning, because then it is considered a literal value. +This restriction also may be bypassed by prepending an identifier with "?". + + +3. Basic symbol definitions + +When a symbol is defined as a label, it must be the only definition of +this symbol in the entire source. A value that is assigned to the symbol this way +can be accesed from every place in the source, even before the label is actually +defined. When a symbol is used before it is defined (this is often called +forward-referencing) the assembler tries to correctly predict the value of +the symbol by doing multiple passes over the source text. Only when all +predictions prove to be correct, the assembler generates the final output. + This kind of symbol, which can only be defined once and thus have a universal +value that can always be forward-referenced, is called a constant. All labels +are constants. + When a symbol is defined with a "=" command, it may have multiple definitions +of this kind. Such symbol is called variable and when it is used, the value from +its latest definition is accessed. A symbol defined with such command may also be +forward-referenced, but only when it is defined exactly once in the entire +source and as such has a single unambiguous value. + + a = 1 ; a = 1 + a = a + 1 ; a = 2 + a = b + 1 ; a = 3 + b = 2 + + A special case of forward-referencing is self-referencing, when the value +of a symbol is used in its own definition. The assembly of such construct is +successful only when the assembler is able to find a value that is stable under +such evaluation, effectively solving an equation. But due to the simplicity +of the resolving algorithm based on predictions a solution may not be found even +when it exists. + + x = (x-1)*(x+2)/2-2*(x+1) ; x = 6 or x = -1 + + The ":=" defines a constant value. It may be used instead of "=" to +ensure that the given symbol is defined exactly once and that it can be +forward-referenced. + The "=:" defines a variable symbol like "=", but it differs in how +it treats the previous value (when such exists). While "=" discards the +previous value, "=:" preserves it so it can later be brought back with the +"restore" command: + + a = 1 + a =: 2 ; preserves a = 1 + a = 3 ; discards a = 2 and replaces it with a = 3 + restore a ; brings back a = 1 + +A "restore" may be followed by multiple symbol identifiers separated with +commas, and it discards the latest definition of every one of them. It is not +considered an error to use "restore" with a symbol that has no active +definition (either because it was never defined or because all of its +definitions were already discarded earlier). If a symbol is treated with the +"restore" command, it becomes a variable and can never be forward-referenced. +For this reason "restore" cannot be applied to constants. + The "label" keyword followed by a symbol identifier is an alternative way +of defining a label. In this basic form it is equivalent to a definition made +with ":", but it occupies an entire line. However with this command it is +possible to provide more settings for the defined label. The identifier may +be optionally followed by the ":" token and then an additional value to be +associated with this label (usually denoting the size of the labeled entity). +The assembler has a number of built-in constants defining various sizes for +this purpose, but this value can also be provided as a plain number. + + label character:byte + label char:1 + +The ":" character may be omitted in favor of a plain whitespace, but it is +recommended for clarity. After an identifier and an optional size, the "at" +keyword may follow and then a value that should be assigned to the label instead +of the current address. + + label wchar:word at char + + The built-in size constants are equivalent to the following set of +definitions: + + byte? = 1 ; 8 bits + word? = 2 ; 16 bits + dword? = 4 ; 32 bits + fword? = 6 ; 48 bits + pword? = 6 ; 48 bits + qword? = 8 ; 64 bits + tbyte? = 10 ; 80 bits + tword? = 10 ; 80 bits + dqword? = 16 ; 128 bits + xword? = 16 ; 128 bits + qqword? = 32 ; 256 bits + yword? = 32 ; 256 bits + dqqword? = 64 ; 512 bits + zword? = 64 ; 512 bits + + The "element" keyword followed by a symbol identifier defines a special +constant that has no fixed value and can be used as a variable in the linear +polynomials. The identifier may be optionally followed by the ":" token and +then a value to be associated with this symbol, called metadata of the +element. + + element A + element B:1 + + The metadata assigned to a symbol can be extracted with a special operator, +defined in the next section. + + +4. Expression values + +In every construction described so far where a value of some kind was +provided, like after the "=" command or after the "at" keyword, it could be +a literal value (a number or a quoted string) or a symbol identifier. +A value can also be specified through an expression containing built-in +operators. + The "+", "-" and "*" perform standard arithmetic operations on integers +("+" and "-" can also be used in a unary form - with only one argument). +"/" and "mod" perform division with remainder, giving a quotient or a remainder +respectively. Of these arithmetic operators "mod" has the highest precedence +(it is calculated first), "*" and "/" come next, while "+" and "-" are evaluated +last (even in their unary variants). Operators with the same precedence are +evaluated from left to right. Parentheses can be used to enclose sub-expressions +when a different order of operations is required. + The "xor", "and" and "or" perform bitwise operations on numbers. "xor" is +addition of bits (exclusive or), "and" is multiplication of bits, and "or" is +inclusive or (logical disjunction). These operators have higher precedence +than any arithmetic operators. + The "shl" and "shr" perform bit-shifting of the first argument by the amount +of bits specified by the second one. "shl" shifts bits left (towards the higher +powers of two), while "shr" shifts bits right (towards zero), dropping bits that +fall into the fractional range. These operators have higher precedence than other +binary bitwise operations. + The "not", "bsf" and "bsr" are unary operators with even higher precedence. +"not" inverts all the bits of a number, while "bsf" and "bsr" search for the +lowest or highest set bit respectively, and give the index of that bit as a +result. + All the operations on numbers are performed as if they were done on the +infinite 2-adic representations of those numbers. For example the "bsr" with a +negative number as an argument gives no valid result, since such number has an +infinite chain of set bits extending towards infinity and as such contains no +highest set bit (this is signaled as an error). + The "bswap" operator allows to create a string of bytes containing the +representation of a number in a reverse byte order (big endian). The second +argument to this operator should be the length in bytes of the required string. +This operator has the same precedence as the "shl" and "shr" operators. + When a string value is used as an argument to any of the operations on +numbers, it is treated as a sequence of bits and automatically converted into +a positive number (extended with zero bits towards the infinity). The +consecutive characters of a string correspond to the higher and higher bits of a +number. + To convert a number back to a string, the "string" unary operator may be +used. This operator has the lowest possible precedence, so when it precedes +an expression, all of it is evaluated prior to the conversion. When conversion +in the opposite direction is needed, simple unary "+" is enough to make a string +become a number. + The length of a string may be obtained with the "lengthof" unary operator. This +operator can only be applied to a string and it is one of the operators with the +highest precedence. + When a symbol defined with the "element" command is used in an expression the +result may be a linear polynomial in a variable represented by the symbol. +Only simple arithmetic operations are allowed on the terms of a polynomial, +and it must stay linear - so, for example, it is only allowed to multiply a +polynomial by a number, but not by another polynomial. + There are a few operators with high precedence that allow to extract the information +about the terms of linear polynomial. The polynomial should come as the first argument, +and the index of the term as the second one. The "element" operator extracts +the variable of a polynomial term (with the coefficient of one), the "scale" operator +extracts the coefficient (a number by which the variable is multiplied) and "metadata" +operator gives back the metadata associated with the variable. + When the second argument is an index higher than the index of the last term +of the polynomial, all three operators return zero. When the second argument +is zero, "element" and "scale" give information about the constant term - +"element" returns numeric 1 and "scale" returns the value of the constant term. + + element A + linpoly = A + A + 3 + vterm = linpoly scale 1 * linpoly element 1 ; vterm = 2 * A + cterm = linpoly scale 0 * linpoly element 0 ; cterm = 3 * 1 + + The "metadata" operator with an index of zero returns the size that is associated +with the first argument. This value is definite only when the first argument is +a symbol that has a size associated with it (or an arithmetic expression +that contains such symbol), otherwise it is zero. There exists an additional +unary operator "sizeof", which gives the same value as "metadata 0". + + label table : 256 + length = sizeof table ; length = 256 + + The "elementof", "scaleof" and "metadataof" are variants of "element", "scale" +and "metadata" operators with the opposite order of arguments. Therefore when "sizeof" +is used in an expression it is equivalent to writing "0 metadataof" in its place. +These operators have even higher precendence than their counterparts and are +right-associative. + The order of the terms of the linear polynomial depends on the way in which the value +was constructed. Every arithmetic operation preserves the order of the terms in +the first argument, and the terms that were not present in the first argument are +attached at the end in the same order in which they occurred in the second argument. +This order only matters when extracting terms with appropriate operators. + The "elementsof" is another unary operator of the highest precedence, it +counts the number of variable terms of a linear polynomial. + An expression may also contain a literal value that defines a floating-point +number. Such number must be in decimal notation, it may contain "." character +as a decimal mark and may be followed by the "e" character and then a decimal +value of the exponent (optionally preceded by "+" or "-" to mark the sign of +exponent). When "." or "e" is present, it must be followed by at least +one digit. The "f" character can be appended at the end of such literal value. +If a number contains neither "." nor "e", the final "f" is the only way to +ensure that it is treated as floating-point and not as a simple decimal +integer. + The floating-point numbers are handled by the assembler in the binary form. +Their range and precision are at least as high as they are in the longest +floating-point format that the assembler is able to produce in the output. + Basic arithmetic operations are allowed to have a floating-point +number as any of the arguments, but none of the arguments may contain +a non-scalar (linear polynomial) terms then. The result of such operation is +always a floating-point number. + The unary "float" operator may be used to convert an integer value to +floating-point. This operator has the highest precedence. + The "trunc" is another unary operator with the highest precedence and it can be +applied to floating-point numbers. It extracts the integer part of a number +(it is a truncation toward zero) and the result is always a plain integer, not +a floating-point number. If the argument was already a plain integer, this +operation leaves it unchanged. + The "bsr" operator can be applied to floating-point numbers and it returns +the exponent of such number, which is the exponent of the largest power of +two that is not larger than the given number. The sign of the floating-point value +does not affect the result of this operation. + It is also allowed to use a floating-point number as the first argument +to the "shl" and "shr" operators. The number is then multiplied or divided by the +power of two specified by the second argument. + + +5. Symbol classes + +There are three distinct classes of symbols, determining the position in +source line at which the symbol may be recognized. A symbol belonging to the +instruction class is recognized only when it is the first identifier of the +command, while a symbol from the expression class is recognized only when used +to provide a value of arguments to some command. + All the types of definitions that were described in the earlier sections +create the expression class symbols. The "label" and "restore" are examples +of built-in symbols belonging to the instruction class. + In any namespace it is allowed for symbols of different classes to share the +same name, for example it is possible to define the instruction named "shl", +while there is also an operator with the same name - but an operator belongs +to the expression class. + It is even possible for a single line to contain the same identifier +meaning different things depending on its position: + + ?restore = 1 + restore restore ; remove the value of the expression-class symbol + + The third class of symbols are the labeled instructions. A symbol belonging +to this class may be recognized only when the first identifier of the command +is not an instruction - in such case the first identifier becomes a label to +the instruction defined by the second one. If we treat "=" as a special kind +of identifer, it may serve as an example of labeled instruction. + The assembler contains built-in symbols of all classes. Their names are +always case-insensitive and they may be redefined, but it is not possible to +remove them. When all the values of such symbol are removed with a command +like "restore", the built-in value persists. + The rules concerning namespace apply equally to the symbols of all classes, +for example symbol of instruction class belonging to the child namespace of +latest label can be executed by preceding its name with dot. It should be +noted, however, that when a namespace is specified through its parent symbol, +it is always a symbol belonging to the expression class. It is not possible to +refer to a child namespace of an instruction, only to the namespace belonging +to the expression class symbol with the same name. + + xor?.mask? := 10101010b + a = XOR.MASK ; symbol in the namespace of built-in case-insensitive "XOR" + + label?.test? := 0 + a = LABEL.TEST ; undefined unless "label?" is defined + +Here the namespace containing "test" belongs to an expression-class symbol, +not to the existing instruction "label". When there is no expression-class symbol +that would fit the "LABEL" specifier, the namespace chosen is the one that would +belong to the case-sensitive symbol of such name. The "test" is therefore not found, +because it has been defined in another namespace - the one of case-insensitive "label". + + +6. Generating data + +The "db" instruction allows to generate bytes of data and put them into the +output. It should be followed by one or more values, separated with commas. +When the value is numeric, it defines a single byte. When the value is a +string, it puts the string of bytes into output. + + db 'Hello',13,10 ; generate 7 bytes + +The "dup" keyword may be used to generate the same value multiple times. The +"dup" should be preceded by numeric expression defining the number of +repetitions, and the value to be repeated should follow. A sequence of values +may also be duplicated this way, in such case "dup" should be followed by the +entire sequence enclosed in parentheses (with values separated with commas). + + db 4 dup 90h ; generate 4 bytes + db 2 dup ('abc',10) ; generate 8 bytes + + When a special identifier consisting of a lone "?" character is used as a +value in the arguments to "db", it reserves a single byte. This advances the +address in the output where the next data are going to be put, but the reserved +bytes are not generated themselves unless they are followed by some other data. +Therefore if the bytes are reserved at the end of output, they do not increase +the size of generated file. This kind of data is called uninitialized, while +all the regular data are said to be initialized. + The "rb" instruction reserves a number of bytes specified by its argument. + + db ? ; reserve 1 byte + rb 7 ; reserve 7 bytes + + Every built-in instruction that generates data (traditionally called a data +directive) is paired with a labeled instruction of the same name. Such command +in addition to generating data defines a label at address of generated data, +with associated size equal to the size of data unit used by this instruction. +In case of "db" and "rb" this size is 1. + + some db sizeof some ; generate a byte with value 1 + + The "dw", "dd", "dp", "dq", "dt", "ddq", "dqq" and "ddqq" are instructions +analogous to "db" with a different sizes of data unit. The order of bytes +within a single generated unit is always little-endian. When a string of bytes +is provided as the value to any of these instructions, the generated data +is extended with zero bytes to the length which is the multiple of data unit. +The "rw", "rd", "rp", "rq", "rt", "rdq", "rqq" and "rdqq" are the instructions +that reserve a specified number of data units. The unit sizes associated with +all these instructions are listed in table 1. + The "dw", "dd", "dq", "dt" and "ddq" instructions allow floating-point +numbers as data units. Any such number is then converted into floating-point +format appropriate for a given size. + The "emit" (with a synonym "dbx") is a data directive that uses the size +of unit specified by its first argument to generate data defined by +the remaining ones. The size may be separated from the next argument with +a colon instead of a comma, for better readability. When the unit size +is such that it has a dedicated data directive, the definition made with "emit" +has the same effect as if these values were passed to the instruction tailored +for this size. + + emit 2: 0,1000,2000 ; generate three 16-bit values + + The "file" instruction reads the data from an external file and writes it +into output. The argument must be a string containing the path to the file, it +may optionally be followed by ":" and the numeric value specifying an offset +within the file, next it may be followed by comma and the numeric value +specifying how many bytes to copy. + + file 'data.bin' ; insert entire file + excerpt file 'data.bin':10h,4 ; insert selected four bytes + + + Table 1 Data directives + /------------------------------\ + | Size | Generate | Reserve | + | (bytes) | data | data | + |=========|==========|=========| + | 1 | db | rb | + | | file | | + |---------|----------|---------| + | 2 | dw | rw | + |---------|----------|---------| + | 4 | dd | rd | + |---------|----------|---------| + | 6 | dp | rp | + |---------|----------|---------| + | 8 | dq | rq | + |---------|----------|---------| + | 10 | dt | rt | + |---------|----------|---------| + | 16 | ddq | rdq | + |---------|----------|---------| + | 32 | dqq | rqq | + |---------|----------|---------| + | 64 | ddqq | rdqq | + |---------|----------|---------| + | * | emit | | + \------------------------------/ + + +7. Conditional assembly + +The "if" instruction causes a block of source text to be assembled only +under certain condition, specified by a logical expression that is an argument +to this instruction. The "else if" command in the following lines +ends the previous conditionally assembled block and opens a new one, assembled +only when the previous conditions were not met and the new condition (an +argument to "else if") is true. The "else" command ends the previous +conditionally assembled block and begins a block that is assembled only when +none of the previous conditions was true. The "end if" command should be used +to end the entire construction. There may be many or none "else if" commands +inside and no more than one "else". + A logical expression is a distinct syntactical entity from the basic +expressions that were described earlier. A logical expression consists of +logical values connected with logical operators. The logical operators are: +unary "~" for negation, "&" for conjunction and "|" for alternative. +The negation is evaluated first, while "&" and "|" are simply evaluated +from left to right, with no precedence over each other. + A logical value in its simplest form may be a basic expression, it then +corresponds to true condition if and only if its value is not constant zero. +Another way to create a logical value is to compare the values of two basic +expressions with one of the following operators: "=" (equal), "<" (less than), +">" (greater than), "<=" (less or equal), ">=" (greater or equal), +"<>" (not equal). + + count = 2 + if count > 1 + db '0' + db count-1 dup ',0' + else if count = 1 + db '0' + end if + + When linear polynomials are compared this way, the logical value is +valid only when they are comparable, which is whey they differ in constant +term only. Otherwise the condition like equality is neither universally true +nor universally false, since it depends on the values substituted for variables, +and assembler signals this as an error. + The "relativeto" operator creates a logical value that is true only when +the difference of compared values does not contain any variable terms. Therefore +it can be used to check whether two linear polynomials are comparable - the +"relativeto" condition is true only when both compared polynomials have the same +variable terms. + Because logical expressions are lazily evaluated, it is possible to create +a single condition that will not cause an error when the polynomials are not +comparable, but will compare them if they are: + + if a relativeto b & a > b + db a - b + end if + + The "eqtype" operator can also be used to compare two basic expressions, +it makes a logical value which is true when the values of the expressions are +of the same type - either both are algebraic, both are strings or both are +floating-point numbers. An algebraic type covers the linear polynomials and +it includes the integer values. + The "eq" operator compares two basic expressions and creates a logical value +which is true only when their values are of the same type and equal. This operator +can be used to check whether a value is a certain string, a certain floating-point +number or a certain linear polynomial. It can compare values that are not +comparable with "=" operator. + The "defined" operator creates a logical value combined with a basic expression +that follows it. This condition is true when the expression does not contain +symbols that have no accessible definition. The expression is only tested for the +availability of its components, it does not need to have a computable value. +This can be used to check whether a symbol of expression class has been defined, +but since the symbol can be accessible through forward-referencing, this condition +may be true even when the symbol is defined later in source. If this is undesirable, +the "definite" operator should be used instead, as it checks whether all symbols +within a basic expression that follows have been defined earlier. + The basic expression that follows "defined" is also allowed to be empty and +the condition is then trivially satisfied. This does not apply to "definite". + The "used" operator forms a logical value if it is followed by a single +identifier. This condition is true when the value of specified symbol has +been used anywhere in the source. + The "assert" is an instruction that signalizes an error when a condition +specified by its argument is not met. + + assert a < 65536 + + +8. Macroinstructions + +The "macro" command allows to define a new instruction, in form of a +macroinstruction. The block of source text between the "macro" and +"end macro" command becomes the text of macroinstruction and this sequence +of lines is assembled in place of the original command that starts with +identifier of instruction defined this way. + + macro null + db 0 + end macro + + null ; "db 0" is assembled here + + The macroinstruction is allowed to have arguments only when the +definition contains them. After the "macro" and the identifier of defined +symbol optionally may come a list of simple names separated with commas, +these names define the parameters of macroinstruction. When this instruction +is then used, it may be followed by at most the same number of arguments +separated with commas, and their values are assigned to the consecutive +parameters. Before any line of text inside the macroinstruction is interpreted, +the name tokens that correspond to any of the parameters are replaced with their +assigned values. + + macro lower name,value + name = value and 0FFh + end macro + + lower a,123h ; a = 23h + +The value of a parameter can be any text, not necessarily a correct expression. +If a line calling the macroinstruction contains fewer arguments than the +number of defined parameters, the excess parameters receive the empty values. + When a name of a parameter is defined, it may be followed by "?" character +to denote that it is case-insensitive, analogously to a name in a symbol +identifier. There must be no whitespace between the name and "?". +A definition of a parameter may also be followed by "*" to denote that it +requires a value that is not empty, or alternatively by ":" character +followed by a default value, which is assigned to the parameter instead of +an empty one when no other value is provided. + + macro prepare name*,value:0 + name = value + end macro + + prepare x ; x = 0 + prepare y,1 ; y = 1 + + If an argument to macroinstruction needs to contain a comma character, the +entire argument must be enclosed between the "<" and ">" characters (they do +not become a part of the value). If another "<" character is encountered inside +such value, it must be balanced with corresponding ">" character inside the +same value. + + macro data name,value + name: + .data db value + .end: + end macro + + data example, <'abc',10> + + The last defined parameter may be followed by "&" character to denote that +this parameter should be assigned a value containing the entire remaining +part of line, even if it normally would define multiple arguments. Therefore +when macroinstruction has just one parameter followed by "&", the value of +this parameter is the entire text of arguments following the instruction. + + macro id first,rest& + dw first + db rest + end macro + + id 2, 7,1,8 + + When a name of a parameter is to be replaced with its value and it is +preceded by "`" character (without any whitespace inbetween), the text of +the value is embedded into a quoted string and this string replaces +both the "`" character and the name of parameter. + + macro text line& + db `line + end macro + + text x+1 ; db 'x+1' + + The "local" is a command that may only be used inside a macroinstruction. +It should be followed by one or more names separated with commas, and it +declares that the names from this list should in the context of current +macroinstruction be interpreted as belonging to a special namespace +associated with this macroinstruction instead of current base namespace. This +allows to create unique symbols every time the macroinstruction is called. +Such declaration defines additional parameters with the specified names and +therefore only affects the uses of those names that follow within the same +macroinstruction. Declaring the same name as local multiple times within +the same macroinstruction gives no additional effect. + + macro measured name,string + local top + name db string + top: name.length = top - name + end macro + + measured hello, 'Hello!' ; hello.length = 6 + +A parameter created with "local" becomes replaced with a text that contains +the same name as the name of parameter, but has added context information +that causes it to be identified as belonging to the unique local namespace +associated with the instance of macroinstruction. This kind of context +information is going to be discussed further in the section about +symbolic variables. + A symbol that is local to a macroinstruction is never considered the most +recent label that is base for symbols starting with dot. Moreover, its +descendant namespace is disconnected from the main tree of symbols, so if +"namespace" command was used with a local symbol as the argument, symbols +from the main tree would no longer be visible (including all the named +instructions of the assembler, even commands like "end namespace"). + Just like an expression symbol may be redefined and refer to its previous +value in the definition of the new one, the macroinstructions can also be +redefined, and use the previous value of this instruction symbol in its +text: + + macro zero + db 0 + end macro + + macro zero name + label name:byte + zero + end macro + + zero x + +And just like other symbols, a macroinstruction may be forward-referenced when +it is defined exactly once in the entire source. + The "purge" command discards the definition of a symbol just like "restore", +but it does so for the symbol of instruction class. It behaves in the same +way as "restore" in all the other aspects. A macroinstruction can remove its +own definition with "purge". + It is possible for a macroinstruction to use its own value in a recursive way, +but to avoid inadvertent infinite recursion this feature is only available when +the macroinstruction is marked as such by following its identifier with ":" +character. + + macro factorial: n + if n + factorial n-1 + result = result * (n) + else + result = 1 + end if + end macro + +In addition to allowing recursion, such macroinstruction behaves like a constant. +It cannot be redefined and "purge" cannot be applied to it. + A macroinstruction may in turn define another macroinstruction or a number +of them. The blocks designated by "macro" and "end macro" must be properly +nested one within the other for such definition to be accepted by the +assembler. + + macro enum enclosing + counter = 0 + macro item name + name := counter + counter = counter + 1 + end macro + macro enclosing + purge item,enclosing + end macro + end macro + + enum x + item a + item b + item c + x + + When it is required that macroinstruction generates unpaired "macro" or +"end macro" command, it can be done with special "esc" instruction. Its +argument becomes a part of macroinstruction, but is not being taken into +account when counting the nested "macro" and "end macro" pairs. + + macro xmacro name + esc macro name x& + end macro + + xmacro text + db `x + end macro + +If "esc" is placed inside a nested definition, it is not processed out until +the innermost macroinstruction becomes defined. This allows a definition +containing "esc" to be placed inside another macroinstruction without having +to repeat "esc" for every nesting level. + When an identifer of macroinstruction in its definition is followed by "!" +character, it defines an unconditional macroinstruction. This is a special +kind of instruction class symbol, which is evaluated even in places where the +assembly is suspended - like inside a conditional block whose condition is +false, or inside a definition of another macroinstruction. This allows to +define instructions that can be used where otherwise a directly stated +"end if" or "end macro" would be required, as in the following example: + + macro proc name + name: + if used name + end macro + + macro endp! + end if + .end: + end macro + + proc tester + db ? + endp + +If the macroinstruction "endp" in the above sample was not defined as an +unconditional one and the block started with "if" was being skipped, the +macroinstruction would not get evaluated, and this would lead to an error +because "end if" would be missing. + It should be noted that "end" command executes an instruction identified +by its argument in the child namespace of case-insensitive "end" symbol. +Therefore command like "end if" could be alternatively invoked with +an "end.if" identifier, and it is possible to override any such instruction +by redefining a symbol in the "end?" namespace. Moreover, any instruction +defined within the "end?" namespace can then be called with the "end" command. +This slighly modified variant of the above sample puts these facts to use: + + macro proc name + name: + if used name + end macro + + macro end?.proc! + end if + .end: + end macro + + proc tester + db ? + end proc + +A similar rule applies to the "else" command and the instructions in the +"else?" namespace. + When an identifier consisting of a lone "?" character is used as an +instruction symbol in the definition of macroinstruction, it defines a special +instruction that is then called every time a line to be assembled does not +contain an unconditional instruction, and the complete text of line becomes +the arguments to this macroinstruction. This special symbol can also be defined +as an unconditional instruction, and then it is called for every following line +with no exception. This allows to completely override the assembly process on +portions of the text. The following sample defines a macroinstruction which +allows to define a block of comments by skiping all the lines of text until it +encounters a line with content equal to the argument given to "comment". + + macro comment? ender + macro ?! line& + if `line = `ender + purge ? + end if + end macro + end macro + + comment ~ + Any text may follow here. + ~ + + The "mvmacro" is an instruction that takes two arguments, both identifying +an instruction-class symbols. The definition of a macroinstruction specified +by the second argument is moved to the symbol identified by the first one. +For the second symbol the effect of this command is the same as of "purge". +This allows to effectively rename a macroinstruction, or temporarily disable it +only to bring it back later. The symbols affected by this operation become +variables and cannot be forward-referenced. + + +9. Labeled macroinstructions + +The "struc" command allows to define a labeled instruction, in form of a +macroinstruction. Except for the fact that such definition must be closed +with "end struc" instead of "end macro", these macroinstructions are defined +in the same way as with "macro" command. A labeled instruction is evaluated +when the first identifier of a command is not an instruction and the second +identifier is of the labeled instruction class: + + struc some + db 1 + end struc + + get some ; "db 1" is assembled here + + Inside a labeled macroinstruction identifiers starting with dot no longer +refer to the namespace of a previously defined regular label. Instead they +refer to the namespace of label with which the instruction was labeled. + + struc POINT + label . : qword + .x dd ? + .y dd ? + end struc + + my POINT ; defines my.x and my.y + +Note that the parent symbol, which can be refered by "." identifier, is not +defined unless an appropriate definition is generated by the macroinstruction. +Furthermore, this symbol is not considered the most recent label in +the surrounding namespace unless it gets defined as an actual label in +the macroinstruction it labeled. + For an easier use of this feature, other syntaxes may be defined with +macroinstructions, like in this sample: + + macro struct? definition& + esc struc definition + label . : .%top - . + namespace . + end macro + + macro ends?! + %top: + end namespace + esc end struc + end macro + + struct POINT vx:?,vy:? + x dd vx + y dd vy + ends + + my POINT 10,20 + + The "restruc" command is analogous to "purge", but it operates on symbols +from the class of labeled instructions. Similarly, the "mvstruc" command is +the same as "mvmacro" but for labeled instructions. + As with "macro", it is possible to use an identifier consisting of a lone "?" +character with "struc". It defines a special labeled macroinstruction that is +called every time the first symbol of a line is not recognized as an instruction. +Everything that follows that first identifier becomes the arguments to labeled +macroinstruction. The following sample uses this feature to catch any orphaned +labels (the ones that are not followed by any character) and treat them as regular +ones instead of causing an error. It achieves it by making ":" the default value +for "def" parameter: + + struc ? def::& + . def + end struc + + orphan + regular: + assert orphan = regular + +Similarly to "macro" this special variant does not override unconditional labeled +instructions unless it is unconditional itself. + While "." provides an efficient method of accessing the label symbol, +sometimes it may be needed to process the actual text of the label. +A special parameter can be defined for this purpose and its name should be +inserted enclosed in parentheses before the name of labeled macroinstruction: + + struc (name) SYMBOL + . db `name,0 + end struc + + test SYMBOL + + +10. Symbolic variables and recognition context + +The "equ" is a built-in labeled instruction that defines symbol of expression +class with a symbolic value. Such value can contain any text (even an empty +one) and when it is used in an expression it is equivalent to inserting +the text of its value in place of its identifier, with an effect similar to +evaluation of a parameter of macroinstruction. + This can lead to a different results than when a standard variable defined +with "=" is used, as the following example demonstrates: + + numeric = 2 + 2 + symbolic equ 2 + 2 + x = numeric*3 ; x = 4*3 + y = symbolic*3 ; y = 2 + 2*3 + +While "x" is assigned the value of 12, the value of "y" is 8. This shows that +the use of such symbols can lead to unintended interactions and therefore +definitions of this type should be avoided unless really necessary. + The "equ" allows redefinitions, and it preserves the previous value of +symbol analogously to the "=:" command, so the earlier value can be brought +back with "restore" instruction. To replace the symbolic value (analogously +to how "=" overwrites the regular value) the "reequ" command should be used +instead of "equ". + A symbolic value, in addition to retaining the exact text it was defined +with, preserves the context in which the symbols contained in this text are +to be interpreted. Therefore it can effectively become a reliable link to +value of some other symbol, lasting even when it is used in a different +context (this includes change of the base namespace or a symbol referred by +a starting dot): + + first: + .x = 1 + link equ .x + .x = 2 + second: + .x = 3 + db link ; db 2 + + It should be noted that the same process is applied to the arguments of any +macroinstruction when they become preprocessed parameters. If during +the execution of a macroinstruction the context changes, the identifiers +within the text of parameters still refer to the same symbols as in the line +that called the instruction: + + x = 1 + namespace x + x = 2 + end namespace + macro prodx value + namespace x + db value*x + end namespace + end macro + prodx x ; db 1*2 + +Furthermore, parameters defined with "local" command use the same mechanism +to alter the context in which given name is interpreted, without altering +the text of the name. However, such modified context is not relevant +if the value of parameter is inserted in a middle or at the end of +a complex identifier, because it is the structure of an identifier that +dictates how its later parts are interpreted and only the context for an +initial part matters. For example, prepending a name of a parameter with +"#" character is going to cause the identifier to use current context instead +of context carried by the text of that parameter, because initial context +for the identifier is then the context associated with text "#". + Unlike the value of a symbolic variable, the body of a macroinstruction +by itself carries no context (although it may contain snippets of text that +came from replaced parameters and because of that have some context associated +with them). Also, if a macroinstruction becomes unrolled at the time when +another one is being defined (this can only happen when called macroinstruction +is unconditional), no context information is added to the arguments, to aid in +preservation of this context-lessness. + If the text following "equ" contains identifiers of known symbolic variables, +each of them is replaced with its contents and it is such processed text that +gets assigned to the newly defined symbol. + The "define" is a regular instruction that also creates a symbolic value, +but as opposed to "equ" it does not evaluate symbolic variables in the +assigned text. It should be followed by an identifier of symbol to be defined +and then by the text of the value. + The difference between "equ" and "define" is often not noticeable, because +when used in final expression the symbolic variables are nestedly evaluated +until only the usable constituents of expressions are left. A possible use of +"define" is to create a link to another symbolic variable, like the following +example demonstrates: + + a equ 0* + x equ -a + define y -a + a equ 1* + db x 2 ; db -0*2 + db y 2 ; db -1*2 + +The other uses of "define" will arise in the later sections, with the +introduction of other instructions that operate on symbolic values. + The "define", like "equ", preserves the previous value of symbol. The +"redefine" is a variant of this instruction that discards the earlier value, +analogously to "reequ". + Note that while symbolic variables belong to the expression class of symbols, +their state cannot be determined with operators like "defined", "definite", +or "used", because a logical expression is evaluated as if every symbolic +variable was replaced with the text of corresponding value. Therefore operator +followed by an identifer of symbolic variable is going to be applied to +the content of this variable, whatever it is. For example if a symbolic variable +is made which is a link to a regular symbol, then any operator like "defined" +followed by the identifier of said symbolic variable is going to determine +the status of a linked symbol, not a linking variable. + + +11. Repeating blocks of instructions + +The "repeat" instruction allows to assemble a block of instructions multiple +times, with the number of repetitions specified by the value of its argument. +The block of instructions should be ended with "end repeat" command. A synonym +"rept" can be used instead of "repeat". + + a = 2 + repeat a + 3 + a = a + 1 + end repeat + assert a = 7 + + The "while" instruction causes the block of instructions to be assembled +repeatedly as long as the condition specified by its argument is true. Its +argument should be a logical expression, like an argument for "if" or +"assert". The block should be closed with "end while" command. + + a = 7 + while a > 4 + a = a - 2 + end while + assert a = 3 + + The "%" is a special parameter which is preprocessed inside the repeated +block of instructions and is replaced with a decimal number being the number +of current repetition (starting with 1). It works in a similar way to a +parameter of macroinstruction, so it is replaced with its value before the +actual command is processed and so it can be used to create symbol +identifiers containing the number as a part of name: + + repeat 16 + f#% = 1 shl % + end repeat + +The above example defines symbols "f1" to "f16" with values being the +consecutive powers of two. + The "repeat" instruction can have additional arguments, separated with +commas, each containing a name of supplementary parameters specific to this +block. Each of the names can be followed by ":" character and the expression +specifying the base value from which the parameter is going to start counting +the repetitions. This allows to easily change the previous sample to define +the range of symbols from "f0" to "f15": + + repeat 16, i:0 + f#i = 1 shl i + end repeat + + The "%%" is another special parameter that has a value equal to the total +number of repetitions planned. This parameter is undefined inside the "while" +block. The following example uses it to create the sequence of bytes with +values descending from 255 to 0: + + repeat 256 + db %%-% + end repeat + + The "break" instruction allows to stop the repeating prematurely. When it +is encountered, it causes the rest of repeated block to be skipped and no +further repetitions to be executed. It can be used to stop the repeating if +a certain condition is met: + + s = x/2 + repeat 100 + if x/s = s + break + end if + s = (s+x/s)/2 + end repeat + +The above sample tries to find the square root of the value of symbol "x", +which is assumed defined elsewhere. It can easily be rewritten to perform the +same task with "while" instead of "repeat": + + s = x/2 + while x/s <> s + s = (s+x/s)/2 + if % = 100 + break + end if + end while + + The "iterate" instruction (with a synonym "irp") repeats the block of +instructions while iterating through the list of values separated with commas. +The first argument to "iterate" should be the a name of parameter, folowed by +the comma and then a list of values. During each iteration the parameter +receives one of the values from the list. + + iterate value, 1,2,3 + db value + end iterate + +Like it is in the case of an argument to macroinstruction, the value of parameter +that contains commas needs to be enclosed with "<" and ">" characters. It is +also possible to enclose the first argument to "iterate" with "<" and ">", in +order to define multiple parameters. The list of values is then divided +into section containing as many values as there are parameters, and each +iteration operates on one such section, assigning to each parameter a +corresponding value: + + iterate , a,1, b,2, c,3 + name = value + end iterate + +The name of a parameter can also, like in the case of macroinstructions, be +followed by "*" to require that the parameter has a value that is not empty, +or ":" and a default value. If an "iterate" statement ends with a comma not +followed by anything else, it is not interpreted as an additional empty value, +to put a blank value at the end of list an empty enclosing "<>" needs to be used. + The "break" instruction plus both the "%" and "%%" parameters can be used +inside the "iterate" block with the same effects as in case of "repeat". + The "indx" is an instruction that can be only be used inside an iterated +block and it changes the values of all the iterated parameters to the ones +corresponding to iteration with number specified by the argument to "indx" (but +when the next iteration is started, the values of parameters are again assigned +the normal way). This allows to process the iterated values in a different +order. In the following example the values are processed from the last to the +first: + + iterate value, 1,2,3 + indx 1+%%-% + db value + end iterate + +With "indx" it is even possible to move the view of iterated values many times +during the single repetition. In the following example the entire processing +is done during the first repetition of iterated block and then the "break" +instruction is used to prevent further iterations: + + iterate str, 'alpha','beta','gamma' + repeat %% + dw offset#% + end repeat + repeat %% + indx % + offset#% db str + end repeat + break + end iterate + + The parameters defined by "iterate" do not attach the context to iterated +values, but neither do they remove the original context if such is already +attached to the text of arguments. So if the values given to "iterate" were +themselves created from another parameter that preserved the original context +for the symbol identifiers (like the parameter of macroinstruction), then this +context is preserved, but otherwise "iterate" defines just a plain text +substitution. + The parameters defined by instructions like "iterate" or "repeat" are +processed everywhere in the text of associated block, but with some limitations +if the block is defined partly by the text of macroinstruction and partly in +other places. In that case the parameters are only accessible in the parts of +the block that are defined in the same place as the initial command. + Every time a parameter is defined, its name can have the "?" character +attached to it to indicate that this parameter is case-insensitive. However +when parameters are recognized inside the preprocessed line, it does not matter +whether they are followed by "?" there. The only modifier that is recognized +by preprocessor when it replaces the parameter with its value is the "`" +character. + The repeating instructions together with "if" belong to a group called +control directives. They are the instructions that control the flow of +assembly. Each of them defines its own block of subordinate instructions, +closed with corresponding "end" command, and if these blocks are nested within +each other, it always must be a proper nesting - the inner block must always +be closed before the outer one. All control directives are therefore the +unconditional instructions - they are recognized even when they are inside +an otherwise skipped block. + The "postpone" is another control directive, which causes a block of +instructions to be assembled later, when all of the following source text +has already been processed. + + dw final_count + postpone + final_count = counter + end postpone + counter = 0 + +The above sample postpones the definition of "final_count" symbol until the +entire source has been processed, so that it can access the final value of +"counter" variable. + The assembly of the source text that follows "postpone" includes the assembly +of any additional blocks declared with "postpone", therefore if there are +multiple such blocks, they are assembled in the reverse order. The one that +was declared last is assembled first when the end of the source text is reached. + When the "postpone" directive is provided with an argument consisting of +a single "?" character, it tells the assembler that the block contains +operations which should not affect any of the values defined in the main +source and thus the assembler may refrain from evaluating them until all +other values have been successfully resolved. Such blocks are processed +even later than the ones declared by "postpone" with no arguments. They +may be used to perform some finalizing tasks, like the computation of a +checksum of the assembled code. + The "irpv" is another repeating instruction and an iterator. It has just two +arguments, first being a name of parameter and second an identifier of +a variable. It iterates through all the stacked values of symbolic +variable, starting from the oldest one (this applies only to the values +defined earlier in the source). + + var equ 1 + var equ 2 + var equ 3 + var reequ 4 + irpv param, var + db param + end irpv + +In the above example there are three iterations, with values 1, 2, and 4. + "irpv" can effectively convert a value of symbolic variable into a parameter, +and this can be useful all by itself, because the symbolic variable is only +evaluated in the expressions inside the arguments of instructions (labeled or +not), while the parameters are preprocessed in the entire line before any +processing of command is started. This allows, for example, to redefine a +regular value that is linked by symbolic variable: + + x = 1 + var equ x + irpv symbol, var + indx %% + symbol = 2 + break + end irpv + assert x = 2 + +The combination of "indx" and "break" was added to the above sample to limit +the iteration to the latest value of symbolic variable. In the next section +a better solution to the same problem will be presented. + When a variable passed to "irpv" has a value that is not symbolic, the +parameter is given a text that produces the same value upon computation. When +the value is a positive number, the parameter is replaced with its decimal +representation (similarly how the "%" parameter is processed), otherwise +the parameter is replaced with an identifier of a proxy symbol holding the +value from stack. + The "outscope" directive is available while any macroinstruction is processed, +and it modifies the command that follows in the same line. If the command causes +any parameters to be defined, they are created not in the context of currently +processed macroinstruction but in the context of the source text that called it. + + macro irpv?! statement& + display 'IRPV wrapper' + esc outscope irpv statement + end macro + +This allows not only to safely wrap some control directives in macroinstructions, +but also to create additional customized language constructions that define +parameters for a block of text. Because "outscope" needs to be present in the +text of a specific macroinstruction that requires it, it is recommended to use +it in conjunction with "esc" as in the example above, this ensures that it is +handled the same way even when the entire definition is put inside another +macroinstruction. + + +12. Matching parameters + +The "match" is a control directive which causes its block of instructions to +be assembled only when the text specified by its second argument matches the +pattern given by the first one. A text is separated from a pattern with a comma +character, and it includes everything that follows this separator up to the end +of line. + Every special character (except for the "," and "=", which have a specific +meaning in the pattern) is matched literally - it must be paired with identical +token in the text. In the following example the content of the first block +is assembled, while the content of the second one is not. + + match +,+ + assert 1 ; positive match + end match + + match +,- + assert 0 ; negative match + end match + + The quoted strings are also matched literally, but name tokens in the pattern +are treated differently. Every name acts as a wildcard and can match any +sequence of tokens which is not empty. If the match is successful, the +parameters with such names are created, and each is assigned a value equal +to the text the wildcard was matched with. + + match a[b], 100h[3] + dw a+b ; dw 100h+3 + end match + + A parameter name in pattern can have an extra "?" character attached to it +to indicate that it is a case-insensitive name. + The "=" character causes the token that follows it to be matched literally. +It allows to perform matching of name tokens, and also of special characters +that would otherwise have a different meaning, like "," or "=", or "?" following +a name. + + match =a==a, a=8 + db a ; db 8 + end match + + If "=" is followed by name token with "?" character attached to it, this +element is matched literally but in a case-insensitive way: + + match =a?==a, A=8 + db a ; db 8 + end match + + When there are many wildcards in the pattern, each consecutive one is matched +with as few tokens as possible and the last one takes what is left. If the +wildcards follow each other without any literally matched elements between +them, the first one is matched with just a single token, and the second one with +the remaining text: + + match car cdr, 1+2+3 + db car ; db 1 + db cdr ; db +2+3 + end match + +In the above sample the matched text must contain at least two tokens, because +each wildcard needs at least one token to be not empty. In the next example +there are additional constraints, but the same general rules applies and the +first wildcard consumes as little as possible: + + match first:rest, 1+2:3+4:5+6 + db `first ; db '1+2' + db 13,10 + db `rest ; db '3+4:5+6' + end match + + While any whitespace next to a wildcard is ignored, the presence or +absence of whitespace between literally matched elements is meaningful. +If such elements have no whitespace between them, their counterparts must +contain no whitespace between them either. But if there is a whitespace +between elements in pattern, it places no constraints on the use of +whitespace in the corresponding text - it can be present of not. + + match ++,++ + assert 1 ; positive match + end match + + match ++,+ + + assert 0 ; negative match + end match + + match + +,++ + assert 1 ; positive match + end match + + match + +,+ + + assert 1 ; positive match + end match + +The presence of whitespace in the text becomes required when the pattern +contains the "=" character followed by a whitespace: + + match += +, ++ + assert 0 ; negative match + end match + + match += +, + + + assert 1 ; positive match + end match + + The "match" command is analogous to "if" in that it allows to use the +"else" or "else match" to create a selection of blocks from which only one is +executed: + + macro let param + match dest+==src, param + dest = dest + src + else match dest-==src, param + dest = dest + src + else match dest++, param + dest = dest + 1 + else match dest--, param + dest = dest + 1 + else match dest==src, param + dest = src + else + assert 0 + end match + end macro + + let x=3 ; x = 3 + let x+=7 ; x = x + 7 + let x++ ; x = x + 1 + +It is even possible to mix "if" and "match" conditions in a sequence of +"else" blocks. The entire construction must be closed with "end" command +corresponding to whichever of the two was used last: + + macro record text + match any, text + recorded equ `text + else if RECORD_EMPTY + recorded equ '' + end if + end macro + + The "match" is able to recognize symbolic variables and before the matching +is started, their identifiers in the text of the second argument are replaced +with corresponding values (just like they are replaced in the text that follows +the "equ" command): + + var equ 2+3 + + match a+b, var + db a xor b + end match + +This means that the "match" can be used instead of "irpv" to convert the +latest value of a symbolic variable to parameter. The sample from the previous +section, where "irpv" was used with "break" to perform just one iteration on +the last value, can be rewritten to use "match" instead: + + x = 1 + var equ x + match symbol, var + symbol = 2 + end match + assert x = 2 + +The difference between them is that "irpv" would execute its block even for +an empty value, while in the case of "match" the "else" block would need to be +added to handle an empty text. + When the evaluation of symbolic variables in the matched text is undesirable, +a symbol created with "define" can be used as a proxy to preserve the text, +because the replacement is not recursive: + + macro drop value + local temporary + define temporary value + match =A, temporary + db A + restore A + else + db value + end match + end macro + + A equ 1 + A equ 2 + + drop A + drop A + +A concern could arise that "define" may modify the meaning of text by +equipping it with a local context. But when the value for "define" comes from +a parameter of macroinstruction (as in the above sample), it already carries +its original context and "define" does not alter it. + The "rawmatch" directive (with a synonym "rmatch") is very similar to "match", +but it operates on the raw text of the second argument. Not only it does not +evaluate the symbolic variables, but it also strips the text of any additional +context it could have carried. + + struc has instruction + rawmatch text, instruction + namespace . + text + end namespace + end rawmatch + end struc + + define x + x has a = 3 + assert x.a = 3 + +In the above sample the identifier of "a" would be interpreted in the context +effective for the line calling the "has" macroinstruction if it was not +converted back into the raw text by "rmatch". + + +13. Output areas + +The "org" instruction starts a new area of output. The content of such +area is written into the destination file next to the previous data, but the +addresses in the new area are based on the value specified in the argument to +"org". The area is closed automatically when the next one is started or when +the source ends. + + org 100h + start: ; start = 100h + + The "$" is a built-in symbol of expression class which is always equal to +the value of current address. Therefore definition of a constant with the value +specified by "$" symbol is equivalent to defining a label at the same point: + + org 100h + start = $ ; start = 100h + +The "$$" symbol is always equal to the base of current addressing space, so +in the area started with "org" it has the same value as the base address from +the argument of "org". The difference between "$" and "$$" is thus the current +position relative to the start of the area: + + org 2000h + db 'Hello!' + size = $ - $$ ; size = 6 + +The "$@" symbol evaluates to the base address of current block of uninitialized +data. When there was no such data defined just before the current position, +this value is equal to "$", otherwise it is equal to "$" minus the length of +said data inside the current addressing space. Note that reserved data +no longer counts as such when it is followed by an initialized one. + The "section" instruction is similar to "org", but it additionally trims +all the reserved data that precedes it analogously to how the uninitialized +data is not written into output when it is at the end of file. The "section" +can therefore be followed by initialized data definitions without causing +the previously reserved data to be initialized with zeros and written into +output. In this sample only the first of the three reserved buffers is +actually converted into zeroed data and written into output, because it is +followed by some initialized data. The second one is trimmed because of the +"section", and the third one is cut off since it lies at the end of file: + + data1 dw 1 + buffer1 rb 10h ; zeroed and present in the output + + org 400h + data dw 2 + buffer2 rb 20h ; not in the output + + section 1000h + data3 dw 3 + buffer3 rb 30h ; not in the output + + The "$%" is a built-in symbol equal to the offset within the output file at +which the initialized data would be generated if it was defined at this point. +The "$%%" symbol is the current offset within the output file. These two +values differ only when they are used after some data has been reserved - +the "$%" is then larger than "$%%" by the length of unitialized data which +would be generated into output if it was to be followed by some initialized +one. + + db 'Hello!' + rb 4 + position = $%% ; position = 6 + next = $% ; next = 10 + +The values in the comments of the above sample assume that the source contains +no other instructions generating output. + The "virtual" creates a special output area which is not written into the main +output file. This kind of area must reside between the "virtual" and "end virtual" +commands, and after it is closed, the output generator comes back to the area it +was previously operating on, with position and address the same as there were just +before opening the "virtual" block. This allows also to nest the "virtual" blocks +within each other. + When "virtual" has no argument, the base address of this area is the same +as current address in the outer area. An argument to "virtual" can have a form +of "at" keyword followed by an expression defining the base address for the +enclosed area: + + int dw 1234h + virtual at int + low db ? + high db ? + end virtual + + Instead of or in addition to such argument, "virtual" can also be followed by +an "as" keyword and a string defining an extension of additional file where +the initialized content of the area is going to be stored at the end of +a successful assembly. + The "load" instruction defines the value of a variable by loading the string +of bytes from the data generated in an output area. It should be followed by +an identifier of symbol to define, then optionally the ":" character and a +number of bytes to load, then the "from" keyword and an address of the data +to load. This address can be specified in two modes. If it is simply a numeric +expression, it is an address within the current area. In that case the loaded +bytes must have already been generated, so it is only possible to load from the +space between "$$" and "$" addresses. + + virtual at 100h + db 'abc' + load b:byte from 101h ; b = 'b' + end virtual + +When the number of bytes is not specified, the length of loaded string is +determined by the size associated with address. + Another variant of "load" needs a special kind of label, which is created +with "::" instead of ":". Such label has a value that cannot be used directly, +but it can be used with "load" instruction to access the data of the area in +which this label has been defined. The address for "load" has then to be +specified as the area label followed by ":" and then the address within that +area: + + virtual at 0 + hex_digits:: + db '0123456789ABCDEF' + end virtual + load a:byte from hex_digits:10 ; a = 'A' + +This variant of "load" can access the data which is generated later, even +within the current area: + + area:: + db 'abc' + load sub:3 from area:$-2 ; sub = 'bcd' + db 'def' + + The "store" instruction can modify already generated data in the output +area. It should be followed by a value (automatically converted to string +of bytes), then optionally the ":" character followed by a number of bytes +to write (when this setting is not present, the length of string is determined +by the size associated with address), then the "at" keyword and the address of +data to replace, in one of the same two modes as allowed by "load". However the +"store" is not allowed to modify the data that has not been generated yet, and +any area that has been touched by "store" becomes a variable area, forbidding +also the "load" to read a data from such area in advance. + The following example uses the combination of "load" and "store" to encrypt +the entire contents of the current area with a simple "xor" operation: + + db "Text" + key = 7Bh + repeat $-$$ + load a : byte from $$+%-1 + store a xor key : byte at $$+%-1 + end repeat + + If the final data of an area that has been modified by "store" needs to be +read earlier in the source, it can be achieved by copying this data into +a different area that would not be constrained in such way. This is analogous +to defining a constant with a final value of some variable: + + load char : byte from const:0 + + virtual + var:: + db 'abc' + .length = $ + end virtual + + store 'A' : byte at var:0 + + virtual + const:: + repeat var.length + load a : byte from var:%-1 + db a + end repeat + end virtual + + The area label can be forward-referenced by "load", but it can never be +forward-referenced by "store", even if it refers to the current output area. + The "virtual" instruction can have an existing area label as the only +argument. This variant allows to extend a previously defined and closed +block with additional data. The area label must refer to a block that was +created earlier in the source with "virtual". Any definition of data within +an extending block is going to have the same effect as if that definition was +present in the original "virtual" block. + + virtual at 0 as 'log' + Log:: + end virtual + + virtual Log + db 'Hello!',13,10 + end virtual + + If an area label is used in an expression, it forms a variable term of a +linear polynomial. The metadata of such term is a string "::", allowing +to determine that area label was used to form the value, since metadata +of terms made with "element" is always numeric. + There is an additional variant of "load" and "store" directives that allows +to read and modify already generated data in the output file given simply +an offset within that output. This variant is recognized when the "at" or +"from" keyword is followed by ":" character and then the value of an offset. + + checksum = 0 + repeat $% + load a : byte from : %-1 + checksum = checksum + a + end repeat + + The "restartout" instruction abandons all the output generated up to this +point and starts anew with an empty one. An optional argument may specify +the base address of newly started output area. When "restartout" has no +argument, the current address is preserved by using it as the base for the +new area. + The "org", "section" and "restartout" instructions cannot be used inside +a "virtual" block, they can only separate areas that go into the output file. + + +14. Source and output control + +The "include" instruction reads the source text from another file and +processes it before proceeding further in the current source. Its argument +should be a string defining the path to a file (the format of the path may +depend on the operating system). If there is a "!" between the instruction +and the argument, the other file is read and processed unconditionally, +even when it is inside a skipped block (the unconditional instructions from +the other file may then get recognized). + + include 'macro.inc' + +An additional argument may be optionally added (separated from the path +by comma), and it is interpreted as a command to be executed after the file +has been read and inserted into the source stream, just before processing +the first line. + The "eval" instruction takes a sequence of bytes defined by its arguments, +treats it as a source text and assembles it. The arguments are either strings +or the numeric values of single bytes, separated with commas. In the next +example "eval" is used to generate definitions of symbols named as a +consecutive letters of the alphabet: + + repeat 26 + eval 'A'+%-1,'=',`% + end repeat + + assert B = 2 + + The "display" instruction causes a sequence of bytes to be written into +standard output, next to the messages generated by the assembler. It should +be followed by strings or numeric values of single bytes, separated +with commas. The following example uses "repeat 1" to define a parameter +with a decimal representation of computed number, and then displays it as +a string: + + macro show description,value + repeat 1, d:value + display description,`d,13,10 + end repeat + end macro + + show '2^64=',1 shl 64 + + The "err" instruction signalizes an error in the assembly process, with +a custom message specified by its argument. It allows the same kind of +arguments as the "display" directive. + + if $>10000h + err 'segment too large' + end if + + The "format" directive allows to set up additional options concerning +the main output. Currently the only available choice is "format binary" followed +by the "as" keyword and a string defining an extension for the output file. +Unless a name of the output file is specified from the command line, it is +constructed from the path to the main source file by dropping the extension and +attaching a new extension if such is defined. + + format binary as 'com' + + The "format" directive, analogously to "end", uses an identifier that follows +it to find an instruction in the child namespace of case-insensitive symbol +named "format". The only built-in instruction that resides in that namespace +is the "binary", but additional ones may be defined in form of macroinstructions. + The built-in symbol "__time__" (with legacy synonym "%t") has the constant value +of the timestamp marking the point in time when the assembly was started. + The "__file__" is a built-in symbol whose value is a string containing +the name of currently processed source file. The accompanying "__line__" symbol +provides the number of currently processed line in that file. When these symbols +are accessed within a macroinstruction, they keep the same value they had for the +calling line. If there are several levels of macroinstructions calling each +other, these symbols have the same value everywhere, corresponding to the line +that called the outermost macroinstruction. + The "__source__" is another built-in symbol, with value being a string containing +the name of the main source file. + The "retaincomments" directive switches the assembler to treat a semicolon as +a regular token and therefore not strip comments from lines before processing. +This allows to use semicolons in places like MATCH pattern. + + retaincomments + macro ? line& + match instruction ; comment , line + virtual + comment + end virtual + instruction + else + line + end match + end macro + + var dd ? ; bvar db ? + + The "isolatelines" directive prevents the assembler from subsequently combining +lines read from the source text when the line break is preceded by a backslash. + The "removecomments" directive brings back the default behavior of semicolons +and the "combinelines" directive allows lines from the source text to be combined +as usual. + + +15. CALM instructions + +The "calminstruction" directive allows to define new instructions in form of +compiled sequences of specialized commands. As opposed to regular macroinstructions, +which operate on a straightforward principle of textual substitution, CALM +(Compiled Assembly-Like Macro) instructions are able to perform many operations +without passing any text through the standard preprocessing and assembly cycle. +This allows for a finer control, better error handling and faster execution. + All references to symbols in the text defining a CALM instruction are fixed +at the time of definition. As a consequence, any symbols local to the CALM instruction +are shared among all its executed instances (for example consecutive instances may see +the values of local symbols left by the previous ones). To aid in reusing these +references, commands in CALM are generally operating on variables, routinely rewriting +the symbols with new values. + A "calminstruction" statement follows the same rules as "macro" declaration, +including options like "!" modifier to define unconditional instruction, "*" to mark +a required argument, ":" to give it a default value and "&" to indicate that +the final argument consumes all the remaining text in line. + However, because CALM instruction operates outside of the standard preprocessing +and assembly cycle, its arguments do not become preprocessed parameters. Instead +they are local symbolic variables, given new values every time the instruction is called. + If the name of defined instruction is preceded by another name enclosed in round +brackets, the statement defines a labeled instruction and enclosed name is the +argument that is going to receive the text of the label. + In the definition of CALM instruction, only the statements of its specialized +language are identified. The initial symbol of every line must be a simple name without +modifiers and it is only recognized as valid instruction if a case-insensitive symbol with +such name is found in the namespace of CALM commands (which, for the purpose +of customization, is accessible as the namespace anchored at the case-insensitive +"calminstruction" symbol). When no such named instruction is found, the initial name may +become a label if it is followed by ":", it is then treated as a case-sensitive symbol +belonging to a specialized class. Symbols of this class are only recognized when used +as arguments to CALM jump commands (described further down). + An "end calminstruction" statement needs to be used to close the definition and +bring back normal mode of assembly. It is not a regular "end" command, +but an identically named instruction in the CALM namespace, which only accepts +"calminstruction" as its argument. + The "assemble" is a command that takes a single argument, which should be +an identifier of a symbolic variable. The text of this variable is passed directly +to assembly, without any preprocessing (if the text came from an argument to +the instruction, it already went through preprocessing when that line was prepared). + + calminstruction please? cmd& + assemble cmd + end calminstruction + + please display 'Hi!' + + The "match" command is in many ways similar to the standard directive with the same +name. Its first argument should be a pattern following the same rules as those for +"match" directive. The second argument must be an identifier of a symbolic variable, +whose text is going to be matched against the pattern. The name tokens in pattern +(except for the ones made literal with "=" symbol) are treated as names of variables +where the matched portions of text should be put if the match is successful. The same +variable that is a source of text can also be used in pattern as a variable +to write to. When there is no match, all variables remain unaffected. + + calminstruction please? cmd& + match (cmd), cmd + assemble cmd + end calminstruction + + please(display 'Hi!') + + Whether the match was successful can also be tested with a conditional jump "jyes" +or "jno" following the "match" command. A "jyes" jump is taken only when the match +succeeded. + + calminstruction please? cmd& + match =do? =not? cmd, cmd + jyes done + assemble cmd + done: + end calminstruction + + please do not display 'Bye!' + +To further control the flow of processing, the "jump" command allows to jump +unconditionally, and with "exit" it is possible to terminate processing of +CALM instruction at any moment (this command takes no arguments). + While the symbols used for the arguments of the instruction are implicitly local, +other identifiers may become fixed references to global symbols if they are seen +as accessible at the time of definition (because in CALM instruction all such references +are treated as uses, not as definitions). A command like "match" may then write to +a global variable. + + define comment + + calminstruction please? cmd& + match cmd //comment, cmd + assemble cmd + end calminstruction + + please display 'Hi!' // 3 + db comment ; db 3 + +To enforce treatment of a symbol as local, a "local" command should be used, followed +by one or more names separated with commas. + + calminstruction please? cmd& + local comment + match cmd //comment, cmd + assemble cmd + end calminstruction + +A symbol made local is initally assigned a defined but unusable value. + If a pattern in CALM instruction has a "?" character immediately following the name +of a wildcard, it does not affect how the symbol is identified (whether the used symbol +is case-insensitive depends on what is present in the local scope at the time +the instruction is defined). Instead, modifying the name of a wildcard with "?" allows it +to be matched with an empty text. + Since the source text for "match" is in this variant given by just a single identifier, +this syntax allows to have more optional arguments. A third argument to "match" may +contain a pair of bracket characters. Any wildcard element must then be matched with +a text that has this kind of brackets properly balanced. + + calminstruction please? cmd& + local first, second + match first + second, cmd, () + jyes split + assemble cmd + exit + split: + assemble first + assemble second + end calminstruction + + please display 'H',('g'+2) + display '!' + +The brackets selected by the third argument must not be used anywhere in the pattern. + The "arrange" command is like an inverse of "match", it can build up a text +containing the values of one or more variables. The first argument defines a variable +where the constructed text is going to be stored, while the second argument is +a pattern formed in the same way as for "match" (except that it does not need +to precede a comma with "=" to have it included in the argument). +All non-name tokens other than "=" and tokens preceded with "=" are copied literally +into the constructed text and they do not carry any recognition context with them. +The name tokens that are not made literal with "=" are treates as names of variables +whose symbolic values are put in their place into the constructed text. + + calminstruction addr? arg + local base, index + match base[index], arg + local cmd + arrange cmd, =dd base + index + assemble cmd + end calminstruction + + addr 8[5] ; dd 8 + 5 + +With suitably selected patterns, "arrange" can be used to copy symbolic value +from one variable to another or to assign it a fixed value (even an empty one). + If a variable used in pattern turns out to have a numeric value instead of symbolic, +as long as it is a non-negative number with no additional terms, it is converted +into a decimal token stored into the constructed symbolic value (an operation +that outside of CALM instructions would require use of a "repeat 1" trick): + + digit = 4 - 1 + + calminstruction demo + local cmd + arrange cmd, =display digit#0h + assemble cmd + end calminstruction + + demo ; display 3#0h + + The "compute" command allows to evaluate expressions and assign numeric results to +variables. The first argument to "compute" defines a target where the result should +be stored, while the second argument can be any numeric expression, which is +becomes pre-compiled at the time of definition. When the expression is evaluated +and any of the symbols it refers to turns out to have symbolic value, this text +is parsed as a new sub-expression, and its calculated value is then used in the +computation of the main expression. + A "compute" therefore can be used not only to evaluate a pre-defined expression, +but also to parse and compute an expression from a text of a symbolic variable +(like one coming from an argument to the instruction), or a combination of both: + + a = 0 + + calminstruction low expr* + compute a, expr and 0FFh + end calminstruction + + low 200 + 73 ; a = 11h + +Because symbolic variable is evaluated as a sub-expression, its use here has no +side-effects that would be caused by a straightforward text substitution. + The "check" command is analogous to "if". It evaluates a condition defined by +the logical expression that follows it and accordingly sets up the result flag which +may be tested with "jyes" or "jno" command. The values of symbolic variables +are treated as numeric sub-expressions (they may not contain any operators specific +to logical expression). + + calminstruction u8range? value + check value >= 0 & value < 256 + jyes ok + local cmd + arrange cmd, =err 'value out of range' + assemble cmd + ok: + end calminstruction + + u8range -1 + + The "publish" command allows to assign a value to a symbol identified by the text +held in a variable. This allows to define a symbol with a name constructed with +a command like "arrange", or a name that was passed in an argument to an instruction. +The first argument needs to be the symbolic variable containing the identifier +of the symbol to define, the second argument should be the variable holding +the value to assign (either symbolic or numeric). The first argument may be +followed by ":" character to indicate that the symbol should be made constant, +or it can be preceded by ":" to make the value stacked on top of the previous one +(so that the previous one can be brought back with "restore" directive). + + calminstruction constdefine? var + local val + arrange val, + match var= val, var + publish var:, val + end calminstruction + + constdefine plus? + + +The above instruction allows to define a symbolic constant, something that is not +possible with standard directives of the assembler. + The purpose of "transform" command is to replace identifiers of symbolic variables +(or constants) with their values in a given text, which is the same operation as done +by "equ" directive when it prepares the value to assign. The argument to "transform" +should be a symbolic variable whose value is going to be processed this way and then +replaced by the transformed text. + + calminstruction (var) constequ? val + transform val + publish var:, val + end calminstruction + +A "transform" command updates the result flag to indicate whether any replacement +has been done. + + calminstruction prepasm? cmd& + loop: + transform cmd + jyes loop ; warning: may hang on cyclic references + assemble cmd + end calminstruction + +The result flag is modified only by some of the commands, like "check", "match" +or "transform". Other commands keep it unchanged. + Optionally, "transform" can have two arguments, with second one specifying +a namespace. Identifiers in the text given by the first argument are then interpreted +as symbols in this namespace regardless of their original context. + The "stringify" is a command that converts text of a variable into a string +and writes it into the same variable (specified by the only argument). This operation +is similar to one performed by "`" operator in preprocessing. + + calminstruction (var) strcalc? val + compute val, val ; compute expression + arrange val, val ; convert result to a decimal token + stringify val ; convert decimal token to string + publish var, val + end calminstruction + + p strcalc 1 shl 1000 + display p + + While most commands available to CALM instructions replace the values of variables +when writing to them, the "take" is a command that allows to work with stacks of values. +It removes the topmost value of the source symbol (specified by the second argument) +and gives it to the destination symbol (the first argument), placing it on top of any +existing values. The destination argument may be empty, in such case the value is +removed completely and the operation is analogous to "restore" directive. This command +updates the result flag to indicate whether there was any value to remove. +If the destination symbol is the same as source, the result flag can be used to check +whether there is an available value without affecting it. + + calminstruction reverse? cmd& + local tmp, stack + collect: + match tmp=,cmd, cmd + take stack, tmp + jyes collect + execute: + assemble cmd + take cmd, stack + jyes execute + end calminstruction + + reverse display '!', display 'i', display 'H' + +A symbol accessed as either destination or source by a "take" command can never be +forward-referenced even if it could otherwise. + Defining macroinstructions in the namespace of case-insensitive "calminstruction" +allows to add customized commands to the language of CALM instructions. However, +they must be defined as case-insensitive to be recognized as such. + + macro calminstruction?.asmarranged? variable*, pattern& + arrange variable, pattern + assemble variable + end macro + + calminstruction writeln? text& + asmarranged text, =display text,10 + end calminstruction + + writeln 'Next!' + +Such additional commands may even be defined as CALM instructions themselves: + + calminstruction calminstruction?.initsym? variable*,value& + publish variable, value + end calminstruction + + calminstruction show? text& + local command + initsym command, display text + stringify text + assemble command + end calminstruction + + show :) + +The command "initsym" in this example is used to assign text to the local +symbolic variable at the time when "show" instruction is defined. +Similarly to "local" (and unlike "stringify" and "assemble") it does not produce +any actual code that would be executed when the "show" instruction is called. +The arguments to "initsym" retain their original context, therefore symbols +in the text assigned to the "command" variable are interpreted as in the local +namespace of the "show" instruction. This allows the "display" command to access +the "text" even though it is local to the CALM instruction and therefore normally +visible only in the scope of the definition of "show". This is similar to the use +of "define" to form symbolic links. diff --git a/x86_64_sse2_x87/fasm/examples/8051/8051.inc b/x86_64_sse2_x87/fasm/examples/8051/8051.inc new file mode 100644 index 0000000..89e33e5 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/8051/8051.inc @@ -0,0 +1,861 @@ + +; This is a very basic implementation of 8051 instruction set, which treats +; all types of addresses as plain numeric values and therefore is not able to +; detect whether symbol has been used in context it was not intended for. + +element R + +repeat 8 i:0 + element R#i? : R + i +end repeat + +element @ + +element @R0? : @ +element @R1? : @ + 1 + +macro AJMP? addr + local value + value = +addr + if value and not 7FFh = ($+2) and not 7FFh + db 01h + value shr 3 and 11100000b,value and 0FFh + else + err "address out of range" + end if +end macro + +macro ACALL? addr + local value + value = +addr + if value and not 7FFh = ($+2) and not 7FFh + db 11h + value shr 3 and 11100000b,value and 0FFh + else + err "address out of range" + end if +end macro + +macro LCALL? addr + local value + value = +addr + db 12h,value shr 8,value and 0FFh +end macro + +macro LJMP? addr + local value + value = +addr + db 02h,value shr 8,value and 0FFh +end macro + +macro SJMP? addr + local offset + offset = -($+2)+addr + if offset>=-80h & offset<80h + db 80h,offset + else + err "relative jump out of range" + end if +end macro + +macro CALL? addr + local value + value = +addr + if value and not 7FFh = ($+2) and not 7FFh + db 11h + value shr 3 and 11100000b + else + db 12h,value shr 8 + end if + db value and 0FFh +end macro + +macro JMP? addr + local value,offset + match =@A? + =DPTR?, addr + db 73h + else + value = +addr + offset = value-($+2) + if offset>=-80h & offset<80h + db 80h,offset + else + if value and not 7FFh = ($+2) and not 7FFh + db 01h + value shr 3 and 11100000b + else + db 02h,value shr 8 + end if + db value and 0FFh + end if + end match +end macro + +macro CJNE? operand1,operand2,addr + local value,offset + offset = -($+3)+addr + if offset>=-80h & offset<80h + match =A?, operand1 + match #data, operand2 + value = +data + db 0B4h,value + else + value = +operand2 + db 0B5h,value + end match + else match #data,operand2 + value = +operand1 + if value eq value element 1 + if value metadata 1 relativeto @ + db 0B6h + value metadata 1 - @ + else if value metadata 1 relativeto R + db 0B8h + value metadata 1 - R + else + err "invalid operand" + end if + db +data + else + err "invalid operand" + end if + else + err 'invalid operand' + end match + db offset + else + err "relative jump out of range" + end if +end macro + +macro DJNZ? operand,addr + local value,offset + value = +operand + if value relativeto 0 + offset = -($+3)+addr + if offset>=-80h & offset<80h + db 0D5h,value,offset + else + err "relative jump out of range" + end if + else if value eq value element 1 & value metadata 1 relativeto R + offset = -($+2)+addr + if offset>=-80h & offset<80h + db 0D8h + value metadata 1 - R,offset + else + err "relative jump out of range" + end if + else + err "invalid operand" + end if +end macro + +macro JBC? operand,addr + local offset + offset = -($+3)+addr + if offset>=-80h & offset<80h + db 10h,operand,offset + else + err "relative jump out of range" + end if +end macro + +macro JB? operand,addr + local offset + offset = -($+3)+addr + if offset>=-80h & offset<80h + db 20h,operand,offset + else + err "relative jump out of range" + end if +end macro + +macro JNB? operand,addr + local offset + offset = -($+3)+addr + if offset>=-80h & offset<80h + db 30h,operand,offset + else + err "relative jump out of range" + end if +end macro + +macro JC? addr + local offset + offset = -($+2)+addr + if offset>=-80h & offset<80h + db 40h,offset + else + err "relative jump out of range" + end if +end macro + +macro JNC? addr + local offset + offset = -($+2)+addr + if offset>=-80h & offset<80h + db 50h,offset + else + err "relative jump out of range" + end if +end macro + +macro JZ? addr + local offset + offset = -($+2)+addr + if offset>=-80h & offset<80h + db 60h,offset + else + err "relative jump out of range" + end if +end macro + +macro JNZ? addr + local offset + offset = -($+2)+addr + if offset>=-80h & offset<80h + db 70h,offset + else + err "relative jump out of range" + end if +end macro + +macro ADD? accu,operand + local value + match =A?, accu + match #data, operand + value = +data + db 24h,value + else + value = +operand + if value relativeto 0 + db 25h,value + else if value eq value element 1 + if value metadata 1 relativeto @ + db 26h + value metadata 1 - @ + else if value metadata 1 relativeto R + db 28h + value metadata 1 - R + else + err "invalid operand" + end if + else + err "invalid operand" + end if + end match + else + err 'invalid operand' + end match +end macro + +macro ADDC? accu,operand + local value + match =A?, accu + match #data, operand + value = +data + db 34h,value + else + value = +operand + if value relativeto 0 + db 35h,value + else if value eq value element 1 + if value metadata 1 relativeto @ + db 36h + value metadata 1 - @ + else if value metadata 1 relativeto R + db 38h + value metadata 1 - R + else + err "invalid operand" + end if + else + err "invalid operand" + end if + end match + else + err 'invalid operand' + end match +end macro + +macro SUBB? accu,operand + local value + match =A?, accu + match #data, operand + value = +data + db 94h,value + else + value = +operand + if value relativeto 0 + db 95h,value + else if value eq value element 1 + if value metadata 1 relativeto @ + db 96h + value metadata 1 - @ + else if value metadata 1 relativeto R + db 98h + value metadata 1 - R + else + err "invalid operand" + end if + else + err "invalid operand" + end if + end match + else + err 'invalid operand' + end match +end macro + +macro ANL? dest,src + local value,data_value + match =A?, dest + match #data, src + value = +data + db 54h,value + else + value = +src + if value relativeto 0 + db 55h,value + else if value eq value element 1 + if value metadata 1 relativeto @ + db 56h + value metadata 1 - @ + else if value metadata 1 relativeto R + db 58h + value metadata 1 - R + else + err "invalid operand" + end if + else + err "invalid operand" + end if + end match + else match =C?, dest + match /addr,src + db 0B0h,addr + else + db 82h,src + end match + else + match =A?, src + value = +dest + db 52h,value + else match #data, src + value = +dest + data_value = +data + db 53h,value,data_value + else + err 'invalid operand' + end match + end match +end macro + +macro ORL? dest,src + local value,data_value + match =A?, dest + match #data, src + value = +data + db 44h,value + else + value = +src + if value relativeto 0 + db 45h,value + else if value eq value element 1 + if value metadata 1 relativeto @ + db 46h + value metadata 1 - @ + else if value metadata 1 relativeto R + db 48h + value metadata 1 - R + else + err "invalid operand" + end if + else + err "invalid operand" + end if + end match + else match =C?, dest + match /addr,src + db 0A0h,addr + else + db 72h,src + end match + else + match =A?, src + value = +dest + db 42h,value + else match #data, src + value = +dest + data_value = +data + db 43h,value,data_value + else + err 'invalid operand' + end match + end match +end macro + +macro XRL? dest,src + local value,data_value + match =A?, dest + match #data, src + value = +data + db 64h,value + else + value = +src + if value relativeto 0 + db 65h,value + else if value eq value element 1 + if value metadata 1 relativeto @ + db 66h + value metadata 1 - @ + else if value metadata 1 relativeto R + db 68h + value metadata 1 - R + else + err "invalid operand" + end if + else + err "invalid operand" + end if + end match + else + match =A?, src + value = +dest + db 62h,value + else match #data, src + value = +dest + data_value = +data + db 63h,value,data_value + else + err 'invalid operand' + end match + end match +end macro + +macro CLR? operand + match =A?, operand + db 0E4h + else match =C?, operand + db 0C3h + else + db 0C2h,operand + end match +end macro + +macro CPL? operand + match =A?, operand + db 0F4h + else match =C?, operand + db 0B3h + else + db 0B2h,operand + end match +end macro + +macro SETB? operand + match =C?, operand + db 0D3h + else + db 0D2h,operand + end match +end macro + +macro DEC? operand + local value + match =A?, operand + db 14h + else + value = +operand + if value relativeto 0 + db 15h,value + else if value eq value element 1 + if value metadata 1 relativeto @ + db 16h + value metadata 1 - @ + else if value metadata 1 relativeto R + db 18h + value metadata 1 - R + else + err "invalid operand" + end if + else + err "invalid operand" + end if + end match +end macro + +macro INC? operand + local value + match =A?, operand + db 04h + else match =DPTR?, operand + db 0A3h + else + value = +operand + if value relativeto 0 + db 05h,value + else if value eq value element 1 + if value metadata 1 relativeto @ + db 06h + value metadata 1 - @ + else if value metadata 1 relativeto R + db 08h + value metadata 1 - R + else + err "invalid operand" + end if + else + err "invalid operand" + end if + end match +end macro + +macro MOV? dest,src + local value,data_value + match =A?, dest + match #data, src + value = +data + db 74h,value + else + value = +src + if value relativeto 0 + db 0E5h,value + else if value eq value element 1 + if value metadata 1 relativeto @ + db 0E6h + value metadata 1 - @ + else if value metadata 1 relativeto R + db 0E8h + value metadata 1 - R + else + err "invalid operand" + end if + else + err "invalid operand" + end if + end match + else match =C?, dest + db 0A2h,src + else match =C?, src + db 92h,dest + else match =DPTR?, dest + value = src + db 90h + if value eqtype '' + dw +src + else + db value shr 8,value and 0FFh + end if + else + value = +dest + if value relativeto 0 + match =A?, src + db 0F5h,value + else match #data, src + data_value = +data + db 75h,value,data_value + else + @value2 = +src + if @value2 relativeto 0 + db 85h,@value2,value + else if @value2 eq @value2 element 1 + if @value2 metadata 1 relativeto @ + db 86h + @value2 metadata 1 - @,value + else if @value2 metadata 1 relativeto R + db 88h + @value2 metadata 1 - R,value + end if + else if + err "invalid operand" + end if + end match + else if value eq value element 1 + if value metadata 1 relativeto @ + match =A?, src + db 0F6h + value metadata 1 - @ + else match #data, src + data_value = +data + db 76h + value metadata 1 - @,data_value + else + data_value = +src + db 0A6h + value metadata 1 - @,data_value + end match + else if value metadata 1 relativeto R + match =A?, src + db 0F8h + value metadata 1 - R + else match #data, src + data_value = +data + db 78h + value metadata 1 - R,data_value + else + data_value = +src + db 0A8h + value metadata 1 - R,data_value + end match + else + err "invalid operand" + end if + else + err "invalid operand" + end if + end match +end macro + +macro MOVC? operands& + match =A?=, =@A? + =DPTR?, operands + db 93h + else match =A?=, =@A? + =PC?, operands + db 83h + else + err "invalid operand" + end match +end macro + +macro MOVX? dest,src + local value + match =A?, dest + match =@DPTR?, src + db 0E0h + else + value = +src + if value eq value element 1 & value metadata 1 relativeto @ + db 0E2h + value metadata 1 - @ + else + err "invalid operand" + end if + end match + else match =A?, src + match =@DPTR?, dest + db 0F0h + else + value = +dest + if value eq value element 1 & value metadata 1 relativeto @ + db 0F2h + value metadata 1 - @ + else + err "invalid operand" + end if + end match + else + err "invalid operand" + end match +end macro + +macro SWAP? operand + match =A?, operand + db 0C4h + else + err 'invalid operand' + end match +end macro + +macro DA? operand + match =A?, operand + db 0D4h + else + err 'invalid operand' + end match +end macro + +macro RR? operand + match =A?, operand + db 03h + else + err 'invalid operand' + end match +end macro + +macro RRC? operand + match =A?, operand + db 13h + else + err 'invalid operand' + end match +end macro + +macro RL? operand + match =A?, operand + db 23h + else + err 'invalid operand' + end match +end macro + +macro RLC? operand + match =A?, operand + db 33h + else + err 'invalid operand' + end match +end macro + +macro DIV? operand + match =AB?, operand + db 84h + else + err "invalid operand" + end match +end macro + +macro MUL? operand + match =AB?, operand + db 0A4h + else + err "invalid operand" + end match +end macro + +macro NOP? + db 0 +end macro + +macro POP? addr + local value + value = +addr + db 0D0h,value +end macro + +macro PUSH? addr + local value + value = +addr + db 0C0h,value +end macro + +macro RET? + db 22h +end macro + +macro RETI? + db 32h +end macro + +macro XCH? accu,operand + local value + match =A?, accu + value = +operand + if value relativeto 0 + db 0C5h,value + else if value eq value element 1 + if value metadata 1 relativeto @ + db 0C6h + value metadata 1 - @ + else if value metadata 1 relativeto R + db 0C8h + value metadata 1 - R + else + err "invalid operand" + end if + else + err "invalid operand" + end if + else + err 'invalid operand' + end match +end macro + +macro XCHD? accu,src + local value + match =A?, accu + value = +src + if value eq value element 1 & value metadata 1 relativeto @ + db 0D6h + value metadata 1 - @ + else + err "invalid operand" + end if + else + err "invalid operand" + end match +end macro + +struc EQU? value + . = value +end struc + +DSEG?.$ = 0 +DSEG?.open = 0 + +macro DSEG? @:at DSEG?.$ + if ~ DSEG?.open + virtual @ + DSEG?.open = 1 + else + match =AT? addr, @ + org addr + else + err 'invalid argument' + end match + end if +end macro + +macro CSEG? @ + if DSEG?.open + DSEG?.$ = $ + end virtual + DSEG?.open = 0 + end if + match =AT? addr, @ + org addr + else match any, @ + err 'invalid argument' + end match +end macro + +macro bitslabel definition + match name =at? address, definition + label name at address + repeat 8, i:0 + label name.i at address+i + end repeat + else + err 'syntax error' + end match +end macro + +; Data addresses: + +bitslabel P0 at 080h +bitslabel SP at 081h +bitslabel DPL at 082h +bitslabel DPH at 083h +bitslabel PCON at 087h +bitslabel TCON at 088h +bitslabel TMOD at 089h +bitslabel TL0 at 08Ah +bitslabel TL1 at 08Bh +bitslabel TH0 at 08Ch +bitslabel TH1 at 08Dh +bitslabel P1 at 090h +bitslabel SCON at 098h +bitslabel SBUF at 099h +bitslabel P2 at 0A0h +bitslabel IE at 0A8h +bitslabel P3 at 0B0h +bitslabel IP at 0B8h +bitslabel PSW at 0D0h +bitslabel ACC at 0E0h +bitslabel B at 0F0h + +; Bit addresses: + +label IT0 at 088h +label IE0 at 089h +label IT1 at 08Ah +label IE1 at 08Bh +label TR0 at 08Ch +label TF0 at 08Dh +label TR1 at 08Eh +label TF1 at 08Fh +label RI at 098h +label TI at 099h +label RB8 at 09Ah +label TB8 at 09Bh +label REN at 09Ch +label SM2 at 09Dh +label SM1 at 09Eh +label SM0 at 09Fh +label EX0 at 0A8h +label ET0 at 0A9h +label EX1 at 0AAh +label ET1 at 0ABh +label ES at 0ACh +label EA at 0AFh +label RXD at 0B0h +label TXD at 0B1h +label INT0 at 0B2h +label INT1 at 0B3h +label T0 at 0B4h +label T1 at 0B5h +label WR at 0B6h +label RD at 0B7h +label PX0 at 0B8h +label PT0 at 0B9h +label PX1 at 0BAh +label PT1 at 0BBh +label PS at 0BCh +label P at 0D0h +label OV at 0D2h +label RS0 at 0D3h +label RS1 at 0D4h +label F0 at 0D5h +label AC at 0D6h +label CY at 0D7h + +; Code addresses: + +label RESET at 000h +label EXTI0 at 003h +label TIMER0 at 00Bh +label EXTI1 at 013h +label TIMER1 at 01Bh +label SINT at 023h diff --git a/x86_64_sse2_x87/fasm/examples/8051/hex.inc b/x86_64_sse2_x87/fasm/examples/8051/hex.inc new file mode 100644 index 0000000..1036cae --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/8051/hex.inc @@ -0,0 +1,75 @@ + +define HEX +format binary as 'hex' + +virtual at 0 + HEX.digits:: db '0123456789ABCDEF' +end virtual + +macro HEX.byte value + HEX.checksum = (HEX.checksum + (value)) and 0FFh + local digit + load digit:byte from HEX.digits:(value) shr 4 + db digit + load digit:byte from HEX.digits:(value) and 0Fh + db digit +end macro + +macro HEX.line length,address,type,value + HEX.checksum = 0 + db ':' + HEX.byte length + HEX.byte (address) shr 8 + HEX.byte (address) and 0FFh + HEX.byte type + HEX.data = value + repeat length + HEX.byte HEX.data and 0FFh + HEX.data = HEX.data shr 8 + end repeat + HEX.data = (-HEX.checksum) and 0FFh + HEX.byte HEX.data + db 13,10 +end macro + +macro HEX.seg address:0 + virtual at address +end macro + +macro HEX.endseg + local code,address,high,size,bytes + code:: address = $$ + size = $-$$ + end virtual + high = 0 + while size + if address shr 16 <> high + high = address shr 16 + HEX.line 2,0,4,high bswap 2 + end if + if size>10h + load bytes:10h from code:address + HEX.line 10h,address and 0FFFFh,0,bytes + address = address + 10h + size = size - 10h + else + load bytes:size from code:address + HEX.line size,address and 0FFFFh,0,bytes + break + end if + end while +end macro + +macro ORG? address + if $ <> address + HEX.endseg + HEX.seg address + end if +end macro + +HEX.seg + +postpone + HEX.endseg + HEX.line 0,0,1,0 +end postpone diff --git a/x86_64_sse2_x87/fasm/examples/8051/invert.asm b/x86_64_sse2_x87/fasm/examples/8051/invert.asm new file mode 100644 index 0000000..6c3ae3a --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/8051/invert.asm @@ -0,0 +1,46 @@ + +; This simple example reads data from external device through P2 +; and outputs it inverted through P1. + +include 'hex.inc' +include '8051.inc' + +ORG 0 + JMP setup + +ORG 3 + JMP ext0_interrupt + +ORG 0Bh + JMP timer0_interrupt + +ORG 30h + +setup: + + SETB EX0 + SETB IT0 + CLR P0.7 + + MOV TH0,#-40 + MOV TL0,#-40 + MOV TMOD,#2 + + SETB TR0 + SETB ET0 + SETB EA + + JMP $ + +timer0_interrupt: + CLR P3.6 + SETB P3.6 + RETI + +ext0_interrupt: + CLR P3.7 + MOV A,P2 + CPL A + MOV P1,A + SETB P3.7 + RETI diff --git a/x86_64_sse2_x87/fasm/examples/8051/make.cmd b/x86_64_sse2_x87/fasm/examples/8051/make.cmd new file mode 100644 index 0000000..96f336c --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/8051/make.cmd @@ -0,0 +1 @@ +fasmg invert.asm invert.hex diff --git a/x86_64_sse2_x87/fasm/examples/avr/avr.inc b/x86_64_sse2_x87/fasm/examples/avr/avr.inc new file mode 100644 index 0000000..cf3c88b --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/avr/avr.inc @@ -0,0 +1,897 @@ + +element R + +repeat 32 i:0 + element R#i? : R + i +end repeat + +XH? = R27 +XL? = R26 +YH? = R29 +YL? = R28 +ZH? = R31 +ZL? = R30 + +element X +element Y +element Z + +iterate , ADC,000111b, ADD,000011b, AND, 001000b, EOR, 001001b, CP,000101b, CPC,000001b, CPSE,000100b, MOV,001011b, MUL,100111b, OR,001010b, SBC,000010b, SUB,000110b + macro instr? Rd,Rr + local value,d,r + value = +Rd + if value metadata 1 relativeto R & value eq value element 1 + d = value metadata 1 - R + value = +Rr + if value metadata 1 relativeto R & value eq value element 1 + r = value metadata 1 - R + dw r and 1111b + d shl 4 + (r shr 4) shl 9 + opcode shl 10 + else + err 'invalid operand' + end if + else + err 'invalid operand' + end if + end macro +end iterate + +iterate , ADIW,10010110b, SBIW,10010111b + macro instr? RRd,K + local value,d + match Rdh:Rdl,RRd + value = +Rdl + if value metadata 1 relativeto R & value eq value element 1 + d = value metadata 1 - R + value = +Rdh + if value metadata 1 relativeto R & value eq value element 1 + if value metadata 1 - R = d + 1 + if d = 24 | d = 26 | d = 28 | d = 30 + value = +K + if value >= 0 & value <= 63 + dw value and 1111b + ((d-24) shr 1) shl 4 + (value shr 4) shl 6 + opcode shl 8 + else + err 'immediate value out of range' + end if + else + err 'specified register not allowed for this instruction' + end if + else + err 'invalid operand' + end if + else + err 'invalid operand' + end if + else + err 'invalid operand' + end if + else match Rd,RRd + value = +Rd + if value metadata 1 relativeto R & value eq value element 1 + d = value metadata 1 - R + if d = 24 | d = 26 | d = 28 | d = 30 + value = +K + if value >= 0 & value <= 63 + dw value and 1111b + ((d-24) shr 1) shl 4 + (value shr 4) shl 6 + opcode shl 8 + else + err 'immediate value out of range' + end if + else + err 'specified register not allowed for this instruction' + end if + else + err 'invalid operand' + end if + else + err 'invalid operand' + end match + end macro +end iterate + +iterate , ANDI,0111b, CPI,0011b, LDI,1110b, ORI,0110b, SBCI,0100b, SBR,0110b, SUBI,0101b + macro instr? Rd,K + local value,d + value = +Rd + if value metadata 1 relativeto R & value eq value element 1 + d = value metadata 1 - R + if d >= 16 + value = +K + if value >= -256 & value <= 255 + value = value and 0FFh + dw value and 1111b + (d-16) shl 4 + (value shr 4) shl 8 + opcode shl 12 + else + err 'immediate value out of range' + end if + else + err 'specified register not allowed for this instruction' + end if + else + err 'invalid operand' + end if + end macro +end iterate + +iterate , ASR,0101b, COM,0000b, DEC,1010b, INC,0011b, LSR,0110b, NEG,0001b, ROR,0111b, SWAP,0010b + macro instr? Rd + local value,d + value = +Rd + if value metadata 1 relativeto R & value eq value element 1 + d = value metadata 1 - R + dw opcode + d shl 4 + 1001010b shl 9 + else + err 'invalid operand' + end if + end macro +end iterate + +iterate , BCLR,1001'0100'1000'1000b, BSET,1001'0100'0000'1000b + macro instr? s + local value + value = +s + if value >= 0 & value <= 7 + dw opcode + value shl 4 + else + err 'bit index out of range' + end if + end macro +end iterate + +iterate , BLD,1111100b, BST,1111101b, SBRC,1111110b, SBRS,1111111b + macro instr? Rd,b + local value,d + value = +Rd + if value metadata 1 relativeto R & value eq value element 1 + d = value metadata 1 - R + value = +b + if value >= 0 & value <= 7 + dw value + d shl 4 + opcode shl 9 + else + err 'bit index out of range' + end if + else + err 'invalid operand' + end if + end macro +end iterate + +iterate , BRBC,111101b, BRBS,111100b + macro instr? s,k + local index,offset + index = +s + if index >= 0 & index <= 7 + offset = -($ shr 1 + 1) + k + if offset >= -64 & offset <= 63 + dw index + (offset and 1111111b) shl 3 + opcode shl 10 + else + err 'relative jump out of range' + end if + else + err 'bit index out of range' + end if + end macro +end iterate + +macro BRCC? k + BRBC 0,k +end macro + +macro BRCS? k + BRBS 0,k +end macro + +macro BREQ? k + BRBS 1,k +end macro + +macro BRGE? k + BRBC 4,k +end macro + +macro BRHC? k + BRBC 5,k +end macro + +macro BRHS? k + BRBS 5,k +end macro + +macro BRID? k + BRBC 7,k +end macro + +macro BRIE? k + BRBS 7,k +end macro + +macro BRLO? k + BRBS 0,k +end macro + +macro BRLT? k + BRBS 4,k +end macro + +macro BRMI? k + BRBS 2,k +end macro + +macro BRNE? k + BRBC 1,k +end macro + +macro BRPL? k + BRBC 2,k +end macro + +macro BRSH? k + BRBC 0,k +end macro + +macro BRTC? k + BRBC 6,k +end macro + +macro BRTS? k + BRBS 6,k +end macro + +macro BRVC? k + BRBC 3,k +end macro + +macro BRVS? k + BRBS 3,k +end macro + +macro CALL? k + local offset + offset = -($ shr 1 + 1) + k + if offset >= -2048 & offset <= 2047 + dw offset and (1 shl 12 - 1) + 1101b shl 12 + else + offset = +k + if offset >= 0 & offset <= 1 shl 22 - 1 + dw (offset shr 16) and 1 + 111b shl 1 + (offset shr 17) shl 4 + 1001010b shl 9 + dw offset and (1 shl 16 - 1) + else + err 'value out of range' + end if + end if +end macro + +iterate , CBI,10011000b, SBI,10011010b, SBIC,10011001b, SBIS,10011011b + macro instr? A,b + local reg,index + reg = +A + if reg >= 0 & reg <= 31 + index = +b + if index >= 0 & index <= 7 + dw index + reg shl 3 + opcode shl 8 + else + err 'bit index out of range' + end if + else + err 'specified register number not allowed' + end if + end macro +end iterate + +macro CBR? r,K + ANDI r,$FF-(K) +end macro + +macro CLC? + dw 1001'0100'1000'1000b +end macro + +macro CLH? + dw 1001'0100'1101'1000b +end macro + +macro CLI? + dw 1001'0100'1111'1000b +end macro + +macro CLN? + dw 1001'0100'1010'1000b +end macro + +macro CLR? r + EOR r,r +end macro + +macro CLS? + dw 1001'0100'1100'1000b +end macro + +macro CLT? + dw 1001'0100'1110'1000b +end macro + +macro CLV? + dw 1001'0100'1011'1000b +end macro + +macro CLZ? + dw 1001'0100'1001'1000b +end macro + +macro DES? K + local value + value = +K + if value >= 0 & value <= 0x0F + dw 1011b + value shl 4 + 10010100b shl 8 + else + err 'value out of range' + end if +end macro + +macro EICALL? + dw 1001'0101'0001'1001b +end macro + +macro EIJMP? + dw 1001'0100'0001'1001b +end macro + +iterate , ELPM,1001'0101'1101'1000b,0110b,0111b, LPM,1001'0101'1100'1000b,0100b,0101b + macro instr? args& + local value,d + match , args + dw opcode + else match Rd=, =Z?, args + value = +Rd + if value metadata 1 relativeto R & value eq value element 1 + d = value metadata 1 - R + dw opcode2 + d shl 4 + 1001000b shl 9 + else + err 'invalid operand' + end if + else match Rd=, =Z?+, args + value = +Rd + if value metadata 1 relativeto R & value eq value element 1 + d = value metadata 1 - R + dw opcode3 + d shl 4 + 1001000b shl 9 + else + err 'invalid operand' + end if + else + err 'invalid operand' + end match + end macro +end iterate + +iterate , FMUL,1100001000b, FMULS,1110000000b, FMULSU,1110001000b + macro instr? Rd,Rr + local value,d,r + value = +Rd + if value metadata 1 relativeto R & value eq value element 1 + d = value metadata 1 - R + if d >= 16 & d <= 23 + value = +Rr + if value metadata 1 relativeto R & value eq value element 1 + r = value metadata 1 - R + if r >= 16 & r <= 23 + dw opcode + (r-16) + (d-16) shl 4 + else + err 'specified register not allowed for this instruction' + end if + else + err 'invalid operand' + end if + else + err 'specified register not allowed for this instruction' + end if + else + err 'invalid operand' + end if + end macro +end iterate + +macro ICALL? + dw 1001'0101'0000'1001b +end macro + +macro IJMP? + dw 1001'0100'0000'1001b +end macro + +macro IN? Rd,A + local value,d + value = +Rd + if value metadata 1 relativeto R & value eq value element 1 + d = value metadata 1 - R + value = +A + if A >= 0 & A <= 63 + dw A and 1111b + d shl 4 + (A shr 4) shl 9 + 10110b shl 11 + else + err 'address out of range' + end if + else + err 'invalid operand' + end if +end macro + +macro JMP? k + local offset + offset = -($ shr 1 + 1) + k + if offset>=-2048 & offset<=2047 + dw offset and 111111111111b + 1100b shl 12 + else + offset = +k + if offset>=0 & offset<=1 shl 22 - 1 + dw (offset shr 16) and 1 + 110b shl 1 + (offset shr 17) shl 4 + 1001010b shl 9 + dw offset and (1 shl 16 - 1) + else + err 'value out of range' + end if + end if +end macro + +iterate , LAC,0110b, LAS,0101b, LAT,0111b, XCH,0100b + macro instr? Rw,Rd + local value,d + match =Z?, Rw + value = +Rd + if value metadata 1 relativeto R & value eq value element 1 + d = value metadata 1 - R + dw opcode + d shl 4 + 1001001b shl 9 + else + err 'invalid operand' + end if + else + err 'invalid operand' + end match + end macro +end iterate + +macro LD? Rd,Rw + local value,d + value = +Rd + if value metadata 1 relativeto R & value eq value element 1 + d = value metadata 1 - R + match =X?, Rw + dw 1100b + d shl 4 + 1001000b shl 9 + else match =Y?, Rw + dw 1000b + d shl 4 + 1000000b shl 9 + else match =Z?, Rw + dw 0000b + d shl 4 + 1000000b shl 9 + else match =X?+, Rw + dw 1101b + d shl 4 + 1001000b shl 9 + else match =Y?+, Rw + dw 1001b + d shl 4 + 1001000b shl 9 + else match =Z?+, Rw + dw 0001b + d shl 4 + 1001000b shl 9 + else match -=X?, Rw + dw 1110b + d shl 4 + 1001000b shl 9 + else match -=Y?, Rw + dw 1010b + d shl 4 + 1001000b shl 9 + else match -=Z?, Rw + dw 0010b + d shl 4 + 1001000b shl 9 + else + err 'invalid operand' + end match + else + err 'invalid operand' + end if +end macro + +macro LDD? Rd,Rq + local value,d,q + value = +Rd + if value metadata 1 relativeto R & value eq value element 1 + d = value metadata 1 - R + value = +Rq + if value relativeto Y + q = value - Y + if q >= 0 & q <= 63 + dw q and 111b + 1 shl 3 + d shl 4 + ((q shr 3) and 11b) shl 10 + (q shr 5) shl 13 + 10b shl 14 + else + err 'value out of range' + end if + else if value relativeto Z + q = value - Z + if q >= 0 & q <= 63 + dw q and 111b + d shl 4 + ((q shr 3) and 11b) shl 10 + (q shr 5) shl 13 + 10b shl 14 + else + err 'value out of range' + end if + else + err 'invalid operand' + end if + else + err 'invalid operand' + end if +end macro + +macro LDS? Rd,k + local value,d + value = +Rd + if value metadata 1 relativeto R & value eq value element 1 + d = value metadata 1 - R + value = +k + if value >= 0 & value <= 65535 + dw d shl 4 + 1001000b shl 9 + dw value + else + err 'address out of range' + end if + else + err 'invalid operand' + end if +end macro + +macro LSL? r + ADD r,r +end macro + +macro MOVW? args& + local value,d,r + match Rdh:Rdl=,Rrh:Rrl, args + value = +Rdl + if value metadata 1 relativeto R & value eq value element 1 + d = value metadata 1 - R + value = +Rdh + if value metadata 1 relativeto R & value eq value element 1 + if value metadata 1 - R = d + 1 & d and 1 = 0 + value = +Rrl + if value metadata 1 relativeto R & value eq value element 1 + r = value metadata 1 - R + value = +Rrh + if value metadata 1 relativeto R & value eq value element 1 + if value metadata 1 - R = r + 1 & r and 1 = 0 + dw r shr 1 + (d shr 1) shl 4 + 1 shl 8 + else + err 'invalid operand' + end if + else + err 'invalid operand' + end if + else + err 'invalid operand' + end if + else + err 'invalid operand' + end if + else + err 'invalid operand' + end if + else + err 'invalid operand' + end if + else match Rd=,Rr,args + value = +Rd + if value metadata 1 relativeto R & value eq value element 1 + d = value metadata 1 - R + if d and 1 = 0 + value = +Rr + if value metadata 1 relativeto R & value eq value element 1 + r = value metadata 1 - R + if r and 1 = 0 + dw r shr 1 + (d shr 1) shl 4 + 1 shl 8 + else + err 'invalid operand' + end if + else + err 'invalid operand' + end if + else + err 'invalid operand' + end if + else + err 'invalid operand' + end if + else + err 'invalid operand' + end match +end macro + +iterate , MULS,0010b, MULSU,0011b + macro instr? Rd,Rr + local value,d,r + value = +Rd + if value metadata 1 relativeto R & value eq value element 1 + d = value metadata 1 - R + if d >= 16 & d <= 31 + value = +Rr + if value metadata 1 relativeto R & value eq value element 1 + r = value metadata 1 - R + if r >= 16 & r <= 31 + dw (r-16) + (d-16) shl 4 + opcode shl 8 + else + err 'specified register not allowed for this instruction' + end if + else + err 'invalid operand' + end if + else + err 'specified register not allowed for this instruction' + end if + else + err 'invalid operand' + end if + end macro +end iterate + +macro NOP? + dw 0 +end macro + +macro OUT? A,Rr + local value,r + value = +Rr + if value metadata 1 relativeto R & value eq value element 1 + r = value metadata 1 - R + value = +A + if A >= 0 & A <= 63 + dw A and 1111b + r shl 4 + (A shr 4) shl 9 + 10111b shl 11 + else + err 'address out of range' + end if + else + err 'invalid operand' + end if +end macro + +iterate , POP,000b, PUSH,001b + macro instr? Rd + local value,d + value = +Rd + if value metadata 1 relativeto R & value eq value element 1 + d = value metadata 1 - R + dw 1111b + d shl 4 + opcode shl 9 + 1001b shl 12 + else + err 'invalid operand' + end if + end macro +end iterate + +iterate , RCALL,1101b, RJMP,1100b + macro instr? k + local offset + offset = -($ shr 1 + 1) + k + if offset>=-2048 & offset<=2047 + dw offset and 111111111111b + opcode shl 12 + else + err 'relative jump out of range' + end if + end macro +end iterate + +macro RET? + dw 1001'0101'0000'1000b +end macro + +macro RETI? + dw 1001'0101'0001'1000b +end macro + +macro ROL? r + ADC r,r +end macro + +macro SEC? + dw 1001'0100'0000'1000b +end macro + +macro SEH? + dw 1001'0100'0101'1000b +end macro + +macro SEI? + dw 1001'0100'0111'1000b +end macro + +macro SEN? + dw 1001'0100'0010'1000b +end macro + +macro SER? Rd + local value,d + value = +Rd + if value metadata 1 relativeto R & value eq value element 1 + d = value metadata 1 - R + if d >= 16 + dw 1111b + (d-16) shl 4 + 11101111b shl 8 + else + err 'specified register not allowed for this instruction' + end if + else + err 'invalid operand' + end if +end macro + +macro SES? + dw 1001'0100'0100'1000b +end macro + +macro SET? + dw 1001'0100'0110'1000b +end macro + +macro SEV? + dw 1001'0100'0011'1000b +end macro + +macro SEZ? + dw 1001'0100'0001'1000b +end macro + +macro SLEEP? + dw 1001'0101'1000'1000b +end macro + +macro SPM? args + match , args + dw 1001'0101'1110'1000b + else match =Z?+,args + dw 1001'0101'1111'1000b + else + err 'invalid operand' + end match +end macro + +macro ST? Rw,Rr + local value,r + value = +Rr + if value metadata 1 relativeto R & value eq value element 1 + r = value metadata 1 - R + match =X?, Rw + dw 1100b + r shl 4 + 1001001b shl 9 + else match =Y?, Rw + dw 1000b + r shl 4 + 1000001b shl 9 + else match =Z?, Rw + dw 0000b + r shl 4 + 1000001b shl 9 + else match =X?+, Rw + dw 1101b + r shl 4 + 1001001b shl 9 + else match =Y?+, Rw + dw 1001b + r shl 4 + 1001001b shl 9 + else match =Z?+, Rw + dw 0001b + r shl 4 + 1001001b shl 9 + else match -=X?, Rw + dw 1110b + r shl 4 + 1001001b shl 9 + else match -=Y?, Rw + dw 1010b + r shl 4 + 1001001b shl 9 + else match -=Z?, Rw + dw 0010b + r shl 4 + 1001001b shl 9 + else + err 'invalid operand' + end match + else + err 'invalid operand' + end if +end macro + +macro STD? Rq,Rr + local value,r,q + value = +Rr + if value metadata 1 relativeto R & value eq value element 1 + r = value metadata 1 - R + value = +Rq + if value relativeto Y + q = value - Y + if q >= 0 & q <= 63 + dw q and 111b + 1 shl 3 + r shl 4 + 1 shl 9 + ((q shr 3) and 11b) shl 10 + (q shr 5) shl 13 + 10b shl 14 + else + err 'value out of range' + end if + else if value relativeto Z + q = value - Z + if q >= 0 & q <= 63 + dw q and 111b + r shl 4 + 1 shl 9 + ((q shr 3) and 11b) shl 10 + (q shr 5) shl 13 + 10b shl 14 + else + err 'value out of range' + end if + else + err 'invalid operand' + end if + else + err 'invalid operand' + end if +end macro + +macro STS? k,Rr + local value,r + value = +Rr + if value metadata 1 relativeto R & value eq value element 1 + r = value metadata 1 - R + value = +k + if value >= 0 & value <= 65535 + dw r shl 4 + 1001001b shl 9 + dw value + else + err 'address out of range' + end if + else + err 'invalid operand' + end if +end macro + +macro TST? Rd + AND Rd,Rd +end macro + +macro WDR? + dw 1001'0101'1010'1000b +end macro + +macro BREAK? + if defined % + break ; loop control directive + else + dw 1001'0101'1001'1000b + end if +end macro + +PC? equ ($ shr 1) + +calminstruction (label) ? definition& + local command + match : command?, definition + jno other + match :: command?, definition + jyes other + arrange definition, =LABEL label =AT =$ =shr 1 + assemble definition + assemble command + exit + other: + arrange definition, label definition + assemble definition +end calminstruction + +if defined SRAM_START + DSEG?.$ = SRAM_START +else + DSEG?.$ = 60h +end if + +DSEG? = 0 + +define DIRECTIVE DIRECTIVE + +macro __ORG? addr* + if ~ DSEG? + org (addr) shl 1 + else + org addr + end if +end macro +DIRECTIVE.ORG? equ __ORG + +macro __EQU? definition& + match name == value, definition + name? = value + else + err 'invalid definition' + end match +end macro +DIRECTIVE.EQU? equ __EQU + +macro __DSEG? + if ~ DSEG? + virtual at DSEG?.$ + DSEG? = 1 + end if +end macro +DIRECTIVE.DSEG? equ __DSEG + +macro __CSEG? + if DSEG? + DSEG?.$ = $ + end virtual + DSEG? = 0 + end if +end macro +DIRECTIVE.CSEG? equ __CSEG + +calminstruction ? line& + local command + match .command, line + jno pass + transform command, DIRECTIVE + jno pass + assemble command + exit + pass: + assemble line +end calminstruction diff --git a/x86_64_sse2_x87/fasm/examples/avr/counter.asm b/x86_64_sse2_x87/fasm/examples/avr/counter.asm new file mode 100644 index 0000000..63c6189 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/avr/counter.asm @@ -0,0 +1,54 @@ + +; This example upon each reset increases the 8-bit counter stored in EEPROM +; and then presents the bits of this value on the pins of port A. + +include "avr.inc" +include "..\8051\hex.inc" + +include "m16def.inc" + +.org 0 + rjmp start + +start: + ldi r16,RAMEND and $ff + out spl,r16 + ldi r16,RAMEND shr 8 + out sph,r16 + + ldi r16,$37 + call eeprom_read_byte + mov r17,r16 + inc r17 + ldi r16,$37 + call eeprom_write_byte + + ldi r18,11111111b + out DDRA,r18 + out PORTA,r17 + +hang: jmp hang + +eeprom_read_byte: +; r16 = EEPROM address +; returns: r16 = byte data + sbic EECR,EEWE + jmp eeprom_read_byte + out EEARL,r16 + sbi EECR,EERE + in r16,EEDR + ret + +eeprom_write_byte: +; r16 = EEPROM address +; r17 = byte data + cli + while_eeprom_busy: + sbic EECR,EEWE + jmp while_eeprom_busy + out EEARL,r16 + out EEDR,r17 + sbi EECR,EEMWE + sbi EECR,EEWE + sei + ret diff --git a/x86_64_sse2_x87/fasm/examples/avr/m16def.inc b/x86_64_sse2_x87/fasm/examples/avr/m16def.inc new file mode 100644 index 0000000..17491ae --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/avr/m16def.inc @@ -0,0 +1,738 @@ + +; Register/Bit Definitions for the ATmega16 + +; I/O Registers +.equ SREG = 0x3f +.equ SPH = 0x3e +.equ SPL = 0x3d +.equ OCR0 = 0x3c +.equ GICR = 0x3b +.equ GIFR = 0x3a +.equ TIMSK = 0x39 +.equ TIFR = 0x38 +.equ SPMCSR = 0x37 +.equ TWCR = 0x36 +.equ MCUCR = 0x35 +.equ MCUCSR = 0x34 +.equ TCCR0 = 0x33 +.equ TCNT0 = 0x32 +.equ OSCCAL = 0x31 +.equ OCDR = 0x31 +.equ SFIOR = 0x30 +.equ TCCR1A = 0x2f +.equ TCCR1B = 0x2e +.equ TCNT1H = 0x2d +.equ TCNT1L = 0x2c +.equ OCR1AH = 0x2b +.equ OCR1AL = 0x2a +.equ OCR1BH = 0x29 +.equ OCR1BL = 0x28 +.equ ICR1H = 0x27 +.equ ICR1L = 0x26 +.equ TCCR2 = 0x25 +.equ TCNT2 = 0x24 +.equ OCR2 = 0x23 +.equ ASSR = 0x22 +.equ WDTCR = 0x21 +.equ UBRRH = 0x20 +.equ UCSRC = 0x20 +.equ EEARH = 0x1f +.equ EEARL = 0x1e +.equ EEDR = 0x1d +.equ EECR = 0x1c +.equ PORTA = 0x1b +.equ DDRA = 0x1a +.equ PINA = 0x19 +.equ PORTB = 0x18 +.equ DDRB = 0x17 +.equ PINB = 0x16 +.equ PORTC = 0x15 +.equ DDRC = 0x14 +.equ PINC = 0x13 +.equ PORTD = 0x12 +.equ DDRD = 0x11 +.equ PIND = 0x10 +.equ SPDR = 0x0f +.equ SPSR = 0x0e +.equ SPCR = 0x0d +.equ UDR = 0x0c +.equ UCSRA = 0x0b +.equ UCSRB = 0x0a +.equ UBRRL = 0x09 +.equ ACSR = 0x08 +.equ ADMUX = 0x07 +.equ ADCSRA = 0x06 +.equ ADCH = 0x05 +.equ ADCL = 0x04 +.equ TWDR = 0x03 +.equ TWAR = 0x02 +.equ TWSR = 0x01 +.equ TWBR = 0x00 + +; TCCR0 - Timer/Counter Control Register +.equ CS00 = 0 ; Clock Select 1 +.equ CS01 = 1 ; Clock Select 1 +.equ CS02 = 2 ; Clock Select 2 +.equ WGM01 = 3 ; Waveform Generation Mode 1 +.equ CTC0 = WGM01 ; For compatibility +.equ COM00 = 4 ; Compare match Output Mode 0 +.equ COM01 = 5 ; Compare Match Output Mode 1 +.equ WGM00 = 6 ; Waveform Generation Mode 0 +.equ PWM0 = WGM00 ; For compatibility +.equ FOC0 = 7 ; Force Output Compare + +; TCNT0 - Timer/Counter Register +.equ TCNT0_0 = 0 +.equ TCNT0_1 = 1 +.equ TCNT0_2 = 2 +.equ TCNT0_3 = 3 +.equ TCNT0_4 = 4 +.equ TCNT0_5 = 5 +.equ TCNT0_6 = 6 +.equ TCNT0_7 = 7 + +; OCR0 - Output Compare Register +.equ OCR0_0 = 0 +.equ OCR0_1 = 1 +.equ OCR0_2 = 2 +.equ OCR0_3 = 3 +.equ OCR0_4 = 4 +.equ OCR0_5 = 5 +.equ OCR0_6 = 6 +.equ OCR0_7 = 7 + +; TIMSK - Timer/Counter Interrupt Mask Register +.equ TOIE0 = 0 ; Timer/Counter0 Overflow Interrupt Enable +.equ OCIE0 = 1 ; Timer/Counter0 Output Compare Match Interrupt register +.equ TOIE1 = 2 ; Timer/Counter1 Overflow Interrupt Enable +.equ OCIE1B = 3 ; Timer/Counter1 Output CompareB Match Interrupt Enable +.equ OCIE1A = 4 ; Timer/Counter1 Output CompareA Match Interrupt Enable +.equ TICIE1 = 5 ; Timer/Counter1 Input Capture Interrupt Enable +.equ TOIE2 = 6 ; Timer/Counter2 Overflow Interrupt Enable +.equ OCIE2 = 7 ; Timer/Counter2 Output Compare Match Interrupt Enable + +; TIFR - Timer/Counter Interrupt Flag register +.equ TOV0 = 0 ; Timer/Counter0 Overflow Flag +.equ OCF0 = 1 ; Output Compare Flag 0 +.equ TOV2 = 6 ; Timer/Counter2 Overflow Flag +.equ OCF2 = 7 ; Output Compare Flag 2 + +; SFIOR - Special Function IO Register +.equ PSR10 = 0 ; Prescaler Reset Timer/Counter1 and Timer/Counter0 + +; TIFR - Timer/Counter Interrupt Flag register +.equ TOV1 = 2 ; Timer/Counter1 Overflow Flag +.equ OCF1B = 3 ; Output Compare Flag 1B +.equ OCF1A = 4 ; Output Compare Flag 1A +.equ ICF1 = 5 ; Input Capture Flag 1 + +; TCCR1A - Timer/Counter1 Control Register A +.equ WGM10 = 0 ; Waveform Generation Mode +.equ PWM10 = WGM10 ; For compatibility +.equ WGM11 = 1 ; Waveform Generation Mode +.equ PWM11 = WGM11 ; For compatibility +.equ FOC1B = 2 ; Force Output Compare 1B +.equ FOC1A = 3 ; Force Output Compare 1A +.equ COM1B0 = 4 ; Compare Output Mode 1B, bit 0 +.equ COM1B1 = 5 ; Compare Output Mode 1B, bit 1 +.equ COM1A0 = 6 ; Comparet Ouput Mode 1A, bit 0 +.equ COM1A1 = 7 ; Compare Output Mode 1A, bit 1 + +; TCCR1B - Timer/Counter1 Control Register B +.equ CS10 = 0 ; Prescaler source of Timer/Counter 1 +.equ CS11 = 1 ; Prescaler source of Timer/Counter 1 +.equ CS12 = 2 ; Prescaler source of Timer/Counter 1 +.equ WGM12 = 3 ; Waveform Generation Mode +.equ CTC10 = WGM12 ; For compatibility +.equ CTC1 = WGM12 ; For compatibility +.equ WGM13 = 4 ; Waveform Generation Mode +.equ CTC11 = WGM13 ; For compatibility +.equ ICES1 = 6 ; Input Capture 1 Edge Select +.equ ICNC1 = 7 ; Input Capture 1 Noise Canceler + +; GICR - General Interrupt Control Register +.equ GIMSK = GICR ; For compatibility +.equ IVCE = 0 ; Interrupt Vector Change Enable +.equ IVSEL = 1 ; Interrupt Vector Select +.equ INT2 = 5 ; External Interrupt Request 2 Enable +.equ INT0 = 6 ; External Interrupt Request 0 Enable +.equ INT1 = 7 ; External Interrupt Request 1 Enable + +; GIFR - General Interrupt Flag Register +.equ INTF2 = 5 ; External Interrupt Flag 2 +.equ INTF0 = 6 ; External Interrupt Flag 0 +.equ INTF1 = 7 ; External Interrupt Flag 1 + +; MCUCR - General Interrupt Control Register +.equ ISC00 = 0 ; Interrupt Sense Control 0 Bit 0 +.equ ISC01 = 1 ; Interrupt Sense Control 0 Bit 1 +.equ ISC10 = 2 ; Interrupt Sense Control 1 Bit 0 +.equ ISC11 = 3 ; Interrupt Sense Control 1 Bit 1 + +; MCUCSR - MCU Control And Status Register +.equ ISC2 = 6 ; Interrupt Sense Control 2 + +; EEDR - EEPROM Data Register +.equ EEDR0 = 0 ; EEPROM Data Register bit 0 +.equ EEDR1 = 1 ; EEPROM Data Register bit 1 +.equ EEDR2 = 2 ; EEPROM Data Register bit 2 +.equ EEDR3 = 3 ; EEPROM Data Register bit 3 +.equ EEDR4 = 4 ; EEPROM Data Register bit 4 +.equ EEDR5 = 5 ; EEPROM Data Register bit 5 +.equ EEDR6 = 6 ; EEPROM Data Register bit 6 +.equ EEDR7 = 7 ; EEPROM Data Register bit 7 + +; EECR - EEPROM Control Register +.equ EERE = 0 ; EEPROM Read Enable +.equ EEWE = 1 ; EEPROM Write Enable +.equ EEMWE = 2 ; EEPROM Master Write Enable +.equ EEWEE = EEMWE ; For compatibility +.equ EERIE = 3 ; EEPROM Ready Interrupt Enable + +; SREG - Status Register +.equ SREG_C = 0 ; Carry Flag +.equ SREG_Z = 1 ; Zero Flag +.equ SREG_N = 2 ; Negative Flag +.equ SREG_V = 3 ; Two's Complement Overflow Flag +.equ SREG_S = 4 ; Sign Bit +.equ SREG_H = 5 ; Half Carry Flag +.equ SREG_T = 6 ; Bit Copy Storage +.equ SREG_I = 7 ; Global Interrupt Enable + +; MCUCR - MCU Control Register +;.equ ISC00 = 0 ; Interrupt Sense Control 0 Bit 0 +;.equ ISC01 = 1 ; Interrupt Sense Control 0 Bit 1 +;.equ ISC10 = 2 ; Interrupt Sense Control 1 Bit 0 +;.equ ISC11 = 3 ; Interrupt Sense Control 1 Bit 1 +.equ SM0 = 4 ; Sleep Mode Select +.equ SM1 = 5 ; Sleep Mode Select +.equ SE = 6 ; Sleep Enable +.equ SM2 = 7 ; Sleep Mode Select + +; MCUCSR - MCU Control And Status Register +.equ MCUSR = MCUCSR; For compatibility +.equ PORF = 0 ; Power-on reset flag +.equ EXTRF = 1 ; External Reset Flag +.equ EXTREF = EXTRF ; For compatibility +.equ BORF = 2 ; Brown-out Reset Flag +.equ WDRF = 3 ; Watchdog Reset Flag +.equ JTRF = 4 ; JTAG Reset Flag +.equ JTD = 7 ; JTAG Interface Disable + +; OSCCAL - Oscillator Calibration Value +.equ CAL0 = 0 ; Oscillator Calibration Value Bit0 +.equ CAL1 = 1 ; Oscillator Calibration Value Bit1 +.equ CAL2 = 2 ; Oscillator Calibration Value Bit2 +.equ CAL3 = 3 ; Oscillator Calibration Value Bit3 +.equ CAL4 = 4 ; Oscillator Calibration Value Bit4 +.equ CAL5 = 5 ; Oscillator Calibration Value Bit5 +.equ CAL6 = 6 ; Oscillator Calibration Value Bit6 +.equ CAL7 = 7 ; Oscillator Calibration Value Bit7 + +; SFIOR - Special function I/O register +;.equ PSR10 = 0 ; Prescaler reset +.equ PSR2 = 1 ; Prescaler reset +.equ PUD = 2 ; Pull-up Disable +.equ ADHSM = 3 ; ADC High Speed Mode +.equ ADTS0 = 5 ; ADC High Speed Mode +.equ ADTS1 = 6 ; ADC Auto Trigger Source +.equ ADTS2 = 7 ; ADC Auto Trigger Source + +; TCCR2 - Timer/Counter2 Control Register +.equ CS20 = 0 ; Clock Select bit 0 +.equ CS21 = 1 ; Clock Select bit 1 +.equ CS22 = 2 ; Clock Select bit 2 +.equ WGM21 = 3 ; Waveform Generation Mode +.equ CTC2 = WGM21 ; For compatibility +.equ COM20 = 4 ; Compare Output Mode bit 0 +.equ COM21 = 5 ; Compare Output Mode bit 1 +.equ WGM20 = 6 ; Waveform Genration Mode +.equ PWM2 = WGM20 ; For compatibility +.equ FOC2 = 7 ; Force Output Compare + +; TCNT2 - Timer/Counter2 +.equ TCNT2_0 = 0 ; Timer/Counter 2 bit 0 +.equ TCNT2_1 = 1 ; Timer/Counter 2 bit 1 +.equ TCNT2_2 = 2 ; Timer/Counter 2 bit 2 +.equ TCNT2_3 = 3 ; Timer/Counter 2 bit 3 +.equ TCNT2_4 = 4 ; Timer/Counter 2 bit 4 +.equ TCNT2_5 = 5 ; Timer/Counter 2 bit 5 +.equ TCNT2_6 = 6 ; Timer/Counter 2 bit 6 +.equ TCNT2_7 = 7 ; Timer/Counter 2 bit 7 + +; OCR2 - Timer/Counter2 Output Compare Register +.equ OCR2_0 = 0 ; Timer/Counter2 Output Compare Register Bit 0 +.equ OCR2_1 = 1 ; Timer/Counter2 Output Compare Register Bit 1 +.equ OCR2_2 = 2 ; Timer/Counter2 Output Compare Register Bit 2 +.equ OCR2_3 = 3 ; Timer/Counter2 Output Compare Register Bit 3 +.equ OCR2_4 = 4 ; Timer/Counter2 Output Compare Register Bit 4 +.equ OCR2_5 = 5 ; Timer/Counter2 Output Compare Register Bit 5 +.equ OCR2_6 = 6 ; Timer/Counter2 Output Compare Register Bit 6 +.equ OCR2_7 = 7 ; Timer/Counter2 Output Compare Register Bit 7 + +; ASSR - Asynchronous Status Register +.equ TCR2UB = 0 ; Timer/counter Control Register2 Update Busy +.equ OCR2UB = 1 ; Output Compare Register2 Update Busy +.equ TCN2UB = 2 ; Timer/Counter2 Update Busy +.equ AS2 = 3 ; Asynchronous Timer/counter2 + +; SFIOR - Special Function IO Register +;.equ PSR2 = 1 ; Prescaler Reset Timer/Counter2 + +; SPDR - SPI Data Register +.equ SPDR0 = 0 ; SPI Data Register bit 0 +.equ SPDR1 = 1 ; SPI Data Register bit 1 +.equ SPDR2 = 2 ; SPI Data Register bit 2 +.equ SPDR3 = 3 ; SPI Data Register bit 3 +.equ SPDR4 = 4 ; SPI Data Register bit 4 +.equ SPDR5 = 5 ; SPI Data Register bit 5 +.equ SPDR6 = 6 ; SPI Data Register bit 6 +.equ SPDR7 = 7 ; SPI Data Register bit 7 + +; SPSR - SPI Status Register +.equ SPI2X = 0 ; Double SPI Speed Bit +.equ WCOL = 6 ; Write Collision Flag +.equ SPIF = 7 ; SPI Interrupt Flag + +; SPCR - SPI Control Register +.equ SPR0 = 0 ; SPI Clock Rate Select 0 +.equ SPR1 = 1 ; SPI Clock Rate Select 1 +.equ CPHA = 2 ; Clock Phase +.equ CPOL = 3 ; Clock polarity +.equ MSTR = 4 ; Master/Slave Select +.equ DORD = 5 ; Data Order +.equ SPE = 6 ; SPI Enable +.equ SPIE = 7 ; SPI Interrupt Enable + +; UDR - USART I/O Data Register +.equ UDR0 = 0 ; USART I/O Data Register bit 0 +.equ UDR1 = 1 ; USART I/O Data Register bit 1 +.equ UDR2 = 2 ; USART I/O Data Register bit 2 +.equ UDR3 = 3 ; USART I/O Data Register bit 3 +.equ UDR4 = 4 ; USART I/O Data Register bit 4 +.equ UDR5 = 5 ; USART I/O Data Register bit 5 +.equ UDR6 = 6 ; USART I/O Data Register bit 6 +.equ UDR7 = 7 ; USART I/O Data Register bit 7 + +; UCSRA - USART Control and Status Register A +.equ USR = UCSRA ; For compatibility +.equ MPCM = 0 ; Multi-processor Communication Mode +.equ U2X = 1 ; Double the USART transmission speed +.equ UPE = 2 ; Parity Error +.equ PE = UPE ; For compatibility +.equ DOR = 3 ; Data overRun +.equ FE = 4 ; Framing Error +.equ UDRE = 5 ; USART Data Register Empty +.equ TXC = 6 ; USART Transmitt Complete +.equ RXC = 7 ; USART Receive Complete + +; UCSRB - USART Control and Status Register B +.equ UCR = UCSRB ; For compatibility +.equ TXB8 = 0 ; Transmit Data Bit 8 +.equ RXB8 = 1 ; Receive Data Bit 8 +.equ UCSZ2 = 2 ; Character Size +.equ CHR9 = UCSZ2 ; For compatibility +.equ TXEN = 3 ; Transmitter Enable +.equ RXEN = 4 ; Receiver Enable +.equ UDRIE = 5 ; USART Data register Empty Interrupt Enable +.equ TXCIE = 6 ; TX Complete Interrupt Enable +.equ RXCIE = 7 ; RX Complete Interrupt Enable + +; UCSRC - USART Control and Status Register C +.equ UCPOL = 0 ; Clock Polarity +.equ UCSZ0 = 1 ; Character Size +.equ UCSZ1 = 2 ; Character Size +.equ USBS = 3 ; Stop Bit Select +.equ UPM0 = 4 ; Parity Mode Bit 0 +.equ UPM1 = 5 ; Parity Mode Bit 1 +.equ UMSEL = 6 ; USART Mode Select +.equ URSEL = 7 ; Register Select + +.equ UBRRHI = UBRRH ; For compatibility + +; TWBR - TWI Bit Rate register +.equ I2BR = TWBR ; For compatibility +.equ TWBR0 = 0 ; +.equ TWBR1 = 1 ; +.equ TWBR2 = 2 ; +.equ TWBR3 = 3 ; +.equ TWBR4 = 4 ; +.equ TWBR5 = 5 ; +.equ TWBR6 = 6 ; +.equ TWBR7 = 7 ; + +; TWCR - TWI Control Register +.equ I2CR = TWCR ; For compatibility +.equ TWIE = 0 ; TWI Interrupt Enable +.equ I2IE = TWIE ; For compatibility +.equ TWEN = 2 ; TWI Enable Bit +.equ I2EN = TWEN ; For compatibility +.equ ENI2C = TWEN ; For compatibility +.equ TWWC = 3 ; TWI Write Collition Flag +.equ I2WC = TWWC ; For compatibility +.equ TWSTO = 4 ; TWI Stop Condition Bit +.equ I2STO = TWSTO ; For compatibility +.equ TWSTA = 5 ; TWI Start Condition Bit +.equ I2STA = TWSTA ; For compatibility +.equ TWEA = 6 ; TWI Enable Acknowledge Bit +.equ I2EA = TWEA ; For compatibility +.equ TWINT = 7 ; TWI Interrupt Flag +.equ I2INT = TWINT ; For compatibility + +; TWSR - TWI Status Register +.equ I2SR = TWSR ; For compatibility +.equ TWPS0 = 0 ; TWI Prescaler +.equ TWS0 = TWPS0 ; For compatibility +.equ I2GCE = TWPS0 ; For compatibility +.equ TWPS1 = 1 ; TWI Prescaler +.equ TWS1 = TWPS1 ; For compatibility +.equ TWS3 = 3 ; TWI Status +.equ I2S3 = TWS3 ; For compatibility +.equ TWS4 = 4 ; TWI Status +.equ I2S4 = TWS4 ; For compatibility +.equ TWS5 = 5 ; TWI Status +.equ I2S5 = TWS5 ; For compatibility +.equ TWS6 = 6 ; TWI Status +.equ I2S6 = TWS6 ; For compatibility +.equ TWS7 = 7 ; TWI Status +.equ I2S7 = TWS7 ; For compatibility + +; TWDR - TWI Data register +.equ I2DR = TWDR ; For compatibility +.equ TWD0 = 0 ; TWI Data Register Bit 0 +.equ TWD1 = 1 ; TWI Data Register Bit 1 +.equ TWD2 = 2 ; TWI Data Register Bit 2 +.equ TWD3 = 3 ; TWI Data Register Bit 3 +.equ TWD4 = 4 ; TWI Data Register Bit 4 +.equ TWD5 = 5 ; TWI Data Register Bit 5 +.equ TWD6 = 6 ; TWI Data Register Bit 6 +.equ TWD7 = 7 ; TWI Data Register Bit 7 + +; TWAR - TWI (Slave) Address register +.equ I2AR = TWAR ; For compatibility +.equ TWGCE = 0 ; TWI General Call Recognition Enable Bit +.equ TWA0 = 1 ; TWI (Slave) Address register Bit 0 +.equ TWA1 = 2 ; TWI (Slave) Address register Bit 1 +.equ TWA2 = 3 ; TWI (Slave) Address register Bit 2 +.equ TWA3 = 4 ; TWI (Slave) Address register Bit 3 +.equ TWA4 = 5 ; TWI (Slave) Address register Bit 4 +.equ TWA5 = 6 ; TWI (Slave) Address register Bit 5 +.equ TWA6 = 7 ; TWI (Slave) Address register Bit 6 + +; SFIOR - Special Function IO Register +.equ ACME = 3 ; Analog Comparator Multiplexer Enable + +; ACSR - Analog Comparator Control And Status Register +.equ ACIS0 = 0 ; Analog Comparator Interrupt Mode Select bit 0 +.equ ACIS1 = 1 ; Analog Comparator Interrupt Mode Select bit 1 +.equ ACIC = 2 ; Analog Comparator Input Capture Enable +.equ ACIE = 3 ; Analog Comparator Interrupt Enable +.equ ACI = 4 ; Analog Comparator Interrupt Flag +.equ ACO = 5 ; Analog Compare Output +.equ ACBG = 6 ; Analog Comparator Bandgap Select +.equ ACD = 7 ; Analog Comparator Disable + +; ADMUX - The ADC multiplexer Selection Register +.equ MUX0 = 0 ; Analog Channel and Gain Selection Bits +.equ MUX1 = 1 ; Analog Channel and Gain Selection Bits +.equ MUX2 = 2 ; Analog Channel and Gain Selection Bits +.equ MUX3 = 3 ; Analog Channel and Gain Selection Bits +.equ MUX4 = 4 ; Analog Channel and Gain Selection Bits +.equ ADLAR = 5 ; Left Adjust Result +.equ REFS0 = 6 ; Reference Selection Bit 0 +.equ REFS1 = 7 ; Reference Selection Bit 1 + +; ADCSRA - The ADC Control and Status register +.equ ADPS0 = 0 ; ADC Prescaler Select Bits +.equ ADPS1 = 1 ; ADC Prescaler Select Bits +.equ ADPS2 = 2 ; ADC Prescaler Select Bits +.equ ADIE = 3 ; ADC Interrupt Enable +.equ ADIF = 4 ; ADC Interrupt Flag +.equ ADATE = 5 ; When this bit is written to one,the Timer/Counter2 prescaler will be reset.The bit will be cleared by hardware after the operation is performed.Writing a zero to this bit will have no effect.This bit will always be read as zero if Timer/Counter2 is clocked by the internal CPU clock.If this bit is written when Timer/Counter2 is operating in asynchronous mode,the bit will remain one until the prescaler has been reset. +.equ ADFR = ADATE ; For compatibility +.equ ADSC = 6 ; ADC Start Conversion +.equ ADEN = 7 ; ADC Enable + +; ADCH - ADC Data Register High Byte +.equ ADCH0 = 0 ; ADC Data Register High Byte Bit 0 +.equ ADCH1 = 1 ; ADC Data Register High Byte Bit 1 +.equ ADCH2 = 2 ; ADC Data Register High Byte Bit 2 +.equ ADCH3 = 3 ; ADC Data Register High Byte Bit 3 +.equ ADCH4 = 4 ; ADC Data Register High Byte Bit 4 +.equ ADCH5 = 5 ; ADC Data Register High Byte Bit 5 +.equ ADCH6 = 6 ; ADC Data Register High Byte Bit 6 +.equ ADCH7 = 7 ; ADC Data Register High Byte Bit 7 + +; ADCL - ADC Data Register Low Byte +.equ ADCL0 = 0 ; ADC Data Register Low Byte Bit 0 +.equ ADCL1 = 1 ; ADC Data Register Low Byte Bit 1 +.equ ADCL2 = 2 ; ADC Data Register Low Byte Bit 2 +.equ ADCL3 = 3 ; ADC Data Register Low Byte Bit 3 +.equ ADCL4 = 4 ; ADC Data Register Low Byte Bit 4 +.equ ADCL5 = 5 ; ADC Data Register Low Byte Bit 5 +.equ ADCL6 = 6 ; ADC Data Register Low Byte Bit 6 +.equ ADCL7 = 7 ; ADC Data Register Low Byte Bit 7 + +; OCDR - On-Chip Debug Related Register in I/O Memory +.equ OCDR0 = 0 ; On-Chip Debug Register Bit 0 +.equ OCDR1 = 1 ; On-Chip Debug Register Bit 1 +.equ OCDR2 = 2 ; On-Chip Debug Register Bit 2 +.equ OCDR3 = 3 ; On-Chip Debug Register Bit 3 +.equ OCDR4 = 4 ; On-Chip Debug Register Bit 4 +.equ OCDR5 = 5 ; On-Chip Debug Register Bit 5 +.equ OCDR6 = 6 ; On-Chip Debug Register Bit 6 +.equ OCDR7 = 7 ; On-Chip Debug Register Bit 7 +.equ IDRD = OCDR7 ; For compatibility + +; MCUCSR - MCU Control And Status Register +;.equ JTRF = 4 ; JTAG Reset Flag +;.equ JTD = 7 ; JTAG Interface Disable + +; SPMCSR - Store Program Memory Control Register +.equ SPMCR = SPMCSR ; For compatibility +.equ SPMEN = 0 ; Store Program Memory Enable +.equ PGERS = 1 ; Page Erase +.equ PGWRT = 2 ; Page Write +.equ BLBSET = 3 ; Boot Lock Bit Set +.equ RWWSRE = 4 ; Read While Write section read enable +.equ ASRE = RWWSRE ; For compatibility +.equ RWWSB = 6 ; Read While Write Section Busy +.equ ASB = RWWSB ; For compatibility +.equ SPMIE = 7 ; SPM Interrupt Enable + +; PORTA - Port A Data Register +.equ PORTA0 = 0 ; Port A Data Register bit 0 +.equ PA0 = 0 ; For compatibility +.equ PORTA1 = 1 ; Port A Data Register bit 1 +.equ PA1 = 1 ; For compatibility +.equ PORTA2 = 2 ; Port A Data Register bit 2 +.equ PA2 = 2 ; For compatibility +.equ PORTA3 = 3 ; Port A Data Register bit 3 +.equ PA3 = 3 ; For compatibility +.equ PORTA4 = 4 ; Port A Data Register bit 4 +.equ PA4 = 4 ; For compatibility +.equ PORTA5 = 5 ; Port A Data Register bit 5 +.equ PA5 = 5 ; For compatibility +.equ PORTA6 = 6 ; Port A Data Register bit 6 +.equ PA6 = 6 ; For compatibility +.equ PORTA7 = 7 ; Port A Data Register bit 7 +.equ PA7 = 7 ; For compatibility + +; DDRA - Port A Data Direction Register +.equ DDA0 = 0 ; Data Direction Register, Port A, bit 0 +.equ DDA1 = 1 ; Data Direction Register, Port A, bit 1 +.equ DDA2 = 2 ; Data Direction Register, Port A, bit 2 +.equ DDA3 = 3 ; Data Direction Register, Port A, bit 3 +.equ DDA4 = 4 ; Data Direction Register, Port A, bit 4 +.equ DDA5 = 5 ; Data Direction Register, Port A, bit 5 +.equ DDA6 = 6 ; Data Direction Register, Port A, bit 6 +.equ DDA7 = 7 ; Data Direction Register, Port A, bit 7 + +; PINA - Port A Input Pins +.equ PINA0 = 0 ; Input Pins, Port A bit 0 +.equ PINA1 = 1 ; Input Pins, Port A bit 1 +.equ PINA2 = 2 ; Input Pins, Port A bit 2 +.equ PINA3 = 3 ; Input Pins, Port A bit 3 +.equ PINA4 = 4 ; Input Pins, Port A bit 4 +.equ PINA5 = 5 ; Input Pins, Port A bit 5 +.equ PINA6 = 6 ; Input Pins, Port A bit 6 +.equ PINA7 = 7 ; Input Pins, Port A bit 7 + +; PORTB - Port B Data Register +.equ PORTB0 = 0 ; Port B Data Register bit 0 +.equ PB0 = 0 ; For compatibility +.equ PORTB1 = 1 ; Port B Data Register bit 1 +.equ PB1 = 1 ; For compatibility +.equ PORTB2 = 2 ; Port B Data Register bit 2 +.equ PB2 = 2 ; For compatibility +.equ PORTB3 = 3 ; Port B Data Register bit 3 +.equ PB3 = 3 ; For compatibility +.equ PORTB4 = 4 ; Port B Data Register bit 4 +.equ PB4 = 4 ; For compatibility +.equ PORTB5 = 5 ; Port B Data Register bit 5 +.equ PB5 = 5 ; For compatibility +.equ PORTB6 = 6 ; Port B Data Register bit 6 +.equ PB6 = 6 ; For compatibility +.equ PORTB7 = 7 ; Port B Data Register bit 7 +.equ PB7 = 7 ; For compatibility + +; DDRB - Port B Data Direction Register +.equ DDB0 = 0 ; Port B Data Direction Register bit 0 +.equ DDB1 = 1 ; Port B Data Direction Register bit 1 +.equ DDB2 = 2 ; Port B Data Direction Register bit 2 +.equ DDB3 = 3 ; Port B Data Direction Register bit 3 +.equ DDB4 = 4 ; Port B Data Direction Register bit 4 +.equ DDB5 = 5 ; Port B Data Direction Register bit 5 +.equ DDB6 = 6 ; Port B Data Direction Register bit 6 +.equ DDB7 = 7 ; Port B Data Direction Register bit 7 + +; PINB - Port B Input Pins +.equ PINB0 = 0 ; Port B Input Pins bit 0 +.equ PINB1 = 1 ; Port B Input Pins bit 1 +.equ PINB2 = 2 ; Port B Input Pins bit 2 +.equ PINB3 = 3 ; Port B Input Pins bit 3 +.equ PINB4 = 4 ; Port B Input Pins bit 4 +.equ PINB5 = 5 ; Port B Input Pins bit 5 +.equ PINB6 = 6 ; Port B Input Pins bit 6 +.equ PINB7 = 7 ; Port B Input Pins bit 7 + +; PORTC - Port C Data Register +.equ PORTC0 = 0 ; Port C Data Register bit 0 +.equ PC0 = 0 ; For compatibility +.equ PORTC1 = 1 ; Port C Data Register bit 1 +.equ PC1 = 1 ; For compatibility +.equ PORTC2 = 2 ; Port C Data Register bit 2 +.equ PC2 = 2 ; For compatibility +.equ PORTC3 = 3 ; Port C Data Register bit 3 +.equ PC3 = 3 ; For compatibility +.equ PORTC4 = 4 ; Port C Data Register bit 4 +.equ PC4 = 4 ; For compatibility +.equ PORTC5 = 5 ; Port C Data Register bit 5 +.equ PC5 = 5 ; For compatibility +.equ PORTC6 = 6 ; Port C Data Register bit 6 +.equ PC6 = 6 ; For compatibility +.equ PORTC7 = 7 ; Port C Data Register bit 7 +.equ PC7 = 7 ; For compatibility + +; DDRC - Port C Data Direction Register +.equ DDC0 = 0 ; Port C Data Direction Register bit 0 +.equ DDC1 = 1 ; Port C Data Direction Register bit 1 +.equ DDC2 = 2 ; Port C Data Direction Register bit 2 +.equ DDC3 = 3 ; Port C Data Direction Register bit 3 +.equ DDC4 = 4 ; Port C Data Direction Register bit 4 +.equ DDC5 = 5 ; Port C Data Direction Register bit 5 +.equ DDC6 = 6 ; Port C Data Direction Register bit 6 +.equ DDC7 = 7 ; Port C Data Direction Register bit 7 + +; PINC - Port C Input Pins +.equ PINC0 = 0 ; Port C Input Pins bit 0 +.equ PINC1 = 1 ; Port C Input Pins bit 1 +.equ PINC2 = 2 ; Port C Input Pins bit 2 +.equ PINC3 = 3 ; Port C Input Pins bit 3 +.equ PINC4 = 4 ; Port C Input Pins bit 4 +.equ PINC5 = 5 ; Port C Input Pins bit 5 +.equ PINC6 = 6 ; Port C Input Pins bit 6 +.equ PINC7 = 7 ; Port C Input Pins bit 7 + +; PORTD - Port D Data Register +.equ PORTD0 = 0 ; Port D Data Register bit 0 +.equ PD0 = 0 ; For compatibility +.equ PORTD1 = 1 ; Port D Data Register bit 1 +.equ PD1 = 1 ; For compatibility +.equ PORTD2 = 2 ; Port D Data Register bit 2 +.equ PD2 = 2 ; For compatibility +.equ PORTD3 = 3 ; Port D Data Register bit 3 +.equ PD3 = 3 ; For compatibility +.equ PORTD4 = 4 ; Port D Data Register bit 4 +.equ PD4 = 4 ; For compatibility +.equ PORTD5 = 5 ; Port D Data Register bit 5 +.equ PD5 = 5 ; For compatibility +.equ PORTD6 = 6 ; Port D Data Register bit 6 +.equ PD6 = 6 ; For compatibility +.equ PORTD7 = 7 ; Port D Data Register bit 7 +.equ PD7 = 7 ; For compatibility + +; DDRD - Port D Data Direction Register +.equ DDD0 = 0 ; Port D Data Direction Register bit 0 +.equ DDD1 = 1 ; Port D Data Direction Register bit 1 +.equ DDD2 = 2 ; Port D Data Direction Register bit 2 +.equ DDD3 = 3 ; Port D Data Direction Register bit 3 +.equ DDD4 = 4 ; Port D Data Direction Register bit 4 +.equ DDD5 = 5 ; Port D Data Direction Register bit 5 +.equ DDD6 = 6 ; Port D Data Direction Register bit 6 +.equ DDD7 = 7 ; Port D Data Direction Register bit 7 + +; PIND - Port D Input Pins +.equ PIND0 = 0 ; Port D Input Pins bit 0 +.equ PIND1 = 1 ; Port D Input Pins bit 1 +.equ PIND2 = 2 ; Port D Input Pins bit 2 +.equ PIND3 = 3 ; Port D Input Pins bit 3 +.equ PIND4 = 4 ; Port D Input Pins bit 4 +.equ PIND5 = 5 ; Port D Input Pins bit 5 +.equ PIND6 = 6 ; Port D Input Pins bit 6 +.equ PIND7 = 7 ; Port D Input Pins bit 7 + +; WDTCR - Watchdog Timer Control Register +.equ WDP0 = 0 ; Watch Dog Timer Prescaler bit 0 +.equ WDP1 = 1 ; Watch Dog Timer Prescaler bit 1 +.equ WDP2 = 2 ; Watch Dog Timer Prescaler bit 2 +.equ WDE = 3 ; Watch Dog Enable +.equ WDTOE = 4 ; RW +.equ WDDE = WDTOE ; For compatibility + +; Locks Bits +.equ LB1 = 0 ; Lock bit +.equ LB2 = 1 ; Lock bit +.equ BLB01 = 2 ; Boot Lock bit +.equ BLB02 = 3 ; Boot Lock bit +.equ BLB11 = 4 ; Boot lock bit +.equ BLB12 = 5 ; Boot lock bit + +; Low Fuse Bits +.equ CKSEL0 = 0 ; Select Clock Source +.equ CKSEL1 = 1 ; Select Clock Source +.equ CKSEL2 = 2 ; Select Clock Source +.equ CKSEL3 = 3 ; Select Clock Source +.equ SUT0 = 4 ; Select start-up time +.equ SUT1 = 5 ; Select start-up time +.equ BODEN = 6 ; Brown out detector enable +.equ BODLEVEL= 7 ; Brown out detector trigger level + +; High Fuse Bits +.equ BOOTRST = 0 ; Select Reset Vector +.equ BOOTSZ0 = 1 ; Select Boot Size +.equ BOOTSZ1 = 2 ; Select Boot Size +.equ EESAVE = 3 ; EEPROM memory is preserved through chip erase +.equ CKOPT = 4 ; Oscillator Options +.equ SPIEN = 5 ; Enable Serial programming and Data Downloading +.equ JTAGEN = 6 ; Enable JTAG +.equ OCDEN = 7 ; Enable OCD + +; Data Memory +.equ FLASHEND = 0x1fff ; Note: Word address +.equ IOEND = 0x003f +.equ SRAM_START = 0x0060 +.equ SRAM_SIZE = 1024 +.equ RAMEND = 0x045f +.equ XRAMEND = 0x0000 +.equ E2END = 0x01ff +.equ EEPROMEND = 0x01ff +.equ EEADRBITS = 9 + +; Bootloader +.equ NRWW_START_ADDR = 0x1c00 +.equ NRWW_STOP_ADDR = 0x1fff +.equ RWW_START_ADDR = 0x0 +.equ RWW_STOP_ADDR = 0x1bff +.equ PAGESIZE = 64 +.equ FIRSTBOOTSTART = 0x1f80 +.equ SECONDBOOTSTART = 0x1f00 +.equ THIRDBOOTSTART = 0x1e00 +.equ FOURTHBOOTSTART = 0x1c00 +.equ SMALLBOOTSTART = FIRSTBOOTSTART +.equ LARGEBOOTSTART = FOURTHBOOTSTART + +; Interrupt Vectors +.equ INT0addr = 0x0002 ; External Interrupt Request 0 +.equ INT1addr = 0x0004 ; External Interrupt Request 1 +.equ OC2addr = 0x0006 ; Timer/Counter2 Compare Match +.equ OVF2addr = 0x0008 ; Timer/Counter2 Overflow +.equ ICP1addr = 0x000a ; Timer/Counter1 Capture Event +.equ OC1Aaddr = 0x000c ; Timer/Counter1 Compare Match A +.equ OC1Baddr = 0x000e ; Timer/Counter1 Compare Match B +.equ OVF1addr = 0x0010 ; Timer/Counter1 Overflow +.equ OVF0addr = 0x0012 ; Timer/Counter0 Overflow +.equ SPIaddr = 0x0014 ; Serial Transfer Complete +.equ URXCaddr = 0x0016 ; USART, Rx Complete +.equ UDREaddr = 0x0018 ; USART Data Register Empty +.equ UTXCaddr = 0x001a ; USART, Tx Complete +.equ ADCCaddr = 0x001c ; ADC Conversion Complete +.equ ERDYaddr = 0x001e ; EEPROM Ready +.equ ACIaddr = 0x0020 ; Analog Comparator +.equ TWIaddr = 0x0022 ; 2-wire Serial Interface +.equ INT2addr = 0x0024 ; External Interrupt Request 2 +.equ OC0addr = 0x0026 ; Timer/Counter0 Compare Match +.equ SPMRaddr = 0x0028 ; Store Program Memory Ready + +.equ INT_VECTORS_SIZE = 42 ; size in words + diff --git a/x86_64_sse2_x87/fasm/examples/avr/make.cmd b/x86_64_sse2_x87/fasm/examples/avr/make.cmd new file mode 100644 index 0000000..841364f --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/avr/make.cmd @@ -0,0 +1 @@ +fasmg counter.asm counter.hex diff --git a/x86_64_sse2_x87/fasm/examples/jvm/Test.asm b/x86_64_sse2_x87/fasm/examples/jvm/Test.asm new file mode 100644 index 0000000..9efe645 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/jvm/Test.asm @@ -0,0 +1,134 @@ + +; This example uses a very simple set of macroinstructions to generate +; the basic structures of JVM class file. +; Please refer to "The Java Virtual Machine Specification" for the detailed +; information on this file format. + +include 'jclass.inc' + +format binary as 'class' + + u4 0xcafebabe ; magic + u2 0,49 ; minor and major version + + constant_pool + + _Code constant_utf8 'Code' + _init constant_utf8 '' + _main constant_utf8 'main' + _void_arrstr constant_utf8 '([Ljava/lang/String;)V' + Test_class constant_class _Test + _Test constant_utf8 'Test' + + Object_init constant_methodref Object_class,init_method + Object_class constant_class _Object + _Object constant_utf8 'java/lang/Object' + init_method constant_nameandtype _init,_void + _void constant_utf8 '()V' + + System.out constant_fieldref System_class,out_field + System_class constant_class _System + _System constant_utf8 'java/lang/System' + out_field constant_nameandtype _out,PrintStream_type + _out constant_utf8 'out' + PrintStream_type constant_utf8 'Ljava/io/PrintStream;' + + PrintStream_println constant_methodref PrintStream_class,println_method + PrintStream_class constant_class _PrintStream + _PrintStream constant_utf8 'java/io/PrintStream' + println_method constant_nameandtype _println,_void_str + _println constant_utf8 'println' + _void_str constant_utf8 '(Ljava/lang/String;)V' + + Integer_toString constant_methodref Integer_class,toString_method + Integer_class constant_class _Integer + _Integer constant_utf8 'java/lang/Integer' + toString_method constant_nameandtype _toString,_str_int + _toString constant_utf8 'toString' + _str_int constant_utf8 '(I)Ljava/lang/String;' + + number constant_integer 17 + + end constant_pool + + u2 ACC_PUBLIC+ACC_SUPER ; access flags + u2 Test_class ; this class + u2 Object_class ; super class + + interfaces + + end interfaces + + fields + + end fields + + methods + + method_info ACC_PUBLIC, _init, _void ; public void Test() + + attribute _Code + + u2 1 ; max_stack + u2 1 ; max_locals + + bytecode + + aload 0 + invokespecial Object_init + return + + end bytecode + + exceptions + end exceptions + + attributes + end attributes + + end attribute + + end method_info + + method_info ACC_PUBLIC+ACC_STATIC, _main, _void_arrstr ; public static void main(String[] args) + + attribute _Code + + u2 3 ; max_stack + u2 2 ; max_locals + + bytecode + + ldc number + istore 1 + example_loop: + iload 1 + dup + imul + invokestatic Integer_toString + getstatic System.out + swap + invokevirtual PrintStream_println + iinc 1,-1 + iload 1 + ifne example_loop + + return + + end bytecode + + exceptions + end exceptions + + attributes + end attributes + + end attribute + + end method_info + + end methods + + attributes + + end attributes diff --git a/x86_64_sse2_x87/fasm/examples/jvm/bytecode.inc b/x86_64_sse2_x87/fasm/examples/jvm/bytecode.inc new file mode 100644 index 0000000..71d5316 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/jvm/bytecode.inc @@ -0,0 +1,797 @@ + +T_BOOLEAN = 4 +T_CHAR = 5 +T_FLOAT = 6 +T_DOUBLE = 7 +T_BYTE = 8 +T_SHORT = 9 +T_INT = 10 +T_LONG = 11 + +macro aaload + db 0x32 +end macro + +macro aastore + db 0x53 +end macro + +macro aconst_null + db 0x01 +end macro + +macro aload index + if index<0 + err "invalid index" + else if index<=3 + db 0x2a+index + else if index<100h + db 0x19,index + else + db 0xc4,0x19,(index) shr 8,(index) and 0FFh + end if +end macro + +macro anewarray class + db 0xbd,(class) shr 8,(class) and 0FFh +end macro + +macro areturn + db 0xb0 +end macro + +macro arraylength + db 0xbe +end macro + +macro astore index + if index<0 + err "invalid index" + else if index<=3 + db 0x4b+index + else if index<100h + db 0x3a,index + else + db 0xc4,0x3a,(index) shr 8,(index) and 0FFh + end if +end macro + +macro athrow + db 0xbf +end macro + +macro baload + db 0x33 +end macro + +macro bastore + db 0x54 +end macro + +macro bipush byte + if byte>-1 & byte<=5 + db 0x03+byte + else + db 0x10,byte + end if +end macro + +macro caload + db 0x34 +end macro + +macro castore + db 0x55 +end macro + +macro checkcast class + db 0xc0,(class) shr 8,(class) and 0FFh +end macro + +macro d2f + db 0x90 +end macro + +macro d2i + db 0x8e +end macro + +macro d2l + db 0x8f +end macro + +macro dadd + db 0x63 +end macro + +macro daload + db 0x31 +end macro + +macro dastore + db 0x52 +end macro + +macro dcmpg + db 0x98 +end macro + +macro dcmpl + db 0x97 +end macro + +macro dconst_0 + db 0x0e +end macro + +macro dconst_1 + db 0x0f +end macro + +macro ddiv + db 0x6f +end macro + +macro dload index + if index<0 + err "invalid index" + else if index<=3 + db 0x26+index + else if index<100h + db 0x18,index + else + db 0xc4,0x18,(index) shr 8,(index) and 0FFh + end if +end macro + +macro dmul + db 0x6b +end macro + +macro dneg + db 0x77 +end macro + +macro drem + db 0x73 +end macro + +macro dreturn + db 0xaf +end macro + +macro dstore index + if index<0 + err "invalid index" + else if index<=3 + db 0x47+index + else if index<100h + db 0x39,index + else + db 0xc4,0x39,(index) shr 8,(index) and 0FFh + end if +end macro + +macro dsub + db 0x67 +end macro + +macro dup + db 0x59 +end macro + +macro dup_x1 + db 0x5a +end macro + +macro dup_x2 + db 0x5b +end macro + +macro dup2 + db 0x5c +end macro + +macro dup2_x1 + db 0x5d +end macro + +macro dup2_x2 + db 0x5e +end macro + +macro f2d + db 0x8d +end macro + +macro f2i + db 0x8b +end macro + +macro f2l + db 0x8c +end macro + +macro fadd + db 0x62 +end macro + +macro faload + db 0x30 +end macro + +macro fastore + db 0x51 +end macro + +macro fcmpg + db 0x96 +end macro + +macro fcmpl + db 0x95 +end macro + +macro fconst_0 + db 0x0b +end macro + +macro fconst_1 + db 0x0c +end macro + +macro fconst_2 + db 0x0d +end macro + +macro fdiv + db 0x6e +end macro + +macro fload index + if index<0 + err "invalid index" + else if index<=3 + db 0x22+index + else if index<100h + db 0x17,index + else + db 0xc4,0x17,(index) shr 8,(index) and 0FFh + end if +end macro + +macro fmul + db 0x6a +end macro + +macro fneg + db 0x76 +end macro + +macro frem + db 0x72 +end macro + +macro freturn + db 0xae +end macro + +macro fstore index + if index<0 + err "invalid index" + else if index<=3 + db 0x43+index + else if index<100h + db 0x38,index + else + db 0xc4,0x38,(index) shr 8,(index) and 0FFh + end if +end macro + +macro fsub + db 0x66 +end macro + +macro getfield index + db 0xb4,(index) shr 8,(index) and 0FFh +end macro + +macro getstatic index + db 0xb2,(index) shr 8,(index) and 0FFh +end macro + +macro goto branch + offset = branch-$ + if offset>=-8000h & offset<8000h + db 0xa7,offset shr 8,offset and 0FFh + else + db 0xc8,offset shr 24,(offset shr 16) and 0FFh,(offset shr 8) and 0FFh,offset and 0FFh + end if +end macro + +macro goto_w branch + offset = branch-$ + db 0xc8,offset shr 24,(offset shr 16) and 0FFh,(offset shr 8) and 0FFh,offset and 0FFh +end macro + +macro i2b + db 0x91 +end macro + +macro i2c + db 0x92 +end macro + +macro i2d + db 0x87 +end macro + +macro i2f + db 0x86 +end macro + +macro i2l + db 0x85 +end macro + +macro i2s + db 0x93 +end macro + +macro iadd + db 0x60 +end macro + +macro iaload + db 0x2e +end macro + +macro iand + db 0x7e +end macro + +macro iastore + db 0x4f +end macro + +macro iconst_m1 + db 0x02 +end macro + +macro iconst_0 + db 0x03 +end macro + +macro iconst_1 + db 0x04 +end macro + +macro iconst_2 + db 0x05 +end macro + +macro iconst_3 + db 0x06 +end macro + +macro iconst_4 + db 0x07 +end macro + +macro iconst_5 + db 0x08 +end macro + +macro idiv + db 0x6c +end macro + +macro if_acmpeq branch + offset = branch-$ + db 0xa5,offset shr 8,offset and 0FFh +end macro + +macro if_acmpne branch + offset = branch-$ + db 0xa6,offset shr 8,offset and 0FFh +end macro + +macro if_icmpeq branch + offset = branch-$ + db 0x9f,offset shr 8,offset and 0FFh +end macro + +macro if_icmpne branch + offset = branch-$ + db 0xa0,offset shr 8,offset and 0FFh +end macro + +macro if_icmplt branch + offset = branch-$ + db 0xa1,offset shr 8,offset and 0FFh +end macro + +macro if_icmpge branch + offset = branch-$ + db 0xa2,offset shr 8,offset and 0FFh +end macro + +macro if_icmpgt branch + offset = branch-$ + db 0xa3,offset shr 8,offset and 0FFh +end macro + +macro if_icmple branch + offset = branch-$ + db 0xa4,offset shr 8,offset and 0FFh +end macro + +macro ifeq branch + offset = branch-$ + db 0x99,offset shr 8,offset and 0FFh +end macro + +macro ifne branch + offset = branch-$ + db 0x9a,offset shr 8,offset and 0FFh +end macro + +macro iflt branch + offset = branch-$ + db 0x9b,offset shr 8,offset and 0FFh +end macro + +macro ifge branch + offset = branch-$ + db 0x9c,offset shr 8,offset and 0FFh +end macro + +macro ifgt branch + offset = branch-$ + db 0x9d,offset shr 8,offset and 0FFh +end macro + +macro ifle branch + offset = branch-$ + db 0x9e,offset shr 8,offset and 0FFh +end macro + +macro ifnonnull branch + offset = branch-$ + db 0xc7,offset shr 8,offset and 0FFh +end macro + +macro ifnull branch + offset = branch-$ + db 0xc6,offset shr 8,offset and 0FFh +end macro + +macro iinc index,const + if index<0 + err "invalid index" + else if index<100h & const<80h & const>=-80h + db 0x84,index,const + else + db 0xc4,0x84,(index) shr 8,(index) and 0FFh,(const) shr 8,(const) and 0FFh + end if +end macro + +macro iload index + if index<0 + err "invalid index" + else if index<=3 + db 0x1a+index + else if index<100h + db 0x15,index + else + db 0xc4,0x15,(index) shr 8,(index) and 0FFh + end if +end macro + +macro imul + db 0x68 +end macro + +macro ineg + db 0x74 +end macro + +macro instanceof index + db 0xc1,(index) shr 8,(index) and 0FFh +end macro + +macro invokedynamic index + db 0xba,(index) shr 8,(index) and 0FFh,0,0 +end macro + +macro invokeinterface index,count + db 0xb9,(index) shr 8,(index) and 0FFh,count +end macro + +macro invokespecial index + db 0xb7,(index) shr 8,(index) and 0FFh +end macro + +macro invokestatic index + db 0xb8,(index) shr 8,(index) and 0FFh +end macro + +macro invokevirtual index + db 0xb6,(index) shr 8,(index) and 0FFh +end macro + +macro ior + db 0x80 +end macro + +macro irem + db 0x70 +end macro + +macro ireturn + db 0xac +end macro + +macro ishl + db 0x78 +end macro + +macro ishr + db 0x7a +end macro + +macro istore index + if index<0 + err "invalid index" + else if index<=3 + db 0x3b+index + else if index<100h + db 0x36,index + else + db 0xc4,0x36,(index) shr 8,(index) and 0FFh + end if +end macro + +macro isub + db 0x64 +end macro + +macro iushr + db 0x7c +end macro + +macro ixor + db 0x82 +end macro + +macro jsr branch + offset = branch-$ + if offset>=-8000h & offset<8000h + db 0xa8,offset shr 8,offset and 0FFh + else + db 0xc9,offset shr 24,(offset shr 16) and 0FFh,(offset shr 8) and 0FFh,offset and 0FFh + end if +end macro + +macro jsr_w branch + offset = branch-$ + db 0xc9,offset shr 24,(offset shr 16) and 0FFh,(offset shr 8) and 0FFh,offset and 0FFh +end macro + +macro l2d + db 0x8a +end macro + +macro l2f + db 0x89 +end macro + +macro l2i + db 0x88 +end macro + +macro ladd + db 0x61 +end macro + +macro laload + db 0x2f +end macro + +macro land + db 0x7f +end macro + +macro lastore + db 0x50 +end macro + +macro lcmp + db 0x94 +end macro + +macro lconst_0 + db 0x09 +end macro + +macro lconst_1 + db 0x0a +end macro + +macro ldc index + if index<0 + err "invalid index" + else if index<100h + db 0x12,index + else + db 0x13,(index) shr 8,(index) and 0FFh + end if +end macro + +macro ldc_w index + db 0x13,(index) shr 8,(index) and 0FFh +end macro + +macro ldc2_w index + db 0x14,(index) shr 8,(index) and 0FFh +end macro + +macro ldiv + db 0x6d +end macro + +macro lload index + if index<0 + err "invalid index" + else if index<=3 + db 0x1e+index + else if index<100h + db 0x16,index + else + db 0xc4,0x16,(index) shr 8,(index) and 0FFh + end if +end macro + +macro lmul + db 0x69 +end macro + +macro lneg + db 0x75 +end macro + +macro lookupswitch +; db 0xab,... + err "not implemented yet" +end macro + +macro lor + db 0x81 +end macro + +macro lrem + db 0x71 +end macro + +macro lreturn + db 0xad +end macro + +macro lshl + db 0x79 +end macro + +macro lshr + db 0x7b +end macro + +macro lstore index + if index<0 + err "invalid index" + else if index<=3 + db 0x3f+index + else if index<100h + db 0x37,index + else + db 0xc4,0x37,(index) shr 8,(index) and 0FFh + end if +end macro + +macro lsub + db 0x65 +end macro + +macro lushr + db 0x7d +end macro + +macro lxor + db 0x83 +end macro + +macro monitorenter + db 0xc2 +end macro + +macro monitorexit + db 0xc3 +end macro + +macro multianewarray index,dimensions + db 0xc5,(index) shr 8,(index) and 0FFh,dimensions +end macro + +macro new index + db 0xbb,(index) shr 8,(index) and 0FFh +end macro + +macro newarray atype + db 0xbc,atype +end macro + +macro nop + db 0x00 +end macro + +macro pop + db 0x57 +end macro + +macro pop2 + db 0x58 +end macro + +macro putfield index + db 0xb5,(index) shr 8,(index) and 0FFh +end macro + +macro putstatic index + db 0xb3,(index) shr 8,(index) and 0FFh +end macro + +macro ret index + if index<0 + err "invalid index" + else if index<100h + db 0xa9,index + else + db 0xc4,0xa9,(index) shr 8,(index) and 0FFh + end if +end macro + +macro return + db 0xb1 +end macro + +macro saload + db 0x35 +end macro + +macro sastore + db 0x56 +end macro + +macro sipush short + db 0x11,(short) shr 8,(short) and 0FFh +end macro + +macro swap + db 0x5f +end macro + +macro tableswitch +; db 0xaa,... + err "not implemented yet" +end macro + +macro breakpoint + db 0xca +end macro + +macro impdep1 + db 0xfe +end macro + +macro impdep2 + db 0xff +end macro diff --git a/x86_64_sse2_x87/fasm/examples/jvm/jclass.inc b/x86_64_sse2_x87/fasm/examples/jvm/jclass.inc new file mode 100644 index 0000000..a38d488 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/jvm/jclass.inc @@ -0,0 +1,254 @@ + +ACC_PUBLIC = 0x0001 +ACC_PRIVATE = 0x0002 +ACC_PROTECTED = 0x0004 +ACC_STATIC = 0x0008 +ACC_FINAL = 0x0010 +ACC_SUPER = 0x0020 +ACC_SYNCHRONIZED = 0x0020 +ACC_NATIVE = 0x0200 +ACC_INTERFACE = 0x0200 +ACC_ABSTRACT = 0x0400 +ACC_STRICT = 0x0800 + +macro u1 values& + irp v,values + db v + end irp +end macro + +macro u2 values& + irp v,values + db (v) bswap 2 + end irp +end macro + +macro u4 values& + irp v,values + db (v) bswap 4 + end irp +end macro + +macro constant_pool + + u2 constant_pool_count + constant_pool_counter = 1 + + struc constant_utf8 string& + . = constant_pool_counter + constant_pool_counter = constant_pool_counter + 1 + local data,length + u1 1 + u2 length + data: db string + length = $ - data + end struc + + struc constant_integer value + . = constant_pool_counter + constant_pool_counter = constant_pool_counter + 1 + u1 3 + u4 value + end struc + + struc constant_float value + . = constant_pool_counter + constant_pool_counter = constant_pool_counter + 1 + u1 4 + u4 value + end struc + + struc constant_long value + . = constant_pool_counter + constant_pool_counter = constant_pool_counter + 1 + u1 5 + u4 value shr 32,value and 0FFFFFFFFh + end struc + + struc constant_double value + . = constant_pool_counter + constant_pool_counter = constant_pool_counter + 1 + u1 6 + u4 value shr 32,value and 0FFFFFFFFh + end struc + + struc constant_class name_index + . = constant_pool_counter + constant_pool_counter = constant_pool_counter + 1 + u1 7 + u2 name_index + end struc + + struc constant_string string_index + . = constant_pool_counter + constant_pool_counter = constant_pool_counter + 1 + u1 8 + u2 string_index + end struc + + struc constant_fieldref class_index,name_and_type_index + . = constant_pool_counter + constant_pool_counter = constant_pool_counter + 1 + u1 9 + u2 class_index + u2 name_and_type_index + end struc + + struc constant_methodref class_index,name_and_type_index + . = constant_pool_counter + constant_pool_counter = constant_pool_counter + 1 + u1 10 + u2 class_index + u2 name_and_type_index + end struc + + struc constant_interfacemethodref class_index,name_and_type_index + . = constant_pool_counter + constant_pool_counter = constant_pool_counter + 1 + u1 11 + u2 class_index + u2 name_and_type_index + end struc + + struc constant_nameandtype name_index,descriptor_index + . = constant_pool_counter + constant_pool_counter = constant_pool_counter + 1 + u1 12 + u2 name_index + u2 descriptor_index + end struc + +end macro + +macro end?.constant_pool + constant_pool_count = constant_pool_counter + restruc constant_utf8,constant_integer,constant_float,constant_long,constant_double + restruc constant_class,constant_string + restruc constant_fieldref,constant_methodref,constant_interfacemethodref,constant_nameandtype +end macro + +macro interfaces + u2 interfaces_count + interfaces_counter = 0 + macro interface interface + interfaces_counter = interfaces_counter + 1 + u2 interface + end macro +end macro + +macro end?.interfaces + interfaces_count = interfaces_counter + purge interface +end macro + +macro attributes + local count,counter + u2 count + counter = 0 + attributes_count equ count + attributes_counter equ counter + macro attribute attribute_name_index + match sym,attributes_counter + sym = sym + 1 + end match + u2 attribute_name_index + local start,length + u4 length + start = $ + attribute_start equ start + attribute_length equ length + end macro + macro end?.attribute + match sym,attribute_length + sym = $ - attribute_start + end match + restore atribute_start,attribute_length + end macro +end macro + +macro end?.attributes + match sym,attributes_count + sym = attributes_counter + end match + restore attributes_count,attributes_counter + purge attribute +end macro + +macro fields + u2 fields_count + fields_counter = 0 + macro field_info access_flags,name_index,descriptor_index + fields_counter = fields_counter + 1 + u2 access_flags + u2 name_index + u2 descriptor_index + attributes + end macro + macro end?.field_info + end?.attributes + end macro +end macro + +macro end?.fields + fields_count = fields_counter + purge field_info,end?.field_info +end macro + +macro methods + u2 methods_count + methods_counter = 0 + macro method_info access_flags,name_index,descriptor_index + methods_counter = methods_counter + 1 + u2 access_flags + u2 name_index + u2 descriptor_index + attributes + end macro + macro end?.method_info + end?.attributes + end macro +end macro + +macro end?.methods + methods_count = methods_counter + purge method_info,end?.method_info +end macro + +macro bytecode + local length + bytecode_length equ length + u4 length + bytecode_offset = $ + org 0 +end macro + +macro end?.bytecode + match sym,bytecode_length + sym = $ + end match + org bytecode_offset+bytecode_length + restore bytecode_length +end macro + +macro exceptions + local length + exception_table_length equ length + u2 length + exception_counter = 0 + macro exception start_pc,end_pc,handler_pc,catch_type + exception_counter = exception_counter + 1 + u2 start_pc + u2 end_pc + u2 handler_pc + u2 catch_type + end macro +end macro + +macro end?.exceptions + match sym,exception_table_length + sym = exception_counter + end match + restore exception_table_length +end macro + +include 'bytecode.inc' diff --git a/x86_64_sse2_x87/fasm/examples/jvm/make.cmd b/x86_64_sse2_x87/fasm/examples/jvm/make.cmd new file mode 100644 index 0000000..f57c185 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/jvm/make.cmd @@ -0,0 +1 @@ +fasmg Test.asm Test.class diff --git a/x86_64_sse2_x87/fasm/examples/x86/hello.asm b/x86_64_sse2_x87/fasm/examples/x86/hello.asm new file mode 100644 index 0000000..7d21aee --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/hello.asm @@ -0,0 +1,14 @@ + +include '8086.inc' + + org 100h + +display_text = 9 + + mov ah,display_text + mov dx,hello + int 21h + + int 20h + +hello db 'Hello world!',24h diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/80186.inc b/x86_64_sse2_x87/fasm/examples/x86/include/80186.inc new file mode 100644 index 0000000..ee2d239 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/80186.inc @@ -0,0 +1,390 @@ + +include '8086.inc' + +define @aux @aux + +calminstruction push? src* + + local opcode + + asmcmd =x86.=parse_operand =@src,src + + check @src.size and not 2 + jno main + + asmcmd =err 'invalid operand size' + + main: + check @src.type = 'mem' + jyes push_mem + check @src.type = 'reg' + jyes push_reg + check @src.type = 'sreg' + jyes push_sreg + check @src.type = 'imm' + jyes push_imm + + asmcmd =err 'invalid operand' + exit + + push_mem: + asmcmd =x86.=store_instruction 0FFh,=@src,110b + exit + + push_reg: + compute opcode, 50h + @src.rm + asmcmd =db opcode + exit + + push_sreg: + compute opcode, 6 + @src.rm shl 3 + asmcmd =db opcode + exit + + push_imm: + check @src.imm relativeto 0 & @src.imm < 80h & @src.imm >= -80h + jyes push_simm + check @src.imm relativeto 0 & @src.imm - 10000h >= -80h & @src.imm < 10000h + jyes push_simm_wrap + asmcmd =db 68h + asmcmd =dw =@src.=imm + exit + push_simm_wrap: + compute @src.imm, @src.imm - 10000h + push_simm: + asmcmd =db 6Ah, =@src.=imm + exit + +end calminstruction + +calminstruction x86.pop_instruction size:0,dest* + + local opcode + + asmcmd =x86.=parse_operand =@dest,dest + + check @dest.size and not 2 + jno main + + asmcmd =err 'invalid operand size' + + main: + check @dest.type = 'mem' + jyes pop_mem + check @dest.type = 'reg' + jyes pop_reg + check @dest.type = 'sreg' + jyes pop_sreg + + invalid_operand: + asmcmd =err 'invalid operand' + exit + + pop_mem: + asmcmd =x86.=store_instruction 8Fh,=@dest,0 + exit + + pop_reg: + compute opcode, 58h + @dest.rm + asmcmd =db opcode + exit + + pop_sreg: + check @dest.rm = 1 + jyes invalid_operand + compute opcode, 7 + @dest.rm shl 3 + asmcmd =db opcode + exit + +end calminstruction + +iterate , push,pop + + calminstruction instr? operand + + local head, tail + + match head tail, operand + jno plain + transform head, x86.compact + jno plain + match {head}, head + jno plain + loop: + asmcmd =instr head + match head tail, tail + jno final + transform head, x86.compact + jno error + match {head}, head + jyes loop + error: + asmcmd =err 'only register operands allowed in compact syntax' + exit + final: + transform tail, x86.compact + jno error + match {operand}, tail + jno error + plain: + asmcmd =instr operand + + end calminstruction + +end iterate + +iterate , pusha,60h, popa,61h, insb,6Ch, outsb,6Eh, insw,6Dh, outsw,6Fh, leave,0C9h + + define x86.instr? db opcode + + calminstruction instr? + assemble x86.instr? + end calminstruction + +end iterate + +calminstruction imul? dest*,src& + + local size + + asmcmd =x86.=parse_operand =@dest,dest + + match , src + jyes imul_rm + + local src1, src2 + + match src1 =, src2, src + jyes imul_second_source + + asmcmd =x86.=parse_operand =@src,src + + check @dest.size = 0 & @src.size = 0 + jyes operand_size_not_specified + check @dest.size <> 0 & @src.size <> 0 & @dest.size <> @src.size + jyes operand_sizes_do_not_match + + compute size, @dest.size or @src.size + + check @src.type = 'imm' & @dest.type = 'reg' + jno invalid_combination_of_operands + + compute @aux.type, @src.type + compute @aux.imm, @src.imm + compute @src.type, @dest.type + compute @src.mod, @dest.mod + compute @src.rm, @dest.rm + + jump main + + imul_second_source: + asmcmd =x86.=parse_operand =@src,src1 + asmcmd =x86.=parse_operand =@aux,src2 + + check @dest.size = 0 & @src.size = 0 & @aux.size = 0 + jyes operand_size_not_specified + + compute size, @dest.size or @src.size or @aux.size + + check (@dest.size & @dest.size <> size) | (@src.size & @src.size <> size) | (@aux.size & @aux.size <> size) + jyes operand_sizes_do_not_match + + jump main + + operand_size_not_specified: + asmcmd =err 'operand size not specified' + compute size, 0 + jump main + + operand_sizes_do_not_match: + asmcmd =err 'operand sizes do not match' + compute size, 0 + jump main + + main: + check @aux.type = 'imm' & ( @src.type = 'mem' | @src.type = 'reg' ) & @dest.type = 'reg' & size = 2 + jyes imul_reg_rm_imm + + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' + exit + + imul_rm: + check @dest.size + jyes imul_rm_size_ok + asmcmd =err 'operand size not specified' + imul_rm_size_ok: + check @dest.type = 'mem' | @dest.type = 'reg' + jno invalid_combination_of_operands + check @dest.size < 2 + jyes imul_rm_8bit + asmcmd =x86.=store_instruction 0F7h,=@dest,5 + exit + imul_rm_8bit: + asmcmd =x86.=store_instruction 0F6h,=@dest,5 + exit + + imul_reg_rm_imm: + check @aux.imm relativeto 0 & @aux.imm < 80h & @aux.imm >= -80h + jyes imul_reg_rm_simm + check @aux.imm relativeto 0 & @aux.imm - 10000h >= -80h & @aux.imm < 10000h + jyes imul_reg_rm_simm_wrap + asmcmd =x86.=store_instruction 69h,=@src,=@dest.=rm,size,=@aux.=imm + exit + imul_reg_rm_simm_wrap: + compute @aux.imm, @aux.imm - 1 shl (size shl 3) + imul_reg_rm_simm: + asmcmd =x86.=store_instruction 6Bh,=@src,=@dest.=rm,1,=@aux.=imm + exit + +end calminstruction + +iterate , rol,0, ror,1, rcl,2, rcr,3, shl,4, sal, 4, shr,5, sar,7 + + calminstruction instr? dest*,cnt* + + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,cnt + + check @dest.size = 0 + jyes operand_size_not_specified + check @src.size and not 1 + jyes invalid_operand_size + + main: + check @src.type = 'reg' & @src.size = 1 & @src.rm = 1 & ( @dest.type = 'reg' | @dest.type = 'mem' ) + jyes shift_rm_cl + check @src.type = 'imm' & ( @dest.type = 'reg' | @dest.type = 'mem' ) + jyes shift_rm_imm + + asmcmd =err 'invalid combination of operands' + exit + + shift_rm_cl: + check @dest.size < 2 + jyes shift_r8_cl + asmcmd =x86.=store_instruction 0D3h,=@dest,postbyte + exit + shift_r8_cl: + asmcmd =x86.=store_instruction 0D2h,=@dest,postbyte + exit + shift_rm_imm: + check @dest.size < 2 + jyes shift_rm8_imm + check @src.imm = 1 + jyes shift_rm_1 + asmcmd =x86.=store_instruction 0C1h,=@dest,postbyte,1,=@src.=imm + exit + shift_rm_1: + asmcmd =x86.=store_instruction 0D1h,=@dest,postbyte + exit + shift_rm8_imm: + check @src.imm = 1 + jyes shift_rm8_1 + asmcmd =x86.=store_instruction 0C0h,=@dest,postbyte,1,=@src.=imm + exit + shift_rm8_1: + asmcmd =x86.=store_instruction 0D0h,=@dest,postbyte + exit + + operand_size_not_specified: + asmcmd =err 'operand size not specified' + jump main + invalid_operand_size: + asmcmd =err 'invalid operand size' + jump main + + end calminstruction + +end iterate + +calminstruction ins? dest*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + check @dest.size + jyes size_ok + asmcmd =err 'operand size not specified' + compute size, 0 + size_ok: + check @src.type = 'reg' & @src.size = 2 & @src.rm = 2 & @dest.type = 'mem' & @dest.mod = 0 & @dest.rm = 5 & ( @dest.segment_prefix = 0 | @dest.segment_prefix = 26h ) + jno invalid_combination_of_operands + check @dest.size = 2 + jyes ins_word + assemble x86.insb + exit + ins_word: + assemble x86.insw + exit + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' + exit +end calminstruction + +calminstruction outs? dest*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + check @src.size + jyes size_ok + asmcmd =err 'operand size not specified' + size_ok: + check @dest.type = 'reg' & @dest.size = 2 & @dest.rm = 2 & @src.type = 'mem' & @src.mod = 0 & @src.rm = 4 + jno invalid_combination_of_operands + check @src.segment_prefix = 0 | @src.segment_prefix = 3Eh + jyes segment_prefix_ok + asmcmd =db =@src.=segment_prefix + segment_prefix_ok: + check @src.size = 2 + jyes outs_word + assemble x86.outsb + exit + outs_word: + assemble x86.outsw + exit + operand_size_not_specified: + asmcmd =err 'operand size not specified' + compute size, 0 + jump size_ok + operand_sizes_do_not_match: + asmcmd =err 'operand sizes do not match' + compute size, 0 + jump size_ok + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' + exit +end calminstruction + +calminstruction enter? alloc*,nesting* + asmcmd =x86.=parse_operand =@src,alloc + asmcmd =x86.=parse_operand =@aux,nesting + check (@src.size and not 2) | (@aux.size and not 1) + jno size_ok + asmcmd =err 'invalid operand size' + size_ok: + check @src.type = 'imm' & @aux.type = 'imm' + jno operand_ok + asmcmd =err 'invalid operand' + exit + operand_ok: + asmcmd =db 0C8h + asmcmd =dw =@src.=imm + asmcmd =db =@aux.=imm +end calminstruction + +calminstruction bound? dest*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + check @src.type = 'mem' & @dest.type = 'reg' + jno invalid_combination_of_operands + check @dest.size = 2 + jno invalid_operand_size + check @src.size and not @dest.size + jno size_ok + asmcmd =err 'operand sizes do not match' + size_ok: + asmcmd =x86.=store_instruction 62h,=@src,=@dest.=rm + exit + invalid_operand_size: + asmcmd =err 'invalid operand size' + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' +end calminstruction diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/80286.inc b/x86_64_sse2_x87/fasm/examples/x86/include/80286.inc new file mode 100644 index 0000000..f9e8ee4 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/80286.inc @@ -0,0 +1,88 @@ + +include '80186.inc' + +iterate , loadall,<0Fh,05h>, clts,<0Fh,06h> + + define x86.instr? db opcode + + calminstruction instr? + assemble x86.instr? + end calminstruction + +end iterate + +calminstruction arpl? dest*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + check @src.type = 'reg' & (@dest.type = 'mem' | @dest.type = 'reg') + jno invalid_combination_of_operands + check @src.size = 2 + jno invalid_operand_size + check @dest.size and not @src.size + jno size_ok + asmcmd =err 'operand sizes do not match' + jump size_ok + invalid_operand_size: + asmcmd =err 'invalid operand size' + size_ok: + asmcmd =x86.=store_instruction 63h,=@dest,=@src.=rm + exit + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' +end calminstruction + +iterate , sldt,0,0, str,0,1, lldt,0,2, ltr,0,3, verr,0,4, verw,0,5, smsw,1,4, lmsw,1,6 + + calminstruction instr? dest* + asmcmd =x86.=parse_operand =@dest,dest + check @dest.size and not 2 + jno size_ok + asmcmd =err 'invalid operand size' + size_ok: + check @dest.type = 'reg' | @dest.type = 'mem' + jyes operand_ok + asmcmd =err 'invalid operand' + exit + operand_ok: + asmcmd =x86.=store_instruction <0Fh,ext>,=@dest,postbyte + end calminstruction + +end iterate + +iterate , lgdt,2, lidt,3, sgdt,0, sidt,1 + + calminstruction instr? dest* + asmcmd =x86.=parse_operand =@dest,dest + check @dest.type = 'mem' + jyes operand_ok + asmcmd =err 'invalid operand' + exit + operand_ok: + check @dest.size and not 5 + jno store_instruction + asmcmd =err 'invalid operand size' + store_instruction: + asmcmd =x86.=store_instruction <0Fh,1>,=@dest,postbyte + exit + end calminstruction + +end iterate + +iterate , lar,2, lsl,3 + + calminstruction instr? dest*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + check @dest.type = 'reg' & (@src.type = 'mem' | @src.type = 'reg') + jno invalid_combination_of_operands + check @src.size and not 2 + jno size_ok + asmcmd =err 'invalid operand size' + size_ok: + asmcmd =x86.=store_instruction <0Fh,ext>,=@src,=@dest.=rm + exit + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' + end calminstruction + +end iterate diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/80287.inc b/x86_64_sse2_x87/fasm/examples/x86/include/80287.inc new file mode 100644 index 0000000..e3127ec --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/80287.inc @@ -0,0 +1,17 @@ + +if ~ defined i80287 + + restore i80287 ; this ensures that symbol cannot be forward-referenced + i80287 = 1 + + include '8087.inc' + + purge feni?,fneni?,fdisi?,fndisi? + + define x87.fsetpm? db 0DBh,0E4h + + calminstruction fsetpm? + assemble x87.fsetpm? + end calminstruction + +end if \ No newline at end of file diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/80386.inc b/x86_64_sse2_x87/fasm/examples/x86/include/80386.inc new file mode 100644 index 0000000..f5c6da5 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/80386.inc @@ -0,0 +1,2771 @@ + +define x86 x86 + +element x86.reg +element x86.r8 : x86.reg + 1 +element x86.r16 : x86.reg + 2 +element x86.r32 : x86.reg + 4 + +element al? : x86.r8 + 0 +element cl? : x86.r8 + 1 +element dl? : x86.r8 + 2 +element bl? : x86.r8 + 3 +element ah? : x86.r8 + 4 +element ch? : x86.r8 + 5 +element dh? : x86.r8 + 6 +element bh? : x86.r8 + 7 + +element ax? : x86.r16 + 0 +element cx? : x86.r16 + 1 +element dx? : x86.r16 + 2 +element bx? : x86.r16 + 3 +element sp? : x86.r16 + 4 +element bp? : x86.r16 + 5 +element si? : x86.r16 + 6 +element di? : x86.r16 + 7 + +element eax? : x86.r32 + 0 +element ecx? : x86.r32 + 1 +element edx? : x86.r32 + 2 +element ebx? : x86.r32 + 3 +element esp? : x86.r32 + 4 +element ebp? : x86.r32 + 5 +element esi? : x86.r32 + 6 +element edi? : x86.r32 + 7 + +element x86.sreg + +element es? : x86.sreg + 0 +element cs? : x86.sreg + 1 +element ss? : x86.sreg + 2 +element ds? : x86.sreg + 3 +element fs? : x86.sreg + 4 +element gs? : x86.sreg + 5 + +element x86.creg + +element x86.crx : x86.creg + 0 +element x86.drx : x86.creg + 1 +element x86.trx : x86.creg + 4 + +repeat 8, i:0 + element cr#i? : x86.crx + i + element dr#i? : x86.drx + i + element tr#i? : x86.trx + i +end repeat + +define x86.byte? :1 +define x86.word? :2 +define x86.dword? :4 +define x86.pword? :6 +define x86.fword? :6 +define x86.qword? :8 + +x86.mode = 16 + +macro use16? + x86.mode = 16 +end macro + +macro use32? + x86.mode = 32 +end macro + +define @dest @dest +define @src @src +define @src2 @src2 +define @aux @aux + +macro calminstruction?.asmcmd? pattern& + local cmd + arrange cmd, pattern + assemble cmd +end macro + +calminstruction x86.parse_operand namespace, operand + + local size, type, segment_prefix, prefix, opcode_prefix + local imm, unresolved, displacement, displacement_size + local address, address_registers, segment, offset + local mode, mod, rm + local scale, index, base + + local i, pre, suf, sym + + compute segment_prefix, 0 + compute prefix, 0 + compute opcode_prefix, 0 + + compute size, 0 + compute displacement_size, 0 + + transform operand + + match pre suf, operand + jno no_size_prefix + transform pre, x86 + jno no_size_prefix + match :size, pre + jno no_size_prefix + arrange operand, suf + no_size_prefix: + + match [address], operand + jyes memory_operand + match =ptr? address, operand + jyes memory_operand + match segment:offset, operand + jyes far_operand + + immediate_operand: + compute type, 'imm' + compute imm, +operand + + compute unresolved, 0 + check defined operand + jyes operand_resolved + compute unresolved, 1 + operand_resolved: + + check imm eq 1 elementof imm + jno export_immediate + check 1 metadataof (1 metadataof imm) relativeto x86.reg + jyes register_operand + check 1 metadataof imm relativeto x86.sreg + jyes segment_register_operand + + jump export_immediate + + register_operand: + + compute type, 'reg' + compute mode, x86.mode + compute mod, 11b + compute rm, 1 metadataof imm - 1 elementof (1 metadataof imm) + check size & size <> 1 metadataof (1 metadataof imm) - x86.reg + jyes operand_sizes_do_not_match + compute size, 1 metadataof (1 metadataof imm) - x86.reg + + jump export_register + + segment_register_operand: + + compute type, 'sreg' + compute mode, x86.mode + compute mod, 11b + compute rm, 1 metadataof imm - x86.sreg + check size & size <> 2 & size <> 4 + jyes invalid_operand_size + + jump export_register + + memory_operand: + compute type, 'mem' + + match segment:address, address + jno segment_prefix_ok + check segment eq 1 elementof segment & 1 metadataof segment relativeto x86.sreg + jno invalid_operand + compute segment, 1 metadataof segment - x86.sreg + check segment >= 4 + jyes segment_prefix_386 + compute segment_prefix, 26h + segment shl 3 + jump segment_prefix_ok + segment_prefix_386: + compute segment_prefix, 64h + segment-4 + segment_prefix_ok: + + compute mode, 0 + + match pre suf, address + jno no_address_size_prefix + transform pre, x86 + jno no_address_size_prefix + match :pre, pre + jno no_address_size_prefix + arrange address, suf + check pre = 2 | pre = 4 | pre = 8 + jno invalid_address_size + compute mode, pre shl 3 + no_address_size_prefix: + + compute scale, 0 + compute index, 0 + compute base, 0 + + check size + jyes size_override + compute size, sizeof address + size_override: + + compute address, address + compute address_registers, 0 + compute i, 1 + extract_registers: + check i > elementsof address + jyes registers_extracted + check i metadataof address relativeto x86.r16 | i metadataof address relativeto x86.r32 + jno next_term + compute address_registers, address_registers + i elementof address * i scaleof address + next_term: + compute i, i+1 + jump extract_registers + registers_extracted: + compute displacement, address - address_registers + + check mode + jyes verify_mode + compute mode, x86.mode + check mode = 16 & displacement relativeto 0 & displacement >= 10000h & address_registers eq 0 + jno mode_ok + compute mode, 32 + jump mode_ok + verify_mode: + check (mode = 16 & 1 metadataof address_registers relativeto x86.r32) | (mode = 32 & 1 metadataof address_registers relativeto x86.r16) + jyes invalid_address + mode_ok: + + check address_registers eq 0 + jyes direct_address + check 1 metadataof address_registers relativeto x86.r32 + jyes address_32bit + check 1 metadataof address_registers relativeto x86.r16 + jyes address_16bit + jump invalid_address + + direct_address: + compute mod, 0 + check mode = 16 + jyes direct_address_16bit + direct_address_32bit: + compute rm, 5 + compute displacement_size, 4 + jump export_address + direct_address_16bit: + compute rm, 6 + compute displacement_size, 2 + jump export_address + + address_16bit: + compute mode, 16 + + check address_registers relativeto bx+si + jyes rm_0 + check address_registers relativeto bx+di + jyes rm_1 + check address_registers relativeto bp+si + jyes rm_2 + check address_registers relativeto bp+di + jyes rm_3 + check address_registers relativeto si + jyes rm_4 + check address_registers relativeto di + jyes rm_5 + check address_registers relativeto bp + jyes rm_6 + check address_registers relativeto bx + jyes rm_7 + jump invalid_address + + rm_0: + compute rm, 0 + jump rm_ok + rm_1: + compute rm, 1 + jump rm_ok + rm_2: + compute rm, 2 + jump rm_ok + rm_3: + compute rm, 3 + jump rm_ok + rm_4: + compute rm, 4 + jump rm_ok + rm_5: + compute rm, 5 + jump rm_ok + rm_6: + compute rm, 6 + jump rm_ok + rm_7: + compute rm, 7 + rm_ok: + + check displacement relativeto 0 + jno displacement_16bit + check displacement = 0 & rm <> 6 + jyes displacement_empty + check displacement<80h & displacement>=-80h + jyes displacement_8bit + check displacement-10000h>=-80h & displacement<10000h + jyes displacement_8bit_wrap_16bit + displacement_16bit: + compute displacement_size, 2 + compute mod, 2 + jump export_address + displacement_empty: + compute displacement_size, 0 + compute mod, 0 + jump export_address + displacement_8bit_wrap_16bit: + compute displacement, displacement-10000h + displacement_8bit: + compute displacement_size, 1 + compute mod, 1 + jump export_address + + address_32bit: + compute mode,32 + check 2 scaleof address_registers = 0 + jyes one_register + check 3 scaleof address_registers = 0 & 2 metadataof address_registers relativeto x86.r32 + jyes two_registers + jump invalid_address + + one_register: + compute scale, 1 scaleof address_registers + compute base, 1 metadataof address_registers - x86.r32 + check scale = 1 + jyes one_register_unscaled + check base <> 4 & (scale = 4 | scale = 8) + jyes one_register_scaled + check base <> 4 & (scale = 2 | scale = 3 | scale = 5 | scale = 9) + jyes one_register_split + jump invalid_address + one_register_unscaled: + compute rm, base + check rm = 4 + jno setup_displacement + compute index, 4 + jump setup_displacement + one_register_scaled: + compute rm, 4 + compute index, base + compute base, 5 + jump index_only + one_register_split: + compute rm, 4 + compute index, base + compute scale, scale - 1 + jump setup_displacement + two_registers: + compute rm,4 + check 1 scaleof address_registers = 1 + jyes base_first + check 2 scaleof address_registers = 1 + jyes base_second + jump invalid_address + base_first: + compute base, 1 metadataof address_registers - x86.r32 + compute index, 2 metadataof address_registers - x86.r32 + compute scale, 2 scaleof address_registers + jump process_sib + base_second: + compute base, 2 metadataof address_registers - x86.r32 + compute index, 1 metadataof address_registers - x86.r32 + compute scale, 1 scaleof address_registers + process_sib: + check index = 4 + jyes forbidden_index + check segment_prefix = 36h & index = 5 & scale = 1 + jyes switch_to_index + check segment_prefix = 3Eh & base = 5 & scale = 1 + jyes switch_to_base + check scale > 2 & scale <> 4 & scale <> 8 + jyes invalid_address + jump setup_displacement + forbidden_index: + check scale = 1 + jno invalid_address + compute index, base + compute base, 4 + jump setup_displacement + switch_to_index: + compute index, base + compute base, 5 + jump setup_displacement + switch_to_base: + compute base, index + compute index, 5 + jump setup_displacement + + setup_displacement: + check displacement relativeto 0 + jno displacement_32bit + check displacement = 0 & rm <> 5 & (rm <> 4 | base <> 5) + jyes displacement_empty + check displacement < 80h & displacement >= -80h + jyes displacement_8bit + check displacement-100000000h >= -80h & displacement < 100000000h + jyes displacement_8bit_wrap_32bit + check segment_prefix = 3Eh & base = 5 & index = 5 & scale = 1 + jno displacement_32bit + compute scale, 2 + jump index_only + displacement_32bit: + compute displacement_size, 4 + compute mod, 2 + jump export_address + displacement_8bit_wrap_32bit: + compute displacement, displacement-100000000h + jump displacement_8bit + index_only: + compute displacement_size, 4 + compute mod, 0 + jump export_address + + far_operand: + compute type, 'far' + + check size & size <> 4 & size <> 6 + jyes operand_sizes_do_not_match + + arrange sym, namespace.=segment + publish sym, segment + + arrange sym, namespace.=offset + publish sym, offset + + jump export_common + + export_immediate: + + arrange sym, namespace.=imm + publish sym, imm + + arrange sym, namespace.=unresolved + publish sym, unresolved + + jump export_common + + export_address: + + arrange sym, namespace.=address + publish sym, address + + arrange sym, namespace.=address_registers + publish sym, address_registers + + arrange sym, namespace.=scale + publish sym, scale + + arrange sym, namespace.=index + publish sym, index + + arrange sym, namespace.=base + publish sym, base + + arrange sym, namespace.=displacement + publish sym, displacement + + export_register: + + arrange sym, namespace.=mode + publish sym, mode + + arrange sym, namespace.=mod + publish sym, mod + + arrange sym, namespace.=rm + publish sym, rm + + export_common: + + arrange sym, namespace.=type + publish sym, type + + arrange sym, namespace.=size + publish sym, size + + arrange sym, namespace.=displacement_size + publish sym, displacement_size + + arrange sym, namespace.=segment_prefix + publish sym, segment_prefix + + arrange sym, namespace.=prefix + publish sym, prefix + + arrange sym, namespace.=opcode_prefix + publish sym, opcode_prefix + + exit + + invalid_operand: + asmcmd =err 'invalid operand' + exit + invalid_operand_size: + asmcmd =err 'invalid operand size' + exit + operand_sizes_do_not_match: + asmcmd =err 'operand sizes do not match' + exit + invalid_address: + asmcmd =err 'invalid address' + exit + invalid_address_size: + asmcmd =err 'invalid address size' + exit + +end calminstruction + +calminstruction x86.parse_jump_operand namespace, operand + + local jump_type + local sym + + match =far? operand, operand + jyes far_jump + match =near? operand, operand + jyes near_jump + match =short? operand, operand + jyes short_jump + compute jump_type, '' + jump parse_operand + far_jump: + compute jump_type, 'far' + jump parse_operand + near_jump: + compute jump_type, 'near' + jump parse_operand + short_jump: + compute jump_type, 'short' + + parse_operand: + + arrange operand, =x86.=parse_operand namespace, operand + assemble operand + + arrange sym, namespace.=jump_type + publish sym, jump_type + + local type, size + + arrange type, namespace.=type + check type = 'imm' + jno done + + arrange size, namespace.=size + check size = 0 + jno verify_target_address + + compute size, x86.mode shr 3 + arrange sym, namespace.=size + publish sym, size + + verify_target_address: + + local imm + + arrange sym, namespace.=imm + compute imm, sym + + check imm relativeto 0 + jno done + check imm < 0 + jyes negative + check imm >= 1 shl (size*8) + jno done + out_of_range: + asmcmd =err 'value out of range' + exit + negative: + check imm < - 1 shl (size*8-1) + jyes out_of_range + compute imm, imm and (1 shl (size*8) - 1) + publish sym, imm + + done: + +end calminstruction + +calminstruction x86.select_operand_prefix rm_operand*,size* + + local sym, prefix + + check (size = 2 & x86.mode = 32) | (size = 4 & x86.mode = 16) + jno no_prefix + + compute prefix, 66h + arrange sym, rm_operand.=prefix + publish sym, prefix + + no_prefix: + + check size <> 0 & size <> 2 & size <> 4 + jno done + + asmcmd =err 'invalid operand size' + + done: + +end calminstruction + +calminstruction x86.store_operand_prefix size* + + check (size = 2 & x86.mode = 32) | (size = 4 & x86.mode = 16) + jno no_prefix + + asmcmd =db 66h + exit + + no_prefix: + + check size <> 0 & size <> 2 & size <> 4 + jno done + + asmcmd =err 'invalid operand size' + + done: + +end calminstruction + +calminstruction x86.store_instruction opcode*,rm_operand*,reg*,imm_size:0,imm + + local segment_prefix, prefix, opcode_prefix + local mode, mod, rm + local scale, index, base + local displacement, displacement_size + + arrange segment_prefix, rm_operand.=segment_prefix + arrange prefix, rm_operand.=prefix + arrange opcode_prefix, rm_operand.=opcode_prefix + + arrange mode, rm_operand.=mode + arrange mod, rm_operand.=mod + arrange rm, rm_operand.=rm + arrange base, rm_operand.=base + + arrange displacement_size, rm_operand.=displacement_size + arrange displacement, rm_operand.=displacement + + check segment_prefix + jno segment_prefix_ok + + check mode = 16 & ( rm = 2 | rm = 3 | ( mod > 0 & rm = 6 ) ) + jyes ss_segment_default + check mode = 32 & ( ( mod > 0 & rm = 5 ) | ( rm = 4 & base = 4 ) | ( mod > 0 & rm = 4 & base = 5 ) ) + jyes ss_segment_default + + ds_segment_default: + check segment_prefix = 3Eh + jyes segment_prefix_ok + jump store_segment_prefix + ss_segment_default: + check segment_prefix = 36h + jyes segment_prefix_ok + store_segment_prefix: + asmcmd =db segment_prefix + segment_prefix_ok: + + check mod <> 11b & mode <> x86.mode + jno addressing_prefix_ok + asmcmd =db 67h + addressing_prefix_ok: + + check prefix + jno operand_prefix_ok + asmcmd =db prefix + operand_prefix_ok: + + check opcode_prefix + jno opcode_prefix_ok + asmcmd =db opcode_prefix + opcode_prefix_ok: + + local modrm, sib + + compute modrm, mod shl 6 + (reg and 111b) shl 3 + rm and 111b + asmcmd =db opcode, modrm + + check mod <> 11b & rm = 4 & mode = 32 + jno sib_ok + arrange scale, rm_operand.=scale + arrange index, rm_operand.=index + compute sib, (bsf scale) shl 6 + (index and 111b) shl 3 + base and 111b + asmcmd =db sib + sib_ok: + + check displacement_size = 1 + jyes displacement_8bit + check displacement_size = 2 + jyes displacement_16bit + check displacement_size = 4 + jno displacement_ok + displacement_32bit: + asmcmd =dd rm_operand.=displacement + jump displacement_ok + displacement_16bit: + asmcmd =dw rm_operand.=displacement + jump displacement_ok + displacement_8bit: + asmcmd =db rm_operand.=displacement + displacement_ok: + + check imm_size = 1 + jyes immediate_8bit + check imm_size = 2 + jyes immediate_16bit + check imm_size = 4 + jno immediate_ok + immediate_32bit: + asmcmd =dd imm + jump immediate_ok + immediate_16bit: + asmcmd =dw imm + jump immediate_ok + immediate_8bit: + asmcmd =db imm + immediate_ok: + +end calminstruction + + +iterate , add,0, or,8, adc,10h, sbb,18h, and,20h, sub,28h, xor,30h, cmp,38h + + calminstruction instr? dest*,src* + + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + + local opcode, rm, size + + compute opcode, basecode + + check @dest.size = 0 & @src.size = 0 + jyes operand_size_not_specified + check @dest.size <> 0 & @src.size <> 0 & @dest.size <> @src.size + jyes operand_sizes_do_not_match + + compute size, @dest.size or @src.size + + main: + + check @src.type = 'reg' & ( @dest.type = 'reg' | @dest.type = 'mem' ) + jyes reg_rm + check @src.type = 'mem' & @dest.type = 'reg' + jyes rm_reg + check @src.type = 'imm' & ( @dest.type = 'reg' | @dest.type = 'mem' ) + jyes rm_imm + + asmcmd =err 'invalid combination of operands' + exit + + operand_size_not_specified: + asmcmd =err 'operand size not specified' + compute size, 0 + jump main + + operand_sizes_do_not_match: + asmcmd =err 'operand sizes do not match' + compute size, 0 + jump main + + reg_rm: + check size > 1 + jno reg_rm_store + asmcmd =x86.=select_operand_prefix =@dest,size + compute opcode, opcode + 1 + reg_rm_store: + asmcmd =x86.=store_instruction opcode,=@dest,=@src.=rm + exit + + rm_reg: + compute opcode, opcode + 2 + check size > 1 + jno rm_reg_store + asmcmd =x86.=select_operand_prefix =@src,size + compute opcode, opcode + 1 + rm_reg_store: + asmcmd =x86.=store_instruction opcode,=@src,=@dest.=rm + exit + + rm_imm: + check size > 1 + jyes rm_imm_word + check @dest.type = 'reg' & @dest.rm = 0 + jyes al_imm + + compute opcode, opcode shr 3 + asmcmd =x86.=store_instruction 80h,=@dest,opcode,1,=@src.=imm + exit + + al_imm: + compute opcode, opcode+4 + asmcmd =db opcode, =@src.=imm + exit + + rm_imm_word: + + asmcmd =x86.=select_operand_prefix =@dest,size + + check @src.imm eqtype 0.0 + jno rm_imm_optimize + + asmcmd =virtual =at 0 + asmcmd =emit size:=@src.=imm + asmcmd =load =@src.=imm:size =from 0 + asmcmd =end =virtual + compute @src.imm, +@src.imm + + rm_imm_optimize: + check @src.imm relativeto 0 & @src.imm<80h & @src.imm>=-80h + jyes rm_simm + check size = 2 & @src.imm relativeto 0 & @src.imm-10000h>=-80h & @src.imm<10000h + jyes rm_simm_wrap + check size = 4 & @src.imm relativeto 0 & @src.imm-100000000h>=-80h & @src.imm<100000000h + jyes rm_simm_wrap + check @dest.type = 'reg' & @dest.rm = 0 + jyes ax_imm + + compute rm, opcode shr 3 + asmcmd =x86.=store_instruction 81h,=@dest,rm,size,=@src.=imm + exit + + ax_imm: + check @dest.prefix + jno ax_imm_prefix_ok + asmcmd =db =@dest.=prefix + ax_imm_prefix_ok: + compute opcode, opcode+5 + asmcmd =db opcode + check size = 4 + jyes imm32 + asmcmd =dw =@src.=imm + exit + imm32: + asmcmd =dd =@src.=imm + exit + + rm_simm_wrap: + compute @src.imm, @src.imm - 1 shl (size shl 3) + + rm_simm: + compute rm, opcode shr 3 + asmcmd =x86.=store_instruction 83h,=@dest,rm,1,=@src.=imm + + end calminstruction + +end iterate + +iterate , not,2, neg,3, mul,4, div,6, idiv,7 + + calminstruction instr? src* + + asmcmd =x86.=parse_operand =@src,src + + check @src.size = 0 + jyes operand_size_not_specified + + main: + check @src.type = 'mem' | @src.type = 'reg' + jno invalid_operand + check @src.size > 1 + jyes rm_word + + asmcmd =x86.=store_instruction 0F6h,=@src,postbyte + exit + + rm_word: + asmcmd =x86.=select_operand_prefix =@src,=@src.=size + asmcmd =x86.=store_instruction 0F7h,=@src,postbyte + exit + + operand_size_not_specified: + asmcmd =err 'operand size not specified' + jump main + + invalid_operand: + asmcmd =err 'invalid operand' + exit + + end calminstruction + +end iterate + +calminstruction mov? dest*,src* + + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + + local ext, rm, size + + check @dest.size = 0 & @src.size = 0 & @dest.type <> 'sreg' + jyes operand_size_not_specified + check @dest.size <> 0 & @src.size <> 0 & @dest.size <> @src.size + jyes operand_sizes_do_not_match + + compute size, @dest.size or @src.size + + main: + + check @src.type = 'reg' & ( @dest.type = 'reg' | @dest.type = 'mem' ) + jyes mov_rm_reg + check @src.type = 'mem' & @dest.type = 'reg' + jyes mov_reg_mem + check @src.type = 'imm' & ( @dest.type = 'reg' | @dest.type = 'mem' ) + jyes mov_rm_imm + check @src.type = 'reg' & @dest.type = 'imm' + jyes mov_creg_reg + check @src.type = 'sreg' & ( @dest.type = 'reg' | @dest.type = 'mem' ) + jyes mov_rm_sreg + check @dest.type = 'sreg' & @dest.rm <> 1 & ( @src.type = 'reg' | @src.type = 'mem' ) + jyes mov_sreg_rm + + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' + exit + + operand_size_not_specified: + asmcmd =err 'operand size not specified' + compute size, 0 + jump main + + operand_sizes_do_not_match: + asmcmd =err 'operand sizes do not match' + compute size, 0 + jump main + + mov_rm_reg: + check @src.type = 'reg' & @dest.type = 'mem' & @src.rm = 0 & @dest.address_registers eq 0 + jyes mov_dirmem_ax + check size > 1 + jno mov_reg_rm_8bit + asmcmd =x86.=select_operand_prefix =@dest,size + asmcmd =x86.=store_instruction 89h,=@dest,=@src.=rm + exit + mov_reg_rm_8bit: + asmcmd =x86.=store_instruction 88h,=@dest,=@src.=rm + exit + + mov_reg_mem: + check @src.type = 'mem' & @dest.type = 'reg' & @dest.rm = 0 & @src.address_registers eq 0 + jyes mov_ax_dirmem + check size > 1 + jno mov_mem_reg_8bit + asmcmd =x86.=select_operand_prefix =@src,size + asmcmd =x86.=store_instruction 8Bh,=@src,=@dest.=rm + exit + mov_mem_reg_8bit: + asmcmd =x86.=store_instruction 8Ah,=@src,=@dest.=rm + exit + + mov_dirmem_ax: + check @dest.segment_prefix = 0 | @dest.segment_prefix = 3Eh + jyes dest_seg_ok + asmcmd =db =@dest.=segment_prefix + dest_seg_ok: + check @dest.mode = x86.mode + jyes dest_addr_size_ok + asmcmd =db 67h + dest_addr_size_ok: + check size > 1 + jno mov_dirmem_al + asmcmd =x86.=store_operand_prefix size + asmcmd =db 0A3h + jump dest_displacement + mov_dirmem_al: + asmcmd =db 0A2h + dest_displacement: + check @dest.mode = 16 + jyes dest_displacement_16bit + asmcmd =dd =@dest.=address + exit + dest_displacement_16bit: + asmcmd =dw =@dest.=address + exit + + mov_ax_dirmem: + check @src.segment_prefix = 0 | @src.segment_prefix = 3Eh + jyes src_seg_ok + asmcmd =db =@src.=segment_prefix + src_seg_ok: + check @src.mode = x86.mode + jyes src_addr_size_ok + asmcmd =db 67h + src_addr_size_ok: + check size > 1 + jno mov_al_dirmem + asmcmd =x86.=store_operand_prefix size + asmcmd =db 0A1h + jump src_displacement + mov_al_dirmem: + asmcmd =db 0A0h + src_displacement: + check @src.mode = 16 + jyes src_displacement_16bit + asmcmd =dd =@src.=address + exit + src_displacement_16bit: + asmcmd =dw =@src.=address + exit + + mov_rm_imm: + check @dest.type = 'mem' + jyes mov_mem_imm + check @dest.type = 'reg' & 1 metadataof (1 metadataof @src.imm) relativeto x86.creg & @src.imm relativeto 1 elementof @src.imm + jyes mov_reg_creg + + mov_reg_imm: + check size > 1 + jno mov_reg_imm_8bit + asmcmd =x86.=store_operand_prefix size + asmcmd =db 0B8h + =@dest.=rm + check size = 2 + jyes src_imm_16bit + asmcmd =dd =@src.=imm + exit + src_imm_16bit: + asmcmd =dw =@src.=imm + exit + mov_reg_imm_8bit: + asmcmd =db 0B0h + =@dest.=rm + asmcmd =db =@src.=imm + exit + + mov_mem_imm: + check size > 1 + jno mov_mem_imm_8bit + asmcmd =x86.=select_operand_prefix =@dest,size + asmcmd =x86.=store_instruction 0C7h,=@dest,0,size,=@src.=imm + exit + mov_mem_imm_8bit: + asmcmd =x86.=store_instruction 0C6h,=@dest,0,1,=@src.=imm + exit + + mov_reg_creg: + check @dest.size = 4 + jno invalid_operand_size + compute ext, 20h + 1 metadataof (1 metadataof @src.imm) - x86.creg + compute rm, 1 metadataof @src.imm - 1 elementof (1 metadataof @src.imm) + asmcmd =x86.=store_instruction <0Fh,ext>,=@dest,rm + exit + + mov_creg_reg: + check 1 metadataof (1 metadataof @dest.imm) relativeto x86.creg & @dest.imm relativeto 1 elementof @dest.imm + jno invalid_combination_of_operands + check @src.size = 4 + jno invalid_operand_size + compute ext, 22h + 1 metadataof (1 metadataof @dest.imm) - x86.creg + compute rm, 1 metadataof @dest.imm - 1 elementof (1 metadataof @dest.imm) + asmcmd =x86.=store_instruction <0Fh,ext>,=@src,rm + exit + + mov_rm_sreg: + check @dest.type = 'mem' + jyes mov_mem_sreg + mov_reg_sreg: + check size > 1 + jno invalid_operand_size + asmcmd =x86.=select_operand_prefix =@dest,size + jump mov_rm_sreg_store + mov_mem_sreg: + check size and not 2 + jyes invalid_operand_size + mov_rm_sreg_store: + asmcmd =x86.=store_instruction 8Ch,=@dest,=@src.=rm + exit + + mov_sreg_rm: + check size = 1 + jyes invalid_operand_size + asmcmd =x86.=store_instruction 8Eh,=@src,=@dest.=rm + exit + + invalid_operand_size: + asmcmd =err 'invalid operand size' + exit + +end calminstruction + +calminstruction test? dest*,src* + + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + + local ext, rm, size + + check @dest.size = 0 & @src.size = 0 + jyes operand_size_not_specified + check @dest.size <> 0 & @src.size <> 0 & @dest.size <> @src.size + jyes operand_sizes_do_not_match + + compute size, @dest.size or @src.size + + main: + + check @src.type = 'reg' & ( @dest.type = 'reg' | @dest.type = 'mem' ) + jyes test_reg_rm + check @src.type = 'mem' & @dest.type = 'reg' + jyes test_mem_reg + check @src.type = 'imm' & ( @dest.type = 'reg' | @dest.type = 'mem' ) + jyes test_rm_imm + + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' + exit + + operand_size_not_specified: + asmcmd =err 'operand size not specified' + compute size, 0 + jump main + + operand_sizes_do_not_match: + asmcmd =err 'operand sizes do not match' + compute size, 0 + jump main + + test_reg_rm: + check size > 1 + jno test_reg_rm_8bit + asmcmd =x86.=select_operand_prefix =@dest,size + asmcmd =x86.=store_instruction 85h,=@dest,=@src.=rm + exit + test_reg_rm_8bit: + asmcmd =x86.=store_instruction 84h,=@dest,=@src.=rm + exit + + test_mem_reg: + check size > 1 + jno test_mem_reg_8bit + asmcmd =x86.=select_operand_prefix =@src,size + asmcmd =x86.=store_instruction 85h,=@src,=@dest.=rm + exit + test_mem_reg_8bit: + asmcmd =x86.=store_instruction 84h,=@src,=@dest.=rm + exit + + test_rm_imm: + check size > 1 + jno test_rm_imm_8bit + check @dest.type = 'reg' & @dest.rm = 0 + jyes test_ax_imm + asmcmd =x86.=select_operand_prefix =@dest,size + asmcmd =x86.=store_instruction 0F7h,=@dest,0,size,=@src.=imm + exit + + test_ax_imm: + asmcmd =x86.=store_operand_prefix size + asmcmd =db 0A9h + check size = 2 + jyes src_imm_16bit + asmcmd =dd =@src.=imm + exit + src_imm_16bit: + asmcmd =dw =@src.=imm + exit + + test_rm_imm_8bit: + check @dest.type = 'reg' & @dest.rm = 0 + jyes test_al_imm + asmcmd =x86.=store_instruction 0F6h,=@dest,0,1,=@src.=imm + exit + test_al_imm: + asmcmd =db 0A8h, =@src.=imm + exit + +end calminstruction + +calminstruction xchg? dest*,src* + + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + + local ext, rm, size + + check @dest.size = 0 & @src.size = 0 + jyes operand_size_not_specified + check @dest.size <> 0 & @src.size <> 0 & @dest.size <> @src.size + jyes operand_sizes_do_not_match + + compute size, @dest.size or @src.size + + main: + + check @src.type = 'reg' & @dest.type = 'reg' + jyes xchg_reg_reg + check @src.type = 'reg' & @dest.type = 'mem' + jyes xchg_reg_rm + check @src.type = 'mem' & @dest.type = 'reg' + jyes xchg_rm_reg + + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' + exit + + operand_size_not_specified: + asmcmd =err 'operand size not specified' + compute size, 0 + jump main + + operand_sizes_do_not_match: + asmcmd =err 'operand sizes do not match' + compute size, 0 + jump main + + xchg_reg_reg: + check @src.rm = 0 | @dest.rm = 0 + jno xchg_rm_reg + check size > 1 + jno xchg_rm_reg_8bit + asmcmd =x86.=store_operand_prefix size + local opcode + compute opcode, 90h + @src.rm + @dest.rm + asmcmd =db opcode + exit + + xchg_reg_rm: + check size > 1 + jno xchg_reg_rm_8bit + asmcmd =x86.=select_operand_prefix =@dest,size + asmcmd =x86.=store_instruction 87h,=@dest,=@src.=rm + exit + xchg_reg_rm_8bit: + asmcmd =x86.=store_instruction 86h,=@dest,=@src.=rm + exit + + xchg_rm_reg: + check size > 1 + jno xchg_rm_reg_8bit + asmcmd =x86.=select_operand_prefix =@src,size + asmcmd =x86.=store_instruction 87h,=@src,=@dest.=rm + exit + xchg_rm_reg_8bit: + asmcmd =x86.=store_instruction 86h,=@src,=@dest.=rm + exit + +end calminstruction + +iterate , inc,0 ,dec,1 + + calminstruction instr? dest* + + asmcmd =x86.=parse_operand =@dest,dest + + check @dest.size + jyes main + + asmcmd =err 'operand size not specified' + + main: + check @dest.type = 'reg' + jyes inc_reg + check @dest.type = 'mem' + jyes inc_rm + + asmcmd =err 'invalid operand' + exit + + inc_reg: + check @dest.size > 1 + jno inc_rm_8bit + asmcmd =x86.=store_operand_prefix =@dest.=size + local opcode + compute opcode, 40h + @dest.rm + postbyte shl 3 + asmcmd =db opcode + exit + + inc_rm: + check @dest.size > 1 + jno inc_rm_8bit + asmcmd =x86.=select_operand_prefix =@dest,=@dest.=size + asmcmd =x86.=store_instruction 0FFh,=@dest,postbyte + exit + inc_rm_8bit: + asmcmd =x86.=store_instruction 0FEh,=@dest,postbyte + + end calminstruction + +end iterate + +calminstruction imul? dest*,src& + + local size + + asmcmd =x86.=parse_operand =@dest,dest + + match , src + jyes imul_rm + + local src1, src2 + + match src1 =, src2, src + jyes imul_second_source + + asmcmd =x86.=parse_operand =@src,src + + check @dest.size = 0 & @src.size = 0 + jyes operand_size_not_specified + check @dest.size <> 0 & @src.size <> 0 & @dest.size <> @src.size + jyes operand_sizes_do_not_match + + compute size, @dest.size or @src.size + + check @dest.type = 'reg' & (@src.type = 'reg' | @src.type = 'mem') + jyes imul_reg_rm + + check @src.type = 'imm' & @dest.type = 'reg' + jno invalid_combination_of_operands + + compute @aux.type, @src.type + compute @aux.imm, @src.imm + compute @src.type, @dest.type + compute @src.mod, @dest.mod + compute @src.rm, @dest.rm + + jump main + + imul_second_source: + asmcmd =x86.=parse_operand =@src,src1 + asmcmd =x86.=parse_operand =@aux,src2 + + check @dest.size = 0 & @src.size = 0 & @aux.size = 0 + jyes operand_size_not_specified + + compute size, @dest.size or @src.size or @aux.size + + check (@dest.size & @dest.size <> size) | (@src.size & @src.size <> size) | (@aux.size & @aux.size <> size) + jyes operand_sizes_do_not_match + + jump main + + operand_size_not_specified: + asmcmd =err 'operand size not specified' + compute size, 0 + jump main + + operand_sizes_do_not_match: + asmcmd =err 'operand sizes do not match' + compute size, 0 + jump main + + main: + check @aux.type = 'imm' & ( @src.type = 'mem' | @src.type = 'reg' ) & @dest.type = 'reg' + jyes imul_reg_rm_imm + + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' + exit + + imul_rm: + check @dest.size + jyes imul_rm_size_ok + asmcmd =err 'operand size not specified' + imul_rm_size_ok: + check @dest.type = 'mem' | @dest.type = 'reg' + jno invalid_combination_of_operands + check @dest.size > 1 + jno imul_rm_8bit + asmcmd =x86.=select_operand_prefix =@dest,=@dest.=size + asmcmd =x86.=store_instruction 0F7h,=@dest,5 + exit + imul_rm_8bit: + asmcmd =x86.=store_instruction 0F6h,=@dest,5 + exit + + imul_reg_rm: + asmcmd =x86.=select_operand_prefix =@src,size + asmcmd =x86.=store_instruction <0Fh,0AFh>,=@src,=@dest.=rm + exit + + imul_reg_rm_imm: + asmcmd =x86.=select_operand_prefix =@src,size + check @aux.imm eqtype 0.0 + jno imul_reg_rm_imm_optimize + asmcmd =virtual =at 0 + asmcmd =emit size:=@aux.=imm + asmcmd =load =@aux.=imm:size =from 0 + asmcmd =end =virtual + compute @aux.imm, +@aux.imm + imul_reg_rm_imm_optimize: + check @aux.imm relativeto 0 & @aux.imm < 80h & @aux.imm >= -80h + jyes imul_reg_rm_simm + check size = 2 & @aux.imm relativeto 0 & @aux.imm - 10000h >= -80h & @aux.imm < 10000h + jyes imul_reg_rm_simm_wrap + check size = 4 & @aux.imm relativeto 0 & @aux.imm - 100000000h >= -80h & @aux.imm < 100000000h + jyes imul_reg_rm_simm_wrap + asmcmd =x86.=store_instruction 69h,=@src,=@dest.=rm,size,=@aux.=imm + exit + imul_reg_rm_simm_wrap: + compute @aux.imm, @aux.imm - 1 shl (size shl 3) + imul_reg_rm_simm: + asmcmd =x86.=store_instruction 6Bh,=@src,=@dest.=rm,1,=@aux.=imm + exit + +end calminstruction + +calminstruction x86.push_instruction size:0,src* + + local opcode + + asmcmd =x86.=parse_operand =@src,src + + check size <> 0 & @src.size and not size + jyes invalid_operand_size + compute size, size or @src.size + check size = 0 | size = 2 | size = 4 + jyes main + + invalid_operand_size: + asmcmd =err 'invalid operand size' + + main: + check @src.type = 'mem' + jyes push_mem + check @src.type = 'reg' + jyes push_reg + check @src.type = 'sreg' + jyes push_sreg + check @src.type = 'imm' + jyes push_imm + + asmcmd =err 'invalid operand' + exit + + push_mem: + asmcmd =x86.=select_operand_prefix =@src,size + asmcmd =x86.=store_instruction 0FFh,=@src,110b + exit + + push_reg: + asmcmd =x86.=store_operand_prefix size + compute opcode, 50h + @src.rm + asmcmd =db opcode + exit + + push_sreg: + asmcmd =x86.=store_operand_prefix size + check @src.rm >= 4 + jyes push_sreg_386 + compute opcode, 6 + @src.rm shl 3 + asmcmd =db opcode + exit + push_sreg_386: + compute opcode, 0A0h + (@src.rm-4) shl 3 + asmcmd =db 0Fh,opcode + exit + + push_imm: + check size + jyes push_imm_size_ok + check x86.mode = 16 + jyes push_imm_16bit + compute size, 4 + jump push_imm_size_ok + push_imm_16bit: + compute size, 2 + push_imm_size_ok: + + asmcmd =x86.=store_operand_prefix size + + check @src.imm eqtype 0.0 + jno push_imm_optimize + asmcmd =virtual =at 0 + asmcmd =emit size:=@src.=imm + asmcmd =load =@src.=imm:size =from 0 + asmcmd =end =virtual + compute @src.imm, +@src.imm + push_imm_optimize: + check @src.imm relativeto 0 & @src.imm < 80h & @src.imm >= -80h + jyes push_simm + check size = 2 & @src.imm relativeto 0 & @src.imm - 10000h >= -80h & @src.imm < 10000h + jyes push_simm_wrap + check size = 4 & @src.imm relativeto 0 & @src.imm - 100000000h >= -80h & @src.imm < 100000000h + jyes push_simm_wrap + asmcmd =db 68h + check size = 2 + jyes src_imm_16bit + asmcmd =dd =@src.=imm + exit + src_imm_16bit: + asmcmd =dw =@src.=imm + exit + push_simm_wrap: + compute @src.imm, @src.imm - 1 shl (size shl 3) + push_simm: + asmcmd =db 6Ah, =@src.=imm + exit + +end calminstruction + +calminstruction x86.pop_instruction size:0,dest* + + local opcode + + asmcmd =x86.=parse_operand =@dest,dest + + check size <> 0 & @dest.size and not size + jyes invalid_operand_size + compute size, size or @dest.size + check size = 0 | size = 2 | size = 4 + jyes main + + invalid_operand_size: + asmcmd =err 'invalid operand size' + + main: + check @dest.type = 'mem' + jyes pop_mem + check @dest.type = 'reg' + jyes pop_reg + check @dest.type = 'sreg' + jyes pop_sreg + + invalid_operand: + asmcmd =err 'invalid operand' + exit + + pop_mem: + asmcmd =x86.=select_operand_prefix =@dest,size + asmcmd =x86.=store_instruction 8Fh,=@dest,0 + exit + + pop_reg: + asmcmd =x86.=store_operand_prefix size + compute opcode, 58h + @dest.rm + asmcmd =db opcode + exit + + pop_sreg: + check @dest.rm = 1 + jyes invalid_operand + asmcmd =x86.=store_operand_prefix size + check @dest.rm >= 4 + jyes pop_sreg_386 + compute opcode, 7 + @dest.rm shl 3 + asmcmd =db opcode + exit + pop_sreg_386: + compute opcode, 0A1h + (@dest.rm-4) shl 3 + asmcmd =db 0Fh,opcode + exit + +end calminstruction + +iterate reg, ax,cx,dx,bx,sp,bp,si,di, eax,ecx,edx,ebx,esp,ebp,esi,edi, es,cs,ss,ds,fs,gs + define x86.compact.reg? {reg} +end iterate + +iterate , push,push_instruction,0, pushw,push_instruction,2, pushd,push_instruction,4, \ + pop,pop_instruction,0, popw,pop_instruction,2, popd,pop_instruction,4 + + calminstruction instr? operand + + local head, tail + + match head tail, operand + jno plain + transform head, x86.compact + jno plain + match {head}, head + jno plain + loop: + asmcmd =x86.=handler size,head + match head tail, tail + jno final + transform head, x86.compact + jno error + match {head}, head + jyes loop + error: + asmcmd =err 'only register operands allowed in compact syntax' + exit + final: + transform tail, x86.compact + jno error + match {operand}, tail + jno error + plain: + asmcmd =x86.=handler size,operand + + end calminstruction + +end iterate + +iterate , ret,0C2h, retn,0C2h, retf,0CAh + + calminstruction instr? operand + match , operand + jyes ret_short + check operand + jno ret_short + asmcmd =db opcode + asmcmd =dw operand + exit + ret_short: + asmcmd =db opcode + 1 + end calminstruction + +end iterate + +iterate , retw,2,0C2h, retnw,2,0C2h, retd,4,0C2h, retnd,4,0C2h, retfw,2,0CAh, retfd,4,0CAh + + calminstruction instr? operand + asmcmd =x86.=store_operand_prefix size + match , operand + jyes ret_short + asmcmd =db opcode + asmcmd =dw operand + exit + ret_short: + asmcmd =db opcode + 1 + end calminstruction + +end iterate + +calminstruction lea? dest*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + check @src.type = 'mem' & @dest.type = 'reg' + jno invalid_combination_of_operands + asmcmd =x86.=select_operand_prefix =@src,=@dest.=size + asmcmd =x86.=store_instruction 8Dh,=@src,=@dest.=rm + exit + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' +end calminstruction + +iterate , les,0C4h, lds,0C5h, lss,<0Fh,0B2h>, lfs,<0Fh,0B4h>, lgs,<0Fh,0B5h> + + calminstruction instr? dest*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + check (@dest.size = 2 & (@src.size <> 0 & @src.size <> 4)) | (@dest.size = 4 & (@src.size <> 0 & @src.size <> 6)) + jyes invalid_operand_size + check @src.type = 'mem' & @dest.type = 'reg' + jno invalid_combination_of_operands + asmcmd =x86.=select_operand_prefix =@src,=@dest.=size + asmcmd =x86.=store_instruction opcode,=@src,=@dest.=rm + exit + invalid_operand_size: + asmcmd =err 'invalid operand size' + exit + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' + end calminstruction + +end iterate + +iterate , rol,0, ror,1, rcl,2, rcr,3, shl,4, sal, 4, shr,5, sar,7 + + calminstruction instr? dest*,cnt* + + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,cnt + + check @dest.size = 0 + jyes operand_size_not_specified + check @src.size and not 1 + jyes invalid_operand_size + + main: + check @src.type = 'reg' & @src.size = 1 & @src.rm = 1 & ( @dest.type = 'reg' | @dest.type = 'mem' ) + jyes shift_rm_cl + check @src.type = 'imm' & ( @dest.type = 'reg' | @dest.type = 'mem' ) + jyes shift_rm_imm + + asmcmd =err 'invalid combination of operands' + exit + + shift_rm_cl: + check @dest.size > 1 + jno shift_r8_cl + asmcmd =x86.=select_operand_prefix =@dest,=@dest.=size + asmcmd =x86.=store_instruction 0D3h,=@dest,postbyte + exit + shift_r8_cl: + asmcmd =x86.=store_instruction 0D2h,=@dest,postbyte + exit + shift_rm_imm: + check @dest.size > 1 + jno shift_rm8_imm + asmcmd =x86.=select_operand_prefix =@dest,=@dest.=size + check @src.imm = 1 + jyes shift_rm_1 + asmcmd =x86.=store_instruction 0C1h,=@dest,postbyte,1,=@src.=imm + exit + shift_rm_1: + asmcmd =x86.=store_instruction 0D1h,=@dest,postbyte + exit + shift_rm8_imm: + check @src.imm = 1 + jyes shift_rm8_1 + asmcmd =x86.=store_instruction 0C0h,=@dest,postbyte,1,=@src.=imm + exit + shift_rm8_1: + asmcmd =x86.=store_instruction 0D0h,=@dest,postbyte + exit + + operand_size_not_specified: + asmcmd =err 'operand size not specified' + jump main + invalid_operand_size: + asmcmd =err 'invalid operand size' + jump main + + end calminstruction + +end iterate + +iterate , shld,0A4h, shrd,0ACh + + calminstruction instr? dest*,src*,cnt* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + asmcmd =x86.=parse_operand =@aux,cnt + check @aux.size and not 1 + jyes invalid_operand_size + check @dest.size and not @src.size + jno main + asmcmd =err 'operand sizes do not match' + main: + check @aux.type = 'reg' & @aux.size = 1 & @aux.rm = 1 & @src.type = 'reg' & ( @dest.type = 'reg' | @dest.type = 'mem' ) + jyes shld_cl + check @aux.type = 'imm' & @src.type = 'reg' & ( @dest.type = 'reg' | @dest.type = 'mem' ) + jyes shld_imm + asmcmd =err 'invalid combination of operands' + exit + shld_cl: + asmcmd =x86.=select_operand_prefix =@dest,=@src.=size + asmcmd =x86.=store_instruction <0Fh,ext+1>,=@dest,=@src.=rm + exit + shld_imm: + asmcmd =x86.=select_operand_prefix =@dest,=@src.=size + asmcmd =x86.=store_instruction <0Fh,ext>,=@dest,=@src.=rm,1,=@aux.=imm + exit + invalid_operand_size: + asmcmd =err 'invalid operand size' + end calminstruction + +end iterate + +iterate , bt,4, bts,5, btr,6, btc,7 + + calminstruction instr? dest*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + check @src.type = 'reg' & (@dest.type = 'mem' | @dest.type = 'reg') + jyes bt_rm_reg + check @src.type = 'imm' & (@dest.type = 'mem' | @dest.type = 'reg') + jyes bt_rm_imm + asmcmd =err 'invalid combination of operands' + exit + bt_rm_reg: + local opcode + check @dest.size and not @src.size + jno bt_rm_reg_ok + asmcmd =err 'operand sizes do not match' + bt_rm_reg_ok: + compute opcode, 0A3h+(postbyte-4) shl 3 + asmcmd =x86.=select_operand_prefix =@dest,=@src.=size + asmcmd =x86.=store_instruction <0Fh,opcode>,=@dest,=@src.=rm + exit + bt_rm_imm: + check @src.size and not 1 + jno bt_rm_imm_ok + asmcmd =err 'invalid operand size' + bt_rm_imm_ok: + check @dest.size + jno bt_rm_imm_prefix_ok + asmcmd =x86.=select_operand_prefix =@dest,=@dest.=size + bt_rm_imm_prefix_ok: + asmcmd =x86.=store_instruction <0Fh,0BAh>,=@dest,postbyte,1,=@src.=imm + end calminstruction + +end iterate + +iterate , bsf,0BCh, bsr,0BDh + + calminstruction instr? dest*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + check @dest.type = 'reg' & (@src.type = 'mem' | @src.type = 'reg') + jyes bsf_reg_rm + asmcmd =err 'invalid combination of operands' + exit + bsf_reg_rm: + check @src.size and not @dest.size + jno bsf_reg_rm_ok + asmcmd =err 'operand sizes do not match' + bsf_reg_rm_ok: + asmcmd =x86.=select_operand_prefix =@src,=@dest.=size + asmcmd =x86.=store_instruction <0Fh,ext>,=@src,=@dest.=rm + end calminstruction + +end iterate + +iterate , movzx,0B6h, movsx,0BEh + + calminstruction instr? dest*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + check @dest.size > @src.size + jyes size_ok + asmcmd =err 'operand sizes do not match' + size_ok: + check @dest.type = 'reg' & (@src.type = 'mem' | @src.type = 'reg') + jyes operands_ok + asmcmd =err 'invalid combination of operands' + exit + operands_ok: + check @src.size = 2 + jyes movzx_word + check @src.size = 1 | (@src.size = 0 & @dest.size = 2) + jyes movzx_byte + check @src.size + jyes invalid_operand_size + asmcmd =err 'operand size not specified' + movzx_word: + asmcmd =x86.=select_operand_prefix =@src,=@dest.=size + asmcmd =x86.=store_instruction <0Fh,ext+1>,=@src,=@dest.=rm + exit + movzx_byte: + asmcmd =x86.=select_operand_prefix =@src,=@dest.=size + asmcmd =x86.=store_instruction <0Fh,ext>,=@src,=@dest.=rm + exit + invalid_operand_size: + asmcmd =err 'invalid operand size' + end calminstruction + +end iterate + +iterate , o,0, no,1, c,2, b,2, nae,2, nc,3, nb,3, ae,3, z,4, e,4, nz,5, ne,5, na,6, be,6, a,7, nbe,7, \ + s,8, ns,9, p,0Ah, pe,0Ah, np,0Bh, po,0Bh, l,0Ch, nge,0Ch, nl,0Dh, ge,0Dh, ng,0Eh, le,0Eh, g,0Fh, nle,0Fh + + calminstruction set#cond? dest* + asmcmd =x86.=parse_operand =@dest,dest + check @dest.size > 1 + jno size_ok + asmcmd =err 'invalid operand size' + size_ok: + check @dest.type = 'reg' | @dest.type = 'mem' + jyes operand_ok + asmcmd =err 'invalid operand' + exit + operand_ok: + asmcmd =x86.=store_instruction <0Fh,90h+code>,=@dest,0 + end calminstruction + +end iterate + +calminstruction call? dest* + + asmcmd =x86.=parse_jump_operand =@dest,dest + + check @dest.type = 'imm' + jyes call_imm + check @dest.type = 'mem' | @dest.type = 'reg' + jyes call_rm + check @dest.type = 'far' + jyes call_direct_far + + invalid_operand: + asmcmd =err 'invalid operand' + exit + + call_direct_far: + check @dest.jump_type & @dest.jump_type <> 'far' + jyes invalid_operand + check @dest.size = 0 + jyes prefix_ok + local size + compute size, @dest.size - 2 + asmcmd =x86.=store_operand_prefix size + prefix_ok: + check @dest.size and not 4 & @dest.size and not 6 + jyes invalid_operand + asmcmd =db 9Ah + check (@dest.size = 0 & x86.mode = 16) | @dest.size = 4 + jyes far_dword + asmcmd =dd =@dest.=offset + asmcmd =dw =@dest.=segment + exit + far_dword: + asmcmd =dw =@dest.=offset,=@dest.=segment + exit + + call_rm: + check @dest.size = 6 + jyes call_rm_pword + check @dest.size = 4 + jyes call_rm_dword + check @dest.size = 2 + jyes call_rm_word + check @dest.size + jyes invalid_operand + check @dest.jump_type = 'far' + jyes call_rm_far + check @dest.jump_type = 'near' + jyes call_rm_near + asmcmd =err 'operand size not specified' + exit + + call_rm_pword: + check @dest.jump_type & @dest.jump_type <> 'far' + jyes invalid_operand + asmcmd =x86.=select_operand_prefix =@dest,4 + call_rm_far: + asmcmd =x86.=store_instruction 0FFh,=@dest,11b + exit + + call_rm_dword: + check @dest.jump_type | @dest.type = 'reg' + jno call_rm_dword_auto + check @dest.jump_type = 'far' + jyes call_rm_dword_far + call_rm_dword_near: + asmcmd =x86.=select_operand_prefix =@dest,4 + jump call_rm_near + call_rm_dword_auto: + check x86.mode = 16 + jno call_rm_dword_near + call_rm_dword_far: + asmcmd =x86.=select_operand_prefix =@dest,2 + jump call_rm_far + + call_rm_word: + check @dest.jump_type & @dest.jump_type <> 'near' + jyes invalid_operand + asmcmd =x86.=select_operand_prefix =@dest,2 + call_rm_near: + asmcmd =x86.=store_instruction 0FFh,=@dest,10b + exit + + call_imm: + check @dest.jump_type & @dest.jump_type <> 'near' + jyes invalid_operand + check @dest.size = 2 + jyes call_imm_word + check @dest.size = 4 + jno invalid_operand + asmcmd =x86.=store_operand_prefix 4 + asmcmd =db 0E8h + compute @dest.imm, @dest.imm-($+4) + asmcmd =dd =@dest.=imm + exit + call_imm_word: + asmcmd =x86.=store_operand_prefix 2 + asmcmd =db 0E8h + compute @dest.imm, @dest.imm-($+2) + check @dest.imm relativeto 0 + jno store_word + compute @dest.imm, @dest.imm and 0FFFFh + store_word: + asmcmd =dw =@dest.=imm + exit + +end calminstruction + +calminstruction jmp? dest* + + asmcmd =x86.=parse_jump_operand =@dest,dest + + check @dest.type = 'imm' + jyes jmp_imm + check @dest.type = 'mem' | @dest.type = 'reg' + jyes jmp_rm + check @dest.type = 'far' + jyes jmp_direct_far + + invalid_operand: + asmcmd =err 'invalid operand' + exit + + jmp_direct_far: + check @dest.jump_type & @dest.jump_type <> 'far' + jyes invalid_operand + check @dest.size = 0 + jyes prefix_ok + local size + compute size, @dest.size - 2 + asmcmd =x86.=store_operand_prefix size + prefix_ok: + check @dest.size and not 4 & @dest.size and not 6 + jyes invalid_operand + asmcmd =db 0EAh + check (@dest.size = 0 & x86.mode = 16) | @dest.size = 4 + jyes far_dword + asmcmd =dd =@dest.=offset + asmcmd =dw =@dest.=segment + exit + far_dword: + asmcmd =dw =@dest.=offset,=@dest.=segment + exit + + jmp_rm: + check @dest.size = 6 + jyes jmp_rm_pword + check @dest.size = 4 + jyes jmp_rm_dword + check @dest.size = 2 + jyes jmp_rm_word + check @dest.size + jyes invalid_operand + check @dest.jump_type = 'far' + jyes jmp_rm_far + check @dest.jump_type = 'near' + jyes jmp_rm_near + asmcmd =err 'operand size not specified' + exit + + jmp_rm_pword: + check @dest.jump_type & @dest.jump_type <> 'far' + jyes invalid_operand + asmcmd =x86.=select_operand_prefix =@dest,4 + jmp_rm_far: + asmcmd =x86.=store_instruction 0FFh,=@dest,101b + exit + + jmp_rm_dword: + check @dest.jump_type | @dest.type = 'reg' + jno jmp_rm_dword_auto + check @dest.jump_type = 'far' + jyes jmp_rm_dword_far + jmp_rm_dword_near: + asmcmd =x86.=select_operand_prefix =@dest,4 + jump jmp_rm_near + jmp_rm_dword_auto: + check x86.mode = 16 + jno jmp_rm_dword_near + jmp_rm_dword_far: + asmcmd =x86.=select_operand_prefix =@dest,2 + jump jmp_rm_far + + jmp_rm_word: + check @dest.jump_type & @dest.jump_type <> 'near' + jyes invalid_operand + asmcmd =x86.=select_operand_prefix =@dest,2 + jmp_rm_near: + asmcmd =x86.=store_instruction 0FFh,=@dest,100b + exit + + jmp_imm: + asmcmd =x86.=store_operand_prefix =@dest.=size + check @dest.jump_type = 'near' + jyes jmp_imm_near + check @dest.jump_type = 'short' + jyes jmp_imm_short_verify + check @dest.jump_type + jyes invalid_operand + check ~ $ relativeto 0 & @dest.imm relativeto 0 + jno jmp_optimize + compute @dest.imm, @dest.imm + $ - 0 scaleof $ + asmcmd =err 'invalid address' + jmp_optimize: + check @dest.unresolved + jyes jmp_imm_short + check @dest.imm relativeto $ + jno jmp_imm_near + check (@dest.imm-($+2)) < 80h & (@dest.imm-($+2)) >= -80h + jyes jmp_imm_short + check (@dest.imm-($+2)) and (1 shl (@dest.size*8) - 1) < 80h | (@dest.imm-($+2)) and (1 shl (@dest.size*8) - 1) >= 1 shl (@dest.size*8) - 80h + jyes jmp_imm_short + jmp_imm_near: + check @dest.size = 2 + jyes jmp_imm_word + check @dest.size = 4 + jno invalid_operand + asmcmd =db 0E9h + compute @dest.imm, @dest.imm-($+4) + asmcmd =dd =@dest.=imm + exit + jmp_imm_word: + asmcmd =db 0E9h + compute @dest.imm, @dest.imm-($+2) + check @dest.imm relativeto 0 + jno store_word + compute @dest.imm, @dest.imm and 0FFFFh + store_word: + asmcmd =dw =@dest.=imm + exit + jmp_imm_short_verify: + check (@dest.imm-($+2)) < 80h & (@dest.imm-($+2)) >= -80h + jyes jmp_imm_short + check (@dest.imm-($+2)) and (1 shl (@dest.size*8) - 1) < 80h | (@dest.imm-($+2)) and (1 shl (@dest.size*8) - 1) >= 1 shl (@dest.size*8) - 80h + jyes jmp_imm_short + asmcmd =db 0EBh, ? + jump relative_jump_out_of_range + jmp_imm_short: + asmcmd =db 0EBh + compute @dest.imm, (@dest.imm-($+1)) and 0FFh + asmcmd =db =@dest.=imm + exit + relative_jump_out_of_range: + asmcmd =err 'relative jump out of range' + exit + +end calminstruction + +iterate , jo,70h, jno,71h, jc,72h, jb,72h, jnae,72h, jnc,73h, jnb,73h, jae,73h, jz,74h, je,74h, jnz,75h, jne,75h, jna,76h, jbe,76h, ja,77h, jnbe,77h, \ + js,78h, jns,79h, jp,7Ah, jpe,7Ah, jnp,7Bh, jpo,7Bh, jl,7Ch, jnge,7Ch, jnl,7Dh, jge,7Dh, jng,7Eh, jle,7Eh, jg,7Fh, jnle,7Fh + + calminstruction instr? dest* + + asmcmd =x86.=parse_jump_operand =@dest,dest + + check @dest.type <> 'imm' | @dest.jump_type = 'far' + jyes invalid_operand + + asmcmd =x86.=store_operand_prefix =@dest.=size + + check ~ $ relativeto 0 & @dest.imm relativeto 0 + jno optimize + compute @dest.imm, @dest.imm + $ - 0 scaleof $ + asmcmd =err 'invalid address' + optimize: + + check @dest.jump_type <> 'near' & ( @dest.unresolved | ( @dest.imm relativeto $ & @dest.imm-($+2) < 80h & @dest.imm-($+2) >= -80h ) ) + jyes short + check @dest.jump_type <> 'near' & @dest.imm relativeto $ & ( (@dest.imm-($+2)) and (1 shl (@dest.size*8) - 1) < 80h | (@dest.imm-($+2)) and (1 shl (@dest.size*8) - 1) >= 1 shl (@dest.size*8) - 80h ) + jyes short + check @dest.jump_type = 'short' + jyes relative_jump_out_of_range + + asmcmd =db 0Fh,10h+opcode + + check @dest.size = 2 + jyes relative_word + compute @dest.imm, @dest.imm-($+4) + asmcmd =dd =@dest.=imm + exit + relative_word: + compute @dest.imm, @dest.imm-($+2) + check @dest.imm relativeto 0 + jno store_word + compute @dest.imm, @dest.imm and 0FFFFh + store_word: + asmcmd =dw =@dest.=imm + exit + + short: + compute @dest.imm, (@dest.imm-($+2)) and 0FFh + asmcmd =db opcode,=@dest.=imm + exit + + relative_jump_out_of_range: + asmcmd =db ?,? + asmcmd =err 'relative jump out of range' + exit + + invalid_operand: + asmcmd =err 'invalid operand' + exit + + end calminstruction +end iterate + +iterate , loopnz,0E0h,0, loopne,0E0h,0, loopz,0E1h,0, loope,0E1h,0, loop,0E2h,0, \ + loopnzw,0E0h,2, loopnew,0E0h,2, loopzw,0E1h,2, loopew,0E1h,2, loopw,0E2h,2, \ + loopnzd,0E0h,4, loopned,0E0h,4, loopzd,0E1h,4, looped,0E1h,4, loopd,0E2h,4, \ + jcxz,0E3h,2, jecxz,0E3h,4 + calminstruction instr? dest* + + asmcmd =x86.=parse_jump_operand =@dest,dest + + check @dest.type = 'imm' & ( @dest.jump_type = 'short' | ~ @dest.jump_type ) + jno invalid_operand + + check len shl 3 and not x86.mode + jno address_prefix_ok + asmcmd =db 67h + address_prefix_ok: + check @dest.size shl 3 <> x86.mode + jno operand_prefix_ok + asmcmd =db 66h + operand_prefix_ok: + + asmcmd =db opcode + + check @dest.imm-($+1) < 80h & @dest.imm-($+1) >= -80h + jyes relative_offset_ok + check (@dest.imm-($+2)) and (1 shl (@dest.size*8) - 1) < 80h | (@dest.imm-($+2)) and (1 shl (@dest.size*8) - 1) >= 1 shl (@dest.size*8) - 80h + jyes relative_offset_ok + asmcmd =db ? + asmcmd =err 'relative jump out of range' + exit + relative_offset_ok: + compute @dest.imm, (@dest.imm-($+1)) and 0FFh + asmcmd =db =@dest.=imm + exit + + invalid_operand: + asmcmd =err 'invalid operand' + exit + + end calminstruction +end iterate + +iterate , daa,27h, das,2Fh, aaa,37h, aas,3Fh, nop,90h, int3,0CCh, into,0CEh, int1,0F1h, iret,0CFh, salc,0D6h, \ + hlt,0F4h, cmc,0F5h, clc,0F8h, stc,0F9h, cli,0FAh, sti,0FBh, cld,0FCh, std,0FDh, \ + pusha,60h, popa,61h, pushf,9Ch, popf,9Dh, sahf,9Eh, lahf,9Fh, leave,0C9h, \ + insb,6Ch, outsb,6Eh, movsb,0A4h, cmpsb,0A6h, stosb,0AAh, lodsb,0ACh, scasb,0AEh, xlatb,0D7h, \ + clts,<0Fh,06h>, loadall,<0Fh,07h> + + define x86.instr? db opcode + + calminstruction instr? + assemble x86.instr? + end calminstruction + +end iterate + +define x86.o16? x86.store_operand_prefix 2 +define x86.o32? x86.store_operand_prefix 4 + +iterate , cbw,98h, cwd,99h, iretw,0CFh, pushaw,60h, popaw,61h, pushfw,9Ch, popfw,9Dh, \ + insw,6Dh, outsw,6Fh, movsw,0A5h, cmpsw,0A7h, stosw,0ABh, lodsw,0ADh, scasw,0AFh + + define x86.instr? db opcode + + calminstruction instr? + assemble x86.o16 + assemble x86.instr? + end calminstruction + +end iterate + +iterate , cwde,98h, cdq,99h, iretd,0CFh, pushad,60h, popad,61h, pushfd,9Ch, popfd,9Ch, \ + insd,6Dh, outsd,6Fh, movsd,0A5h, cmpsd,0A7h, stosd,0ABh, lodsd,0ADh, scasd,0AFh + + define x86.instr? db opcode + + calminstruction instr? + assemble x86.o32 + assemble x86.instr? + end calminstruction + +end iterate + +iterate , lock,0F0h, repnz,0F2h, repne,0F2h, rep,0F3h, repz,0F3h, repe,0F3h + + define x86.prefix? db opcode + + calminstruction prefix? instr& + assemble x86.prefix? + assemble instr + end calminstruction + +end iterate + +calminstruction int? number* + asmcmd =db 0CDh,number +end calminstruction + +calminstruction aam? number:10 + asmcmd =db 0D4h,number +end calminstruction + +calminstruction aad? number:10 + asmcmd =db 0D5h,number +end calminstruction + +if defined SSE2 + + purge movsd?, cmpsd? + +end if + +calminstruction movs? dest*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + local size + check @dest.size = 0 & @src.size = 0 + jyes operand_size_not_specified + check @dest.size <> 0 & @src.size <> 0 & @dest.size <> @src.size + jyes operand_sizes_do_not_match + compute size, @dest.size or @src.size + size_ok: + check @src.type = 'mem' & @src.mod = 0 & @dest.type = 'mem' & @dest.mod = 0 & ( (@src.mode = 16 & @src.rm = 4 & @dest.mode = 16 & @dest.rm = 5) | (@src.mode = 32 & @src.rm = 6 & @dest.mode = 32 & @dest.rm = 7) ) & ( @dest.segment_prefix = 0 | @dest.segment_prefix = 26h ) + jno invalid_combination_of_operands + check @src.segment_prefix = 0 | @src.segment_prefix = 3Eh + jyes segment_prefix_ok + asmcmd =db =@src.=segment_prefix + segment_prefix_ok: + check @dest.mode = x86.mode + jyes address_prefix_ok + asmcmd =db 67h + address_prefix_ok: + check size > 1 + jyes movs_word + assemble x86.movsb + exit + movs_word: + asmcmd =x86.=store_operand_prefix size + assemble x86.movsw + exit + operand_size_not_specified: + asmcmd =err 'operand size not specified' + compute size, 0 + jump size_ok + operand_sizes_do_not_match: + asmcmd =err 'operand sizes do not match' + compute size, 0 + jump size_ok + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' + exit +end calminstruction + +calminstruction cmps? src*,dest* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + local size + check @dest.size = 0 & @src.size = 0 + jyes operand_size_not_specified + check @dest.size <> 0 & @src.size <> 0 & @dest.size <> @src.size + jyes operand_sizes_do_not_match + compute size, @dest.size or @src.size + size_ok: + check @src.type = 'mem' & @src.mod = 0 & @dest.type = 'mem' & @dest.mod = 0 & ( (@src.mode = 16 & @src.rm = 4 & @dest.mode = 16 & @dest.rm = 5) | (@src.mode = 32 & @src.rm = 6 & @dest.mode = 32 & @dest.rm = 7) ) & ( @dest.segment_prefix = 0 | @dest.segment_prefix = 26h ) + jno invalid_combination_of_operands + check @src.segment_prefix = 0 | @src.segment_prefix = 3Eh + jyes segment_prefix_ok + asmcmd =db =@src.=segment_prefix + segment_prefix_ok: + check @dest.mode = x86.mode + jyes address_prefix_ok + asmcmd =db 67h + address_prefix_ok: + check size > 1 + jyes cmps_word + assemble x86.cmpsb + exit + cmps_word: + asmcmd =x86.=store_operand_prefix size + assemble x86.cmpsw + exit + operand_size_not_specified: + asmcmd =err 'operand size not specified' + compute size, 0 + jump size_ok + operand_sizes_do_not_match: + asmcmd =err 'operand sizes do not match' + compute size, 0 + jump size_ok + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' + exit +end calminstruction + +calminstruction stos? dest* + asmcmd =x86.=parse_operand =@dest,dest + check @dest.size + jyes size_ok + asmcmd =err 'operand size not specified' + size_ok: + check @dest.type = 'mem' & @dest.mod = 0 & ( (@dest.mode = 16 & @dest.rm = 5) | (@dest.mode = 32 & @dest.rm = 7) ) & ( @dest.segment_prefix = 0 | @dest.segment_prefix = 26h ) + jno invalid_operand + check @dest.mode = x86.mode + jyes address_prefix_ok + asmcmd =db 67h + address_prefix_ok: + check @dest.size > 1 + jyes stos_word + assemble x86.stosb + exit + stos_word: + asmcmd =x86.=store_operand_prefix =@dest.=size + assemble x86.stosw + exit + invalid_operand: + asmcmd =err 'invalid operand' + exit +end calminstruction + +calminstruction lods? src* + asmcmd =x86.=parse_operand =@src,src + check @src.size + jyes size_ok + asmcmd =err 'operand size not specified' + size_ok: + check @src.type = 'mem' & @src.mod = 0 & ( (@src.mode = 16 & @src.rm = 4) | (@src.mode = 32 & @src.rm = 6) ) + jno invalid_operand + check @src.segment_prefix = 0 | @src.segment_prefix = 3Eh + jyes segment_prefix_ok + asmcmd =db =@src.=segment_prefix + segment_prefix_ok: + check @src.mode = x86.mode + jyes address_prefix_ok + asmcmd =db 67h + address_prefix_ok: + check @src.size > 1 + jyes lods_word + assemble x86.lodsb + exit + lods_word: + asmcmd =x86.=store_operand_prefix =@src.=size + assemble x86.lodsw + exit + invalid_operand: + asmcmd =err 'invalid operand' + exit +end calminstruction + +calminstruction scas? dest* + asmcmd =x86.=parse_operand =@dest,dest + check @dest.size + jyes size_ok + asmcmd =err 'operand size not specified' + size_ok: + check @dest.type = 'mem' & @dest.mod = 0 & ( (@dest.mode = 16 & @dest.rm = 5) | (@dest.mode = 32 & @dest.rm = 7) ) & ( @dest.segment_prefix = 0 | @dest.segment_prefix = 26h ) + jno invalid_operand + check @dest.mode = x86.mode + jyes address_prefix_ok + asmcmd =db 67h + address_prefix_ok: + check @dest.size > 1 + jyes scas_word + assemble x86.scasb + exit + scas_word: + asmcmd =x86.=store_operand_prefix =@dest.=size + assemble x86.scasw + exit + invalid_operand: + asmcmd =err 'invalid operand' + exit +end calminstruction + +calminstruction ins? dest*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + check @dest.size + jyes size_ok + asmcmd =err 'operand size not specified' + compute size, 0 + size_ok: + check @src.type = 'reg' & @src.size = 2 & @src.rm = 2 & @dest.type = 'mem' & @dest.mod = 0 & ( (@dest.mode = 16 & @dest.rm = 5) | (@dest.mode = 32 & @dest.rm = 7) ) & ( @dest.segment_prefix = 0 | @dest.segment_prefix = 26h ) + jno invalid_combination_of_operands + check @dest.mode = x86.mode + jyes address_prefix_ok + asmcmd =db 67h + address_prefix_ok: + check @dest.size > 1 + jyes ins_word + assemble x86.insb + exit + ins_word: + asmcmd =x86.=store_operand_prefix =@dest.=size + assemble x86.insw + exit + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' + exit +end calminstruction + +calminstruction outs? dest*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + check @src.size + jyes size_ok + asmcmd =err 'operand size not specified' + size_ok: + check @dest.type = 'reg' & @dest.size = 2 & @dest.rm = 2 & @src.type = 'mem' & @src.mod = 0 & ( (@src.mode = 16 & @src.rm = 4) | (@src.mode = 32 & @src.rm = 6) ) + jno invalid_combination_of_operands + check @src.segment_prefix = 0 | @src.segment_prefix = 3Eh + jyes segment_prefix_ok + asmcmd =db =@src.=segment_prefix + segment_prefix_ok: + check @src.mode = x86.mode + jyes address_prefix_ok + asmcmd =db 67h + address_prefix_ok: + check @src.size > 1 + jyes outs_word + assemble x86.outsb + exit + outs_word: + asmcmd =x86.=store_operand_prefix =@src.=size + assemble x86.outsw + exit + operand_size_not_specified: + asmcmd =err 'operand size not specified' + compute size, 0 + jump size_ok + operand_sizes_do_not_match: + asmcmd =err 'operand sizes do not match' + compute size, 0 + jump size_ok + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' + exit +end calminstruction + +calminstruction xlat? src* + asmcmd =x86.=parse_operand =@src,src + check @src.size > 1 + jno size_ok + asmcmd =err 'invalid operand size' + size_ok: + check @src.type = 'mem' & @src.mod = 0 & ( (@src.mode = 16 & @src.rm = 7) | (@src.mode = 32 & @src.rm = 3) ) + jno invalid_operand + check @src.segment_prefix = 0 | @src.segment_prefix = 3Eh + jyes segment_prefix_ok + asmcmd =db =@src.=segment_prefix + segment_prefix_ok: + check @src.mode = x86.mode + jyes address_prefix_ok + asmcmd =db 67h + address_prefix_ok: + assemble x86.xlatb + exit + invalid_operand: + asmcmd =err 'invalid operand' + exit +end calminstruction + +calminstruction in? dest*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + check @dest.size + jyes size_ok + asmcmd =err 'operand size not specified' + size_ok: + check @src.type = 'reg' & @src.size = 2 & @src.rm = 2 & @dest.type = 'reg' & @dest.rm = 0 + jyes in_ax_dx + check @src.type = 'imm' & @dest.type = 'reg' & @dest.rm = 0 + jyes in_ax_imm + asmcmd =err 'invalid combination of operands' + exit + in_ax_dx: + check @dest.size > 1 + jno in_al_dx + asmcmd =x86.=store_operand_prefix =@dest.=size + asmcmd =db 0EDh + exit + in_al_dx: + asmcmd =db 0ECh + exit + in_ax_imm: + check @dest.size > 1 + jno in_al_imm + asmcmd =x86.=store_operand_prefix =@dest.=size + asmcmd =db 0E5h,=@src.=imm + exit + in_al_imm: + asmcmd =db 0E4h,=@src.=imm + exit +end calminstruction + +calminstruction out? dest*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + check @src.size + jyes size_ok + asmcmd =err 'operand size not specified' + size_ok: + check @dest.type = 'reg' & @dest.size = 2 & @dest.rm = 2 & @src.type = 'reg' & @src.rm = 0 + jyes out_dx_ax + check @dest.type = 'imm' & @src.type = 'reg' & @src.rm = 0 + jyes out_imm_ax + asmcmd =err 'invalid combination of operands' + exit + out_dx_ax: + check @src.size > 1 + jno out_dx_al + asmcmd =x86.=store_operand_prefix =@src.=size + asmcmd =db 0EFh + exit + out_dx_al: + asmcmd =db 0EEh + exit + out_imm_ax: + check @src.size > 1 + jno out_imm_al + asmcmd =x86.=store_operand_prefix =@src.=size + asmcmd =db 0E7h,=@dest.=imm + exit + out_imm_al: + asmcmd =db 0E6h,=@dest.=imm + exit +end calminstruction + +calminstruction enter? alloc*,nesting* + asmcmd =x86.=parse_operand =@src,alloc + asmcmd =x86.=parse_operand =@aux,nesting + check (@src.size and not 2) | (@aux.size and not 1) + jno size_ok + asmcmd =err 'invalid operand size' + size_ok: + check @src.type = 'imm' & @aux.type = 'imm' + jyes operand_ok + asmcmd =err 'invalid operand' + exit + operand_ok: + asmcmd =db 0C8h + asmcmd =dw =@src.=imm + asmcmd =db =@aux.=imm +end calminstruction + +calminstruction bound? dest*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + check @src.type = 'mem' & @dest.type = 'reg' + jno invalid_combination_of_operands + check @src.size and not @dest.size + jno size_ok + asmcmd =err 'operand sizes do not match' + size_ok: + asmcmd =x86.=select_operand_prefix =@src,=@dest.=size + asmcmd =x86.=store_instruction 62h,=@src,=@dest.=rm + exit + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' +end calminstruction + +calminstruction arpl? dest*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + check @src.type = 'reg' & (@dest.type = 'mem' | @dest.type = 'reg') + jno invalid_combination_of_operands + check @src.size = 2 + jno invalid_operand_size + check @dest.size and not @src.size + jno size_ok + asmcmd =err 'operand sizes do not match' + jump size_ok + invalid_operand_size: + asmcmd =err 'invalid operand size' + size_ok: + asmcmd =x86.=store_instruction 63h,=@dest,=@src.=rm + exit + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' +end calminstruction + +iterate , lldt,0,2, ltr,0,3, verr,0,4, verw,0,5, lmsw,1,6 + + calminstruction instr? dest* + asmcmd =x86.=parse_operand =@dest,dest + check @dest.size and not 2 + jno size_ok + asmcmd =err 'invalid operand size' + size_ok: + check @dest.type = 'reg' | @dest.type = 'mem' + jyes operand_ok + asmcmd =err 'invalid operand' + exit + operand_ok: + asmcmd =x86.=store_instruction <0Fh,ext>,=@dest,postbyte + end calminstruction + +end iterate + +iterate , sldt,0,0, str,0,1, smsw,1,4 + + calminstruction instr? dest* + asmcmd =x86.=parse_operand =@dest,dest + check @dest.type = 'reg' + jyes select_operand_prefix + check @dest.size and not 2 + jno size_ok + asmcmd =err 'invalid operand size' + size_ok: + check @dest.type = 'mem' + jyes store_instruction + asmcmd =err 'invalid combination of operands' + exit + select_operand_prefix: + asmcmd =x86.=select_operand_prefix =@dest,=@dest.=size + store_instruction: + asmcmd =x86.=store_instruction <0Fh,ext>,=@dest,postbyte + end calminstruction + +end iterate + +iterate , lgdt,2, lidt,3, sgdt,0, sidt,1 + + calminstruction instr? dest* + asmcmd =x86.=parse_operand =@dest,dest + check @dest.type = 'mem' + jyes operand_ok + asmcmd =err 'invalid operand' + exit + operand_ok: + check @dest.size = 6 + jyes o32 + check @dest.size = 5 + jyes o16 + check @dest.size + jno store_instruction + asmcmd =err 'invalid operand size' + jump store_instruction + o16: + asmcmd =x86.=select_operand_prefix =@dest,2 + jump store_instruction + o32: + asmcmd =x86.=select_operand_prefix =@dest,4 + store_instruction: + asmcmd =x86.=store_instruction <0Fh,1>,=@dest,postbyte + end calminstruction + +end iterate + +iterate , lar,2, lsl,3 + + calminstruction instr? dest*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + check @dest.type = 'reg' & (@src.type = 'mem' | @src.type = 'reg') + jno invalid_combination_of_operands + check @src.size and not 2 + jno size_ok + asmcmd =err 'invalid operand size' + size_ok: + asmcmd =x86.=select_operand_prefix =@src,=@dest.=size + asmcmd =x86.=store_instruction <0Fh,ext>,=@src,=@dest.=rm + exit + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' + end calminstruction + +end iterate + +calminstruction xbts? dest*,src*,offs*,len* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + asmcmd =x86.=parse_operand =@src2,offs + asmcmd =x86.=parse_operand =@aux,len + check @dest.type = 'reg' & (@src.type = 'mem' | @src.type = 'reg') & \ + @src2.type = 'reg' & @src2.rm = 0 & @aux.type = 'reg' & @aux.size = 1 & @aux.rm = 1 + jno invalid_combination_of_operands + check @src.size and not @dest.size | @src2.size <> @dest.size + jyes operand_sizes_no_not_match + check @dest.size > 1 + jyes size_ok + asmcmd =err 'invalid operand size' + jump size_ok + operand_sizes_no_not_match: + asmcmd =err 'operand sizes do not match' + size_ok: + asmcmd =x86.=select_operand_prefix =@src,=@dest.=size + asmcmd =x86.=store_instruction <0Fh,0A6h>,=@src,=@dest.=rm + exit + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' +end calminstruction + +calminstruction ibts? dest*,offs*,len*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + asmcmd =x86.=parse_operand =@src2,offs + asmcmd =x86.=parse_operand =@aux,len + check @src.type = 'reg' & (@dest.type = 'mem' | @dest.type = 'reg') & \ + @src2.type = 'reg' & @src2.rm = 0 & @aux.type = 'reg' & @aux.size = 1 & @aux.rm = 1 + jno invalid_combination_of_operands + check @dest.size and not @src.size | @src2.size <> @src.size + jyes operand_sizes_no_not_match + check @src.size > 1 + jyes size_ok + asmcmd =err 'invalid operand size' + jump size_ok + operand_sizes_no_not_match: + asmcmd =err 'operand sizes do not match' + size_ok: + asmcmd =x86.=select_operand_prefix =@dest,=@src.=size + asmcmd =x86.=store_instruction <0Fh,0A7h>,=@dest,=@src.=rm + exit + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' +end calminstruction diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/80387.inc b/x86_64_sse2_x87/fasm/examples/x86/include/80387.inc new file mode 100644 index 0000000..a51a93d --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/80387.inc @@ -0,0 +1,160 @@ + +if ~ defined i80387 + + restore i80387 ; this ensures that symbol cannot be forward-referenced + i80387 = 1 + + include '80287.inc' + + purge fsetpm? + + iterate , fprem1,<0D9h,0F5h>, fsincos,<0D9h,0FBh>, fsin,<0D9h,0FEh>, fcos,<0D9h,0FFh>, fucompp,<0DAh,0E9h> + + define x87.instr? db opcode + + calminstruction instr? + assemble x87.instr? + end calminstruction + + end iterate + + iterate , fucom,4, fucomp,5 + + calminstruction instr? src:st1 + asmcmd =x87.=parse_operand =@src,src + check @src.type = 'streg' + jno invalid_operand + asmcmd =x86.=store_instruction 0DDh,=@src,postbyte + exit + invalid_operand: + asmcmd =err 'invalid operand' + end calminstruction + + end iterate + + + iterate , fldenv,4, fnstenv,6 + + calminstruction instr? dest* + asmcmd =x86.=parse_operand =@dest,dest + check @dest.size & ( ( x86.mode = 16 & @dest.size <> 14 ) | ( x86.mode = 32 & @dest.size <> 28 ) ) + jyes invalid_operand_size + check @dest.type = 'mem' + jno invalid_operand + asmcmd =x86.=store_instruction 0D9h,=@dest,postbyte + exit + invalid_operand_size: + asmcmd =err 'invalid operand size' + exit + invalid_operand: + asmcmd =err 'invalid operand' + end calminstruction + + end iterate + + iterate , fldenvw,4, fnstenvw,6 + + calminstruction instr? dest* + asmcmd =x86.=parse_operand =@dest,dest + check @dest.size and not 14 + jyes invalid_operand_size + check @dest.type = 'mem' + jno invalid_operand + assemble x86.o16 + asmcmd =x86.=store_instruction 0D9h,=@dest,postbyte + exit + invalid_operand_size: + asmcmd =err 'invalid operand size' + exit + invalid_operand: + asmcmd =err 'invalid operand' + end calminstruction + + end iterate + + iterate , fldenvd,4, fnstenvd,6 + + calminstruction instr? dest* + asmcmd =x86.=parse_operand =@dest,dest + check @dest.size and not 28 + jyes invalid_operand_size + check @dest.type = 'mem' + jno invalid_operand + assemble x86.o32 + asmcmd =x86.=store_instruction 0D9h,=@dest,postbyte + exit + invalid_operand_size: + asmcmd =err 'invalid operand size' + exit + invalid_operand: + asmcmd =err 'invalid operand' + end calminstruction + + end iterate + + iterate , frstor,4, fnsave,6 + + calminstruction instr? dest* + asmcmd =x86.=parse_operand =@dest,dest + check @dest.size & ( ( x86.mode = 16 & @dest.size <> 94 ) | ( x86.mode = 32 & @dest.size <> 108 ) ) + jyes invalid_operand_size + check @dest.type = 'mem' + jno invalid_operand + asmcmd =x86.=store_instruction 0DDh,=@dest,postbyte + exit + invalid_operand_size: + asmcmd =err 'invalid operand size' + exit + invalid_operand: + asmcmd =err 'invalid operand' + end calminstruction + + end iterate + + iterate , frstorw,4, fnsavew,6 + + calminstruction instr? dest* + asmcmd =x86.=parse_operand =@dest,dest + check @dest.size and not 94 + jyes invalid_operand_size + check @dest.type = 'mem' + jno invalid_operand + assemble x86.o16 + asmcmd =x86.=store_instruction 0DDh,=@dest,postbyte + exit + invalid_operand_size: + asmcmd =err 'invalid operand size' + exit + invalid_operand: + asmcmd =err 'invalid operand' + end calminstruction + + end iterate + + iterate , frstord,4, fnsaved,6 + calminstruction instr? dest* + asmcmd =x86.=parse_operand =@dest,dest + check @dest.size and not 108 + jyes invalid_operand_size + check @dest.type = 'mem' + jno invalid_operand + assemble x86.o32 + asmcmd =x86.=store_instruction 0DDh,=@dest,postbyte + exit + invalid_operand_size: + asmcmd =err 'invalid operand size' + exit + invalid_operand: + asmcmd =err 'invalid operand' + end calminstruction + + end iterate + + iterate op, stenvw, stenvd, savew, saved + calminstruction f#op? dest* + assemble x87.fwait? + asmcmd =fn#op? dest + end calminstruction + end iterate + +end if \ No newline at end of file diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/80486.inc b/x86_64_sse2_x87/fasm/examples/x86/include/80486.inc new file mode 100644 index 0000000..2c2729e --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/80486.inc @@ -0,0 +1,63 @@ + +include '80386.inc' + +purge loadall? +purge xbts?,ibts? + +iterate , cmpxchg,0B0h, xadd,0C0h + + calminstruction instr? dest*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + check @src.type = 'reg' & ( @dest.type = 'reg' | @dest.type = 'mem' ) + jyes xadd_rm_reg + asmcmd =err 'invalid combination of operands' + exit + xadd_rm_reg: + check @dest.size and not @src.size + jno size_ok + asmcmd =err 'operand sizes do not match' + size_ok: + check @src.size > 1 + jno xadd_rm_reg_8bit + asmcmd =x86.=select_operand_prefix =@dest,=@src.=size + asmcmd =x86.=store_instruction <0Fh,ext+1>,=@dest,=@src.=rm + exit + xadd_rm_reg_8bit: + asmcmd =x86.=store_instruction <0Fh,ext>,=@dest,=@src.=rm + end calminstruction + +end iterate + +calminstruction bswap? dest* + asmcmd =x86.=parse_operand =@dest,dest + check @dest.type = 'reg' & @dest.size = 4 + jyes operand_ok + asmcmd =err 'invalid operand' + exit + operand_ok: + asmcmd =x86.=store_operand_prefix =@dest.=size + local opcode + compute opcode, 0C8h + @dest.rm and 111b + asmcmd =db 0Fh,opcode +end calminstruction + +calminstruction invlpg? dest* + asmcmd =x86.=parse_operand =@dest,dest + check @dest.type = 'mem' + jyes operand_ok + asmcmd =err 'invalid operand' + exit + operand_ok: + asmcmd =x86.=store_instruction <0Fh,1>,=@dest,7 +end calminstruction + +iterate , invd,<0Fh,8>, wbinvd,<0Fh,9> + + define x86.instr? db opcode + + calminstruction instr? + assemble x86.instr? + end calminstruction + +end iterate diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/8086.inc b/x86_64_sse2_x87/fasm/examples/x86/include/8086.inc new file mode 100644 index 0000000..7c71467 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/8086.inc @@ -0,0 +1,1623 @@ + +define x86 x86 + +element x86.reg +element x86.r8 : x86.reg + 1 +element x86.r16 : x86.reg + 2 + +element al? : x86.r8 + 0 +element cl? : x86.r8 + 1 +element dl? : x86.r8 + 2 +element bl? : x86.r8 + 3 +element ah? : x86.r8 + 4 +element ch? : x86.r8 + 5 +element dh? : x86.r8 + 6 +element bh? : x86.r8 + 7 + +element ax? : x86.r16 + 0 +element cx? : x86.r16 + 1 +element dx? : x86.r16 + 2 +element bx? : x86.r16 + 3 +element sp? : x86.r16 + 4 +element bp? : x86.r16 + 5 +element si? : x86.r16 + 6 +element di? : x86.r16 + 7 + +element x86.sreg + +element es? : x86.sreg + 0 +element cs? : x86.sreg + 1 +element ss? : x86.sreg + 2 +element ds? : x86.sreg + 3 + +define x86.byte? :1 +define x86.word? :2 +define x86.dword? :4 + +define @dest @dest +define @src @src + +macro calminstruction?.asmcmd? pattern& + local cmd + arrange cmd, pattern + assemble cmd +end macro + +calminstruction x86.parse_operand namespace, operand + + local size, type, segment_prefix + local imm, unresolved, displacement, displacement_size + local address, address_registers, segment, offset + local mod, rm + + local i, pre, suf, sym + + compute segment_prefix, 0 + + compute size, 0 + compute displacement_size, 0 + + transform operand + + match pre suf, operand + jno no_size_prefix + transform pre, x86 + jno no_size_prefix + match :size, pre + jno no_size_prefix + arrange operand, suf + no_size_prefix: + + match [address], operand + jyes memory_operand + match =ptr? address, operand + jyes memory_operand + match segment:offset, operand + jyes far_operand + + immediate_operand: + compute type, 'imm' + compute imm, +operand + + compute unresolved, 0 + check defined operand + jyes operand_resolved + compute unresolved, 1 + operand_resolved: + + check imm eq 1 elementof imm + jno export_immediate + check 1 metadataof (1 metadataof imm) relativeto x86.reg + jyes register_operand + check 1 metadataof imm relativeto x86.sreg + jyes segment_register_operand + + jump export_immediate + + register_operand: + + compute type, 'reg' + compute mod, 11b + compute rm, 1 metadataof imm - 1 elementof (1 metadataof imm) + check size & size <> 1 metadataof (1 metadataof imm) - x86.reg + jyes operand_sizes_do_not_match + compute size, 1 metadataof (1 metadataof imm) - x86.reg + + jump export_register + + segment_register_operand: + + compute type, 'sreg' + compute mod, 11b + compute rm, 1 metadataof imm - x86.sreg + check size and not 2 + jyes operand_sizes_do_not_match + compute size, 2 + + jump export_register + + memory_operand: + compute type, 'mem' + + match segment:address, address + jno segment_prefix_ok + check segment eq 1 elementof segment & 1 metadataof segment relativeto x86.sreg + jno invalid_operand + compute segment, 1 metadataof segment - x86.sreg + compute segment_prefix, 26h + segment shl 3 + segment_prefix_ok: + + check size + jyes size_override + compute size, sizeof address + size_override: + + compute address, address + compute address_registers, 0 + compute i, 1 + extract_registers: + check i > elementsof address + jyes registers_extracted + check i metadataof address relativeto x86.r16 | i metadataof address relativeto x86.r32 + jno next_term + compute address_registers, address_registers + i elementof address * i scaleof address + next_term: + compute i, i+1 + jump extract_registers + registers_extracted: + compute displacement, address - address_registers + + check address_registers eq 0 + jyes direct_address + check address_registers relativeto bx+si + jyes rm_0 + check address_registers relativeto bx+di + jyes rm_1 + check address_registers relativeto bp+si + jyes rm_2 + check address_registers relativeto bp+di + jyes rm_3 + check address_registers relativeto si + jyes rm_4 + check address_registers relativeto di + jyes rm_5 + check address_registers relativeto bp + jyes rm_6 + check address_registers relativeto bx + jyes rm_7 + jump invalid_address + + direct_address: + compute mod, 0 + compute rm, 6 + compute displacement_size, 2 + jump export_address + + rm_0: + compute rm, 0 + jump rm_ok + rm_1: + compute rm, 1 + jump rm_ok + rm_2: + compute rm, 2 + jump rm_ok + rm_3: + compute rm, 3 + jump rm_ok + rm_4: + compute rm, 4 + jump rm_ok + rm_5: + compute rm, 5 + jump rm_ok + rm_6: + compute rm, 6 + jump rm_ok + rm_7: + compute rm, 7 + rm_ok: + + check displacement relativeto 0 + jno displacement_16bit + check displacement = 0 & rm <> 6 + jyes displacement_empty + check displacement<80h & displacement>=-80h + jyes displacement_8bit + check displacement-10000h>=-80h & displacement<10000h + jyes displacement_8bit_wrap_16bit + displacement_16bit: + compute displacement_size, 2 + compute mod, 2 + jump export_address + displacement_empty: + compute displacement_size, 0 + compute mod, 0 + jump export_address + displacement_8bit_wrap_16bit: + compute displacement, displacement-10000h + displacement_8bit: + compute displacement_size, 1 + compute mod, 1 + jump export_address + + far_operand: + compute type, 'far' + + check size and not 4 + jyes operand_sizes_do_not_match + + arrange sym, namespace.=segment + publish sym, segment + + arrange sym, namespace.=offset + publish sym, offset + + jump export_common + + export_immediate: + + arrange sym, namespace.=imm + publish sym, imm + + arrange sym, namespace.=unresolved + publish sym, unresolved + + jump export_common + + export_address: + + arrange sym, namespace.=address + publish sym, address + + arrange sym, namespace.=address_registers + publish sym, address_registers + + arrange sym, namespace.=displacement + publish sym, displacement + + export_register: + + arrange sym, namespace.=mod + publish sym, mod + + arrange sym, namespace.=rm + publish sym, rm + + export_common: + + arrange sym, namespace.=type + publish sym, type + + arrange sym, namespace.=size + publish sym, size + + arrange sym, namespace.=displacement_size + publish sym, displacement_size + + arrange sym, namespace.=segment_prefix + publish sym, segment_prefix + + exit + + invalid_operand: + asmcmd =err 'invalid operand' + exit + invalid_operand_size: + asmcmd =err 'invalid operand size' + exit + operand_sizes_do_not_match: + asmcmd =err 'operand sizes do not match' + exit + invalid_address: + asmcmd =err 'invalid address' + exit + invalid_address_size: + asmcmd =err 'invalid address size' + exit + +end calminstruction + +calminstruction x86.parse_jump_operand namespace, operand + + local jump_type + local sym + + match =far? operand, operand + jyes far_jump + match =near? operand, operand + jyes near_jump + match =short? operand, operand + jyes short_jump + compute jump_type, '' + jump parse_operand + far_jump: + compute jump_type, 'far' + jump parse_operand + near_jump: + compute jump_type, 'near' + jump parse_operand + short_jump: + compute jump_type, 'short' + + parse_operand: + + arrange operand, =x86.=parse_operand namespace, operand + assemble operand + + arrange sym, namespace.=jump_type + publish sym, jump_type + +end calminstruction + +calminstruction x86.store_instruction opcode*,rm_operand*,reg*,imm_size:0,imm + + local segment_prefix + local mod, rm + local displacement, displacement_size + + arrange segment_prefix, rm_operand.=segment_prefix + + arrange mod, rm_operand.=mod + arrange rm, rm_operand.=rm + + arrange displacement_size, rm_operand.=displacement_size + arrange displacement, rm_operand.=displacement + + check segment_prefix + jno segment_prefix_ok + check rm = 2 | rm = 3 | ( mod > 0 & rm = 6 ) + jyes ss_segment_default + ds_segment_default: + check segment_prefix = 3Eh + jyes segment_prefix_ok + jump store_segment_prefix + ss_segment_default: + check segment_prefix = 36h + jyes segment_prefix_ok + store_segment_prefix: + asmcmd =db segment_prefix + segment_prefix_ok: + + local modrm + + compute modrm, mod shl 6 + reg shl 3 + rm + asmcmd =db opcode, modrm + + check displacement_size = 1 + jyes displacement_8bit + check displacement_size = 2 + jno displacement_ok + displacement_16bit: + asmcmd =dw rm_operand.=displacement + jump displacement_ok + displacement_8bit: + asmcmd =db rm_operand.=displacement + displacement_ok: + + check imm_size = 1 + jyes immediate_8bit + check imm_size = 2 + jno immediate_ok + immediate_16bit: + asmcmd =dw imm + jump immediate_ok + immediate_8bit: + asmcmd =db imm + immediate_ok: + +end calminstruction + + +iterate , add,0, or,8, adc,10h, sbb,18h, and,20h, sub,28h, xor,30h, cmp,38h + + calminstruction instr? dest*,src* + + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + + local opcode, rm, size + + compute opcode, basecode + + check @dest.size = 0 & @src.size = 0 + jyes operand_size_not_specified + check @dest.size <> 0 & @src.size <> 0 & @dest.size <> @src.size + jyes operand_sizes_do_not_match + + compute size, @dest.size or @src.size + + check size > 2 + jyes invalid_operand_size + + main: + + check @src.type = 'reg' & ( @dest.type = 'reg' | @dest.type = 'mem' ) + jyes reg_rm + check @src.type = 'mem' & @dest.type = 'reg' + jyes rm_reg + check @src.type = 'imm' & ( @dest.type = 'reg' | @dest.type = 'mem' ) + jyes rm_imm + + asmcmd =err 'invalid combination of operands' + exit + + operand_size_not_specified: + asmcmd =err 'operand size not specified' + compute size, 0 + jump main + + operand_sizes_do_not_match: + asmcmd =err 'operand sizes do not match' + compute size, 0 + jump main + + invalid_operand_size: + asmcmd =err 'invalid operand size' + compute size, 0 + jump main + + reg_rm: + check size = 2 + jno reg_rm_store + compute opcode, opcode + 1 + reg_rm_store: + asmcmd =x86.=store_instruction opcode,=@dest,=@src.=rm + exit + + rm_reg: + compute opcode, opcode + 2 + check size = 2 + jno rm_reg_store + compute opcode, opcode + 1 + rm_reg_store: + asmcmd =x86.=store_instruction opcode,=@src,=@dest.=rm + exit + + rm_imm: + check size = 2 + jyes rm_imm_word + check @dest.type = 'reg' & @dest.rm = 0 + jyes al_imm + + compute opcode, opcode shr 3 + asmcmd =x86.=store_instruction 80h,=@dest,opcode,1,=@src.=imm + exit + + al_imm: + compute opcode, opcode+4 + asmcmd =db opcode, =@src.=imm + exit + + rm_imm_word: + + check @src.imm relativeto 0 & @src.imm<80h & @src.imm>=-80h + jyes rm_simm + check @src.imm relativeto 0 & @src.imm-10000h>=-80h & @src.imm<10000h + jyes rm_simm_wrap + check @dest.type = 'reg' & @dest.rm = 0 + jyes ax_imm + + compute rm, opcode shr 3 + asmcmd =x86.=store_instruction 81h,=@dest,rm,size,=@src.=imm + exit + + ax_imm: + compute opcode, opcode+5 + asmcmd =db opcode + asmcmd =dw =@src.=imm + exit + + rm_simm_wrap: + compute @src.imm, @src.imm - 10000h + + rm_simm: + compute rm, opcode shr 3 + asmcmd =x86.=store_instruction 83h,=@dest,rm,1,=@src.=imm + + end calminstruction + +end iterate + +iterate , not,2, neg,3, mul,4, imul,5, div,6, idiv,7 + + calminstruction instr? src* + + asmcmd =x86.=parse_operand =@src,src + + check @src.size = 0 + jyes operand_size_not_specified + + check @src.size > 2 + jyes invalid_operand_size + + main: + check @src.type = 'mem' | @src.type = 'reg' + jno invalid_operand + check @src.size = 2 + jyes rm_word + + asmcmd =x86.=store_instruction 0F6h,=@src,postbyte + exit + + rm_word: + asmcmd =x86.=store_instruction 0F7h,=@src,postbyte + exit + + operand_size_not_specified: + asmcmd =err 'operand size not specified' + jump main + + invalid_operand_size: + asmcmd =err 'invalid operand size' + jump main + + invalid_operand: + asmcmd =err 'invalid operand' + exit + + end calminstruction + +end iterate + +calminstruction mov? dest*,src* + + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + + local ext, rm, size + + check @dest.size = 0 & @src.size = 0 + jyes operand_size_not_specified + check @dest.size <> 0 & @src.size <> 0 & @dest.size <> @src.size + jyes operand_sizes_do_not_match + + compute size, @dest.size or @src.size + + check size > 2 + jyes invalid_operand_size + + main: + + check @src.type = 'reg' & ( @dest.type = 'reg' | @dest.type = 'mem' ) + jyes mov_reg_rm + check @src.type = 'mem' & @dest.type = 'reg' + jyes mov_mem_reg + check @src.type = 'imm' & ( @dest.type = 'reg' | @dest.type = 'mem' ) + jyes mov_rm_imm + check @src.type = 'sreg' & ( @dest.type = 'reg' | @dest.type = 'mem' ) + jyes mov_rm_sreg + check @dest.type = 'sreg' & @dest.rm <> 1 & ( @src.type = 'reg' | @src.type = 'mem' ) + jyes mov_sreg_rm + + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' + exit + + operand_size_not_specified: + asmcmd =err 'operand size not specified' + compute size, 0 + jump main + + operand_sizes_do_not_match: + asmcmd =err 'operand sizes do not match' + compute size, 0 + jump main + + invalid_operand_size: + asmcmd =err 'invalid operand size' + compute size, 0 + jump main + + mov_reg_rm: + check @src.type = 'reg' & @dest.type = 'mem' & @src.rm = 0 & @dest.address_registers eq 0 + jyes mov_ax_dirmem + check size < 2 + jyes mov_reg_rm_8bit + asmcmd =x86.=store_instruction 89h,=@dest,=@src.=rm + exit + mov_reg_rm_8bit: + asmcmd =x86.=store_instruction 88h,=@dest,=@src.=rm + exit + + mov_mem_reg: + check @src.type = 'mem' & @dest.type = 'reg' & @dest.rm = 0 & @src.address_registers eq 0 + jyes mov_dirmem_ax + check size < 2 + jyes mov_mem_reg_8bit + asmcmd =x86.=store_instruction 8Bh,=@src,=@dest.=rm + exit + mov_mem_reg_8bit: + asmcmd =x86.=store_instruction 8Ah,=@src,=@dest.=rm + exit + + mov_ax_dirmem: + check @dest.segment_prefix = 0 | @dest.segment_prefix = 3Eh + jyes dest_seg_ok + asmcmd =db =@dest.=segment_prefix + dest_seg_ok: + check size < 2 + jyes mov_al_dirmem + asmcmd =db 0A3h + asmcmd =dw =@dest.=address + exit + mov_al_dirmem: + asmcmd =db 0A2h + asmcmd =dw =@dest.=address + exit + + mov_dirmem_ax: + check @src.segment_prefix = 0 | @src.segment_prefix = 3Eh + jyes src_seg_ok + asmcmd =db =@src.=segment_prefix + src_seg_ok: + check size < 2 + jyes mov_dirmem_al + asmcmd =db 0A1h + asmcmd =dw =@src.=address + exit + mov_dirmem_al: + asmcmd =db 0A0h + asmcmd =dw =@src.=address + exit + + mov_rm_imm: + check @dest.type = 'mem' + jyes mov_mem_imm + + mov_reg_imm: + check size < 2 + jyes mov_reg_imm_8bit + asmcmd =db 0B8h + =@dest.=rm + asmcmd =dw =@src.=imm + exit + mov_reg_imm_8bit: + asmcmd =db 0B0h + =@dest.=rm + asmcmd =db =@src.=imm + exit + + mov_mem_imm: + check size < 2 + jyes mov_mem_imm_8bit + asmcmd =x86.=store_instruction 0C7h,=@dest,0,size,=@src.=imm + exit + mov_mem_imm_8bit: + asmcmd =x86.=store_instruction 0C6h,=@dest,0,1,=@src.=imm + exit + + mov_rm_sreg: + check size < 2 + jyes invalid_operand_size + asmcmd =x86.=store_instruction 8Ch,=@dest,=@src.=rm + exit + + mov_sreg_rm: + check size < 2 + jyes invalid_operand_size + asmcmd =x86.=store_instruction 8Eh,=@src,=@dest.=rm + exit + +end calminstruction + +calminstruction test? dest*,src* + + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + + local ext, rm, size + + check @dest.size = 0 & @src.size = 0 + jyes operand_size_not_specified + check @dest.size <> 0 & @src.size <> 0 & @dest.size <> @src.size + jyes operand_sizes_do_not_match + + compute size, @dest.size or @src.size + + check size > 2 + jyes invalid_operand_size + + main: + + check @src.type = 'reg' & ( @dest.type = 'reg' | @dest.type = 'mem' ) + jyes test_reg_rm + check @src.type = 'mem' & @dest.type = 'reg' + jyes test_mem_reg + check @src.type = 'imm' & ( @dest.type = 'reg' | @dest.type = 'mem' ) + jyes test_rm_imm + + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' + exit + + operand_size_not_specified: + asmcmd =err 'operand size not specified' + compute size, 0 + jump main + + operand_sizes_do_not_match: + asmcmd =err 'operand sizes do not match' + compute size, 0 + jump main + + invalid_operand_size: + asmcmd =err 'invalid operand size' + compute size, 0 + jump main + + test_reg_rm: + check size < 2 + jyes test_reg_rm_8bit + asmcmd =x86.=store_instruction 85h,=@dest,=@src.=rm + exit + test_reg_rm_8bit: + asmcmd =x86.=store_instruction 84h,=@dest,=@src.=rm + exit + + test_mem_reg: + check size < 2 + jyes test_mem_reg_8bit + asmcmd =x86.=store_instruction 85h,=@src,=@dest.=rm + exit + test_mem_reg_8bit: + asmcmd =x86.=store_instruction 84h,=@src,=@dest.=rm + exit + + test_rm_imm: + check size < 2 + jyes test_rm_imm_8bit + check @dest.type = 'reg' & @dest.rm = 0 + jyes test_ax_imm + asmcmd =x86.=store_instruction 0F7h,=@dest,0,size,=@src.=imm + exit + + test_ax_imm: + asmcmd =db 0A9h + asmcmd =dw =@src.=imm + exit + + test_rm_imm_8bit: + check @dest.type = 'reg' & @dest.rm = 0 + jyes test_al_imm + asmcmd =x86.=store_instruction 0F6h,=@dest,0,1,=@src.=imm + exit + test_al_imm: + asmcmd =db 0A8h, =@src.=imm + exit + +end calminstruction + +calminstruction xchg? dest*,src* + + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + + local ext, rm, size + + check @dest.size = 0 & @src.size = 0 + jyes operand_size_not_specified + check @dest.size <> 0 & @src.size <> 0 & @dest.size <> @src.size + jyes operand_sizes_do_not_match + + compute size, @dest.size or @src.size + + check size > 2 + jyes invalid_operand_size + + main: + + check @src.type = 'reg' & @dest.type = 'reg' + jyes xchg_reg_reg + check @src.type = 'reg' & @dest.type = 'mem' + jyes xchg_reg_rm + check @src.type = 'mem' & @dest.type = 'reg' + jyes xchg_rm_reg + + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' + exit + + operand_size_not_specified: + asmcmd =err 'operand size not specified' + compute size, 0 + jump main + + operand_sizes_do_not_match: + asmcmd =err 'operand sizes do not match' + compute size, 0 + jump main + + invalid_operand_size: + asmcmd =err 'invalid operand size' + compute size, 0 + jump main + + xchg_reg_reg: + check @src.rm = 0 | @dest.rm = 0 + jno xchg_rm_reg + check size < 2 + jyes xchg_rm_reg_8bit + local opcode + compute opcode, 90h + @src.rm + @dest.rm + asmcmd =db opcode + exit + + xchg_reg_rm: + check size < 2 + jyes xchg_reg_rm_8bit + asmcmd =x86.=store_instruction 87h,=@dest,=@src.=rm + exit + xchg_reg_rm_8bit: + asmcmd =x86.=store_instruction 86h,=@dest,=@src.=rm + exit + + xchg_rm_reg: + check size < 2 + jyes xchg_rm_reg_8bit + asmcmd =x86.=store_instruction 87h,=@src,=@dest.=rm + exit + xchg_rm_reg_8bit: + asmcmd =x86.=store_instruction 86h,=@src,=@dest.=rm + exit + +end calminstruction + +iterate , inc,0 ,dec,1 + + calminstruction instr? dest* + + asmcmd =x86.=parse_operand =@dest,dest + + check @dest.size > 2 + jyes invalid_operand_size + check @dest.size + jyes main + + asmcmd =err 'operand size not specified' + jump main + + invalid_operand_size: + asmcmd =err 'invalid operand size' + jump main + + main: + check @dest.type = 'reg' + jyes inc_reg + check @dest.type = 'mem' + jyes inc_rm + + asmcmd =err 'invalid operand' + exit + + inc_reg: + check @dest.size < 2 + jyes inc_rm_8bit + local opcode + compute opcode, 40h + @dest.rm + postbyte shl 3 + asmcmd =db opcode + exit + + inc_rm: + check @dest.size < 2 + jyes inc_rm_8bit + asmcmd =x86.=store_instruction 0FFh,=@dest,postbyte + exit + inc_rm_8bit: + asmcmd =x86.=store_instruction 0FEh,=@dest,postbyte + + end calminstruction + +end iterate + +calminstruction push? src* + + local opcode + + asmcmd =x86.=parse_operand =@src,src + + check @src.size and not 2 + jno main + + asmcmd =err 'invalid operand size' + + main: + check @src.type = 'mem' + jyes push_mem + check @src.type = 'reg' + jyes push_reg + check @src.type = 'sreg' + jyes push_sreg + + asmcmd =err 'invalid operand' + exit + + push_mem: + asmcmd =x86.=store_instruction 0FFh,=@src,110b + exit + + push_reg: + compute opcode, 50h + @src.rm + asmcmd =db opcode + exit + + push_sreg: + compute opcode, 6 + @src.rm shl 3 + asmcmd =db opcode + exit + +end calminstruction + +calminstruction pop? dest* + + local opcode + + asmcmd =x86.=parse_operand =@dest,dest + + check @dest.size and not 2 + jno main + + asmcmd =err 'invalid operand size' + + main: + check @dest.type = 'mem' + jyes pop_mem + check @dest.type = 'reg' + jyes pop_reg + check @dest.type = 'sreg' + jyes pop_sreg + + invalid_operand: + asmcmd =err 'invalid operand' + exit + + pop_mem: + asmcmd =x86.=store_instruction 8Fh,=@dest,0 + exit + + pop_reg: + compute opcode, 58h + @dest.rm + asmcmd =db opcode + exit + + pop_sreg: + compute opcode, 7 + @dest.rm shl 3 + asmcmd =db opcode + exit + +end calminstruction + +iterate reg, ax,cx,dx,bx,sp,bp,si,di, es,cs,ss,ds + define x86.compact.reg? {reg} +end iterate + +iterate , push,pop + + calminstruction instr? operand + + local head, tail + + match head tail, operand + jno plain + transform head, x86.compact + jno plain + match {head}, head + jno plain + loop: + asmcmd =instr head + match head tail, tail + jno final + transform head, x86.compact + jno error + match {head}, head + jyes loop + error: + asmcmd =err 'only register operands allowed in compact syntax' + exit + final: + transform tail, x86.compact + jno error + match {operand}, tail + jno error + plain: + asmcmd =instr operand + + end calminstruction + +end iterate + +iterate , ret,0C2h, retn,0C2h, retf,0CAh + + calminstruction instr? operand + match , operand + jyes ret_short + check operand + jno ret_short + asmcmd =db opcode + asmcmd =dw operand + exit + ret_short: + asmcmd =db opcode + 1 + end calminstruction + +end iterate + +calminstruction lea? dest*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + check @src.type = 'mem' & @dest.type = 'reg' + jno invalid_combination_of_operands + check @dest.size = 2 + jyes ok + asmcmd =err 'invalid operand size' + ok: + asmcmd =x86.=store_instruction 8Dh,=@src,=@dest.=rm + exit + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' +end calminstruction + +iterate , les,0C4h, lds,0C5h + + calminstruction instr? dest*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + check @dest.size and not 2 | @src.size and not 4 + jyes invalid_operand_size + check @src.type = 'mem' & @dest.type = 'reg' + jno invalid_combination_of_operands + asmcmd =x86.=store_instruction opcode,=@src,=@dest.=rm + exit + invalid_operand_size: + asmcmd =err 'invalid operand size' + exit + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' + end calminstruction + +end iterate + +iterate , rol,0, ror,1, rcl,2, rcr,3, shl,4, sal, 4, shr,5, sar,7 + + calminstruction instr? dest*,cnt* + + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,cnt + + check @dest.size = 0 + jyes operand_size_not_specified + check @dest.size > 2 + jyes invalid_operand_size + check @src.size and not 1 + jyes invalid_operand_size + + main: + check @src.type = 'reg' & @src.size = 1 & @src.rm = 1 & ( @dest.type = 'reg' | @dest.type = 'mem' ) + jyes shift_rm_cl + check @src.type = 'imm' & ( @dest.type = 'reg' | @dest.type = 'mem' ) + jyes shift_rm_imm + + asmcmd =err 'invalid combination of operands' + exit + + shift_rm_cl: + check @dest.size < 2 + jyes shift_r8_cl + asmcmd =x86.=store_instruction 0D3h,=@dest,postbyte + exit + shift_r8_cl: + asmcmd =x86.=store_instruction 0D2h,=@dest,postbyte + exit + + shift_rm_imm: + compute @src.imm, @src.imm and (1 shl (@dest.size shl 3) - 1) + check @dest.size < 2 + jyes shift_rm8_imm + shift_rm16_imm: + check @src.imm + jno done + asmcmd =x86.=store_instruction 0D1h,=@dest,postbyte + compute @src.imm, @src.imm - 1 + jump shift_rm16_imm + shift_rm8_imm: + check @src.imm + jno done + asmcmd =x86.=store_instruction 0D0h,=@dest,postbyte + compute @src.imm, @src.imm - 1 + jump shift_rm8_imm + done: + exit + + operand_size_not_specified: + asmcmd =err 'operand size not specified' + jump main + invalid_operand_size: + asmcmd =err 'invalid operand size' + jump main + + end calminstruction + +end iterate + +calminstruction call? dest* + + asmcmd =x86.=parse_jump_operand =@dest,dest + + check @dest.type = 'imm' + jyes call_imm + check @dest.type = 'mem' | @dest.type = 'reg' + jyes call_rm + check @dest.type = 'far' + jyes call_direct_far + + invalid_operand: + asmcmd =err 'invalid operand' + exit + + call_direct_far: + check @dest.jump_type & @dest.jump_type <> 'far' + jyes invalid_operand + check @dest.size and not 4 + jyes invalid_operand + asmcmd =db 9Ah + asmcmd =dw =@dest.=offset,=@dest.=segment + exit + + call_rm: + check @dest.size = 4 + jyes call_rm_dword + check @dest.size = 2 + jyes call_rm_word + check @dest.size + jyes invalid_operand + check @dest.jump_type = 'far' + jyes call_rm_far + check @dest.jump_type = 'near' + jyes call_rm_near + asmcmd =err 'operand size not specified' + exit + + call_rm_dword: + check @dest.jump_type & @dest.jump_type <> 'far' + jyes invalid_operand + call_rm_far: + asmcmd =x86.=store_instruction 0FFh,=@dest,11b + exit + + call_rm_word: + check @dest.jump_type & @dest.jump_type <> 'near' + jyes invalid_operand + call_rm_near: + asmcmd =x86.=store_instruction 0FFh,=@dest,10b + exit + + call_imm: + check @dest.jump_type & @dest.jump_type <> 'near' + jyes invalid_operand + check @dest.imm relativeto 0 & (@dest.imm < 0 | @dest.imm >= 10000h) + jyes value_out_of_range + asmcmd =db 0E8h + compute @dest.imm, @dest.imm-($+2) + asmcmd =dw =@dest.=imm + exit + + value_out_of_range: + asmcmd =err 'value out of range' + +end calminstruction + +calminstruction jmp? dest* + + asmcmd =x86.=parse_jump_operand =@dest,dest + + check @dest.type = 'imm' + jyes jmp_imm + check @dest.type = 'mem' | @dest.type = 'reg' + jyes jmp_rm + check @dest.type = 'far' + jyes jmp_direct_far + + invalid_operand: + asmcmd =err 'invalid operand' + exit + + jmp_direct_far: + check @dest.jump_type & @dest.jump_type <> 'far' + jyes invalid_operand + check @dest.size and not 4 + jyes invalid_operand + asmcmd =db 0EAh + asmcmd =dw =@dest.=offset,=@dest.=segment + exit + + jmp_rm: + check @dest.size = 4 + jyes jmp_rm_dword + check @dest.size = 2 + jyes jmp_rm_word + check @dest.size + jyes invalid_operand + check @dest.jump_type = 'far' + jyes jmp_rm_far + check @dest.jump_type = 'near' + jyes jmp_rm_near + asmcmd =err 'operand size not specified' + exit + + jmp_rm_dword: + check @dest.jump_type & @dest.jump_type <> 'far' + jyes invalid_operand + jmp_rm_far: + asmcmd =x86.=store_instruction 0FFh,=@dest,101b + exit + + jmp_rm_word: + check @dest.jump_type & @dest.jump_type <> 'near' + jyes invalid_operand + jmp_rm_near: + asmcmd =x86.=store_instruction 0FFh,=@dest,100b + exit + + jmp_imm: + check @dest.imm relativeto 0 & (@dest.imm < 0 | @dest.imm >= 10000h) + jyes value_out_of_range + check @dest.jump_type = 'near' + jyes jmp_imm_near + check @dest.jump_type = 'short' + jyes jmp_imm_short_verify + check @dest.jump_type + jyes invalid_operand + check ~ $ relativeto 0 & @dest.imm relativeto 0 + jno jmp_optimize + compute @dest.imm, @dest.imm + $ - 0 scaleof $ + asmcmd =err 'invalid address' + jmp_optimize: + check @dest.unresolved + jyes jmp_imm_short + check @dest.imm relativeto $ + jno jmp_imm_near + check (@dest.imm-($+2)) and 0FFFFh < 80h | (@dest.imm-($+2)) and 0FFFFh >= 0FF80h + jyes jmp_imm_short + jmp_imm_near: + asmcmd =db 0E9h + compute @dest.imm, @dest.imm-($+2) + asmcmd =dw =@dest.=imm + exit + jmp_imm_short_verify: + check (@dest.imm-($+2)) and 0FFFFh < 80h | (@dest.imm-($+2)) and 0FFFFh >= 0FF80h + jno relative_jump_out_of_range + jmp_imm_short: + asmcmd =db 0EBh + compute @dest.imm, (@dest.imm-($+1)) and 0FFh + asmcmd =db =@dest.=imm + exit + + relative_jump_out_of_range: + asmcmd =db ?, ? + asmcmd =err 'relative jump out of range' + exit + + value_out_of_range: + asmcmd =err 'value out of range' + +end calminstruction + +x86.jumps = 0 + +calminstruction jumps? + compute x86.jumps, 1 +end calminstruction + +calminstruction nojumps? + compute x86.jumps, 0 +end calminstruction + +iterate , jo,70h, jno,71h, jc,72h, jb,72h, jnae,72h, jnc,73h, jnb,73h, jae,73h, jz,74h, je,74h, jnz,75h, jne,75h, jna,76h, jbe,76h, ja,77h, jnbe,77h, \ + js,78h, jns,79h, jp,7Ah, jpe,7Ah, jnp,7Bh, jpo,7Bh, jl,7Ch, jnge,7Ch, jnl,7Dh, jge,7Dh, jng,7Eh, jle,7Eh, jg,7Fh, jnle,7Fh + + calminstruction instr? dest* + + asmcmd =x86.=parse_jump_operand =@dest,dest + + check @dest.type <> 'imm' & ( @dest.jump_type & @dest.jump_type <> 'short' ) + jyes invalid_operand + + check @dest.imm relativeto 0 & (@dest.imm < 0 | @dest.imm >= 10000h) + jyes value_out_of_range + + check @dest.unresolved | ( @dest.imm-($+2) < 80h & @dest.imm-($+2) >= -80h ) + jyes in_range + check (@dest.imm-($+2)) and 0FFFFh < 80h | (@dest.imm-($+2)) and 0FFFFh >= 0FF80h + jyes in_range + + check x86.jumps & ~ @dest.jump_type + jyes trampoline + + asmcmd =db ?,? + asmcmd =err 'relative jump out of range' + exit + + in_range: + compute @dest.imm, (@dest.imm-($+2)) and 0FFh + asmcmd =db opcode,=@dest.=imm + exit + + trampoline: + asmcmd =db opcode =xor 1,3 + asmcmd =db 0E9h + compute @dest.imm, @dest.imm-($+2) + asmcmd =dw =@dest.=imm + exit + + invalid_operand: + asmcmd =err 'invalid operand' + exit + + value_out_of_range: + asmcmd =err 'value out of range' + + end calminstruction +end iterate + +iterate , loopnz,0E0h, loopne,0E0h, loopz,0E1h, loope,0E1h, loop,0E2h, jcxz,0E3h + + calminstruction instr? dest* + + asmcmd =x86.=parse_jump_operand =@dest,dest + + check @dest.type = 'imm' & ( @dest.jump_type = 'short' | ~ @dest.jump_type ) + jno invalid_operand + + asmcmd =db opcode + + check @dest.imm-($+1) < 80h & @dest.imm-($+1) >= -80h + jyes relative_offset_ok + check (@dest.imm-($+2)) and 0FFFFh < 80h | (@dest.imm-($+2)) and 0FFFFh >= 0FF80h + jyes relative_offset_ok + asmcmd =db ? + asmcmd =err 'relative jump out of range' + exit + relative_offset_ok: + compute @dest.imm, (@dest.imm-($+1)) and 0FFh + asmcmd =db =@dest.=imm + exit + + invalid_operand: + asmcmd =err 'invalid operand' + exit + + end calminstruction +end iterate + +iterate , daa,27h, das,2Fh, aaa,37h, aas,3Fh, nop,90h, cbw,98h, cwd,99h, \ + int3,0CCh, into,0CEh, iret,0CFh, salc,0D6h, \ + hlt,0F4h, cmc,0F5h, clc,0F8h, stc,0F9h, cli,0FAh, sti,0FBh, cld,0FCh, std,0FDh, \ + pushf,9Ch, popf,9Dh, sahf,9Eh, lahf,9Fh, \ + movsb,0A4h, cmpsb,0A6h, stosb,0AAh, lodsb,0ACh, scasb,0AEh, xlatb,0D7h, \ + movsw,0A5h, cmpsw,0A7h, stosw,0ABh, lodsw,0ADh, scasw,0AFh + + define x86.instr? db opcode + + calminstruction instr? + assemble x86.instr? + end calminstruction + +end iterate + +iterate , lock,0F0h, repnz,0F2h, repne,0F2h, rep,0F3h, repz,0F3h, repe,0F3h + + define x86.prefix? db opcode + + calminstruction prefix? instr& + assemble x86.prefix? + assemble instr + end calminstruction + +end iterate + +calminstruction int? number* + asmcmd =db 0CDh,number +end calminstruction + +calminstruction aam? number:10 + asmcmd =db 0D4h,number +end calminstruction + +calminstruction aad? number:10 + asmcmd =db 0D5h,number +end calminstruction + +calminstruction movs? dest*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + local size + check @dest.size = 0 & @src.size = 0 + jyes operand_size_not_specified + check @dest.size <> 0 & @src.size <> 0 & @dest.size <> @src.size + jyes operand_sizes_do_not_match + compute size, @dest.size or @src.size + size_ok: + check @src.type = 'mem' & @src.mod = 0 & @src.rm = 4 & @dest.type = 'mem' & @dest.mod = 0 & @dest.rm = 5 & ( @dest.segment_prefix = 0 | @dest.segment_prefix = 26h ) + jno invalid_combination_of_operands + check @src.segment_prefix = 0 | @src.segment_prefix = 3Eh + jyes segment_prefix_ok + asmcmd =db =@src.=segment_prefix + segment_prefix_ok: + check size = 2 + jyes movs_word + assemble x86.movsb + exit + movs_word: + assemble x86.movsw + exit + operand_size_not_specified: + asmcmd =err 'operand size not specified' + compute size, 0 + jump size_ok + operand_sizes_do_not_match: + asmcmd =err 'operand sizes do not match' + compute size, 0 + jump size_ok + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' + exit +end calminstruction + +calminstruction cmps? src*,dest* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + local size + check @dest.size = 0 & @src.size = 0 + jyes operand_size_not_specified + check @dest.size <> 0 & @src.size <> 0 & @dest.size <> @src.size + jyes operand_sizes_do_not_match + compute size, @dest.size or @src.size + size_ok: + check @src.type = 'mem' & @src.mod = 0 & @src.rm = 4 & @dest.type = 'mem' & @dest.mod = 0 & @dest.rm = 5 & ( @dest.segment_prefix = 0 | @dest.segment_prefix = 26h ) + jno invalid_combination_of_operands + check @src.segment_prefix = 0 | @src.segment_prefix = 3Eh + jyes segment_prefix_ok + asmcmd =db =@src.=segment_prefix + segment_prefix_ok: + check size = 2 + jyes cmps_word + assemble x86.cmpsb + exit + cmps_word: + assemble x86.cmpsw + exit + operand_size_not_specified: + asmcmd =err 'operand size not specified' + compute size, 0 + jump size_ok + operand_sizes_do_not_match: + asmcmd =err 'operand sizes do not match' + compute size, 0 + jump size_ok + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' + exit +end calminstruction + +calminstruction stos? dest* + asmcmd =x86.=parse_operand =@dest,dest + check @dest.size + jyes size_ok + asmcmd =err 'operand size not specified' + size_ok: + check @dest.type = 'mem' & @dest.mod = 0 & @dest.rm = 5 & ( @dest.segment_prefix = 0 | @dest.segment_prefix = 26h ) + jno invalid_operand + check @dest.size = 2 + jyes stos_word + assemble x86.stosb + exit + stos_word: + assemble x86.stosw + exit + invalid_operand: + asmcmd =err 'invalid operand' + exit +end calminstruction + +calminstruction lods? src* + asmcmd =x86.=parse_operand =@src,src + check @src.size + jyes size_ok + asmcmd =err 'operand size not specified' + size_ok: + check @src.type = 'mem' & @src.mod = 0 & @src.rm = 4 + jno invalid_operand + check @src.segment_prefix = 0 | @src.segment_prefix = 3Eh + jyes segment_prefix_ok + asmcmd =db =@src.=segment_prefix + segment_prefix_ok: + check @src.size = 2 + jyes lods_word + assemble x86.lodsb + exit + lods_word: + assemble x86.lodsw + exit + invalid_operand: + asmcmd =err 'invalid operand' + exit +end calminstruction + +calminstruction scas? dest* + asmcmd =x86.=parse_operand =@dest,dest + check @dest.size + jyes size_ok + asmcmd =err 'operand size not specified' + size_ok: + check @dest.type = 'mem' & @dest.mod = 0 & @dest.rm = 5 & ( @dest.segment_prefix = 0 | @dest.segment_prefix = 26h ) + jno invalid_operand + check @dest.size = 2 + jyes scas_word + assemble x86.scasb + exit + scas_word: + assemble x86.scasw + exit + invalid_operand: + asmcmd =err 'invalid operand' + exit +end calminstruction + +calminstruction xlat? src* + asmcmd =x86.=parse_operand =@src,src + check @src.size > 1 + jno size_ok + asmcmd =err 'invalid operand size' + size_ok: + check @src.type = 'mem' & @src.mod = 0 & @src.rm = 7 + jno invalid_operand + check @src.segment_prefix = 0 | @src.segment_prefix = 3Eh + jyes segment_prefix_ok + asmcmd =db =@src.=segment_prefix + segment_prefix_ok: + assemble x86.xlatb + exit + invalid_operand: + asmcmd =err 'invalid operand' + exit +end calminstruction + +calminstruction in? dest*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + check @dest.size + jyes size_ok + asmcmd =err 'operand size not specified' + size_ok: + check @src.type = 'reg' & @src.size = 2 & @src.rm = 2 & @dest.type = 'reg' & @dest.rm = 0 + jyes in_ax_dx + check @src.type = 'imm' & @dest.type = 'reg' & @dest.rm = 0 + jyes in_ax_imm + asmcmd =err 'invalid combination of operands' + exit + in_ax_dx: + check @dest.size = 2 + jno in_al_dx + asmcmd =db 0EDh + exit + in_al_dx: + asmcmd =db 0ECh + exit + in_ax_imm: + check @dest.size = 2 + jno in_al_imm + asmcmd =db 0E5h,=@src.=imm + exit + in_al_imm: + asmcmd =db 0E4h,=@src.=imm + exit +end calminstruction + +calminstruction out? dest*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + check @src.size + jyes size_ok + asmcmd =err 'operand size not specified' + size_ok: + check @dest.type = 'reg' & @dest.size = 2 & @dest.rm = 2 & @src.type = 'reg' & @src.rm = 0 + jyes out_dx_ax + check @dest.type = 'imm' & @src.type = 'reg' & @src.rm = 0 + jyes out_imm_ax + asmcmd =err 'invalid combination of operands' + exit + out_dx_ax: + check @src.size = 2 + jno out_dx_al + asmcmd =db 0EFh + exit + out_dx_al: + asmcmd =db 0EEh + exit + out_imm_ax: + check @src.size = 2 + jno out_imm_al + asmcmd =db 0E7h,=@dest.=imm + exit + out_imm_al: + asmcmd =db 0E6h,=@dest.=imm + exit +end calminstruction diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/8087.inc b/x86_64_sse2_x87/fasm/examples/x86/include/8087.inc new file mode 100644 index 0000000..ff3099d --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/8087.inc @@ -0,0 +1,572 @@ + +if ~ defined i8087 + + restore i8087 ; this ensures that symbol cannot be forward-referenced + i8087 = 1 + + define x87 x87 + + element st? + + repeat 8, i:0 + element st#i? : st? + i + end repeat + + define x86.qword? :8 + define x86.tword? :10 + define x86.tbyte? :10 + + calminstruction x87.parse_operand namespace, operand + + local size, type, mod, rm, imm + local i, sym, cmd + + match =st?(i), operand + jyes indexed_streg_operand + + asmcmd =x86.=parse_operand namespace, operand + + arrange type, namespace.=type + arrange size, namespace.=size + arrange imm, namespace.=imm + + check type = 'imm' & size = 0 + jno done + check imm eq 1 elementof imm & 1 metadataof imm relativeto st? + jyes streg_operand + check imm relativeto st? & imm = st? + jno done + + compute type, 'streg' + compute mod, 11b + compute rm, 0 + + jump export_register + + streg_operand: + + compute type, 'streg' + compute mod, 11b + compute rm, 1 metadataof imm - st? + + jump export_register + + indexed_streg_operand: + + compute size, 0 + compute type, 'streg' + compute mod, 11b + compute rm, +i + + arrange sym, namespace.=size + publish sym, size + + export_register: + + arrange sym, namespace.=mod + publish sym, mod + + arrange sym, namespace.=rm + publish sym, rm + + arrange sym, namespace.=type + publish sym, type + + done: + + end calminstruction + + iterate , fwait,9Bh, wait,9Bh, fnop,<0D9h,0D0h>, \ + fchs,<0D9h,0E0h>, fabs,<0D9h,0E1h>, ftst,<0D9h,0E4h>, fxam,<0D9h,0E5h>, fld1,<0D9h,0E8h>, \ + fldl2t,<0D9h,0E9h>, fldl2e,<0D9h,0EAh>, fldpi,<0D9h,0EBh>, fldlg2,<0D9h,0ECh>, fldln2,<0D9h,0EDh>, fldz,<0D9h,0EEh>, \ + f2xm1,<0D9h,0F0h>, fyl2x,<0D9h,0F1h>, fptan,<0D9h,0F2h>, fpatan,<0D9h,0F3h>, fxtract,<0D9h,0F4h>, fdecstp,<0D9h,0F6h>, fincstp,<0D9h,0F7h>, fprem,<0D9h,0F8h>, \ + fyl2xp1,<0D9h,0F9h>, fsqrt,<0D9h,0FAh>, frndint,<0D9h,0FCh>, fscale,<0D9h,0FDh>, \ + fneni,<0DBh,0E0h>, fndisi,<0DBh,0E1h>, fnclex,<0DBh,0E2h>, fninit,<0DBh,0E3h>, \ + fcompp,<0DEh,0D9h> + + define x87.instr? db opcode + + calminstruction instr? + assemble x87.instr? + end calminstruction + + end iterate + + iterate op, eni, disi, clex, init + calminstruction f#op? + assemble x87.fwait? + assemble x87.fn#op? + end calminstruction + end iterate + + iterate , fadd,0, fmul,1, fsub,4, fsubr,5, fdiv,6, fdivr,7 + + calminstruction instr? operand& + local dest, src + match dest=,src, operand + jyes st + asmcmd =x87.=parse_operand =@dest,operand + check @dest.type = 'mem' + jno invalid_combination_of_operands + check @dest.size = 4 + jyes mem_dword + check @dest.size = 8 + jyes mem_qword + check @dest.size + jno unknown_size + asmcmd =err 'invalid operand size' + jump mem_dword + unknown_size: + asmcmd =err 'operand size not specified' + mem_dword: + asmcmd =x86.=store_instruction 0D8h,=@dest,postbyte + exit + mem_qword: + asmcmd =x86.=store_instruction 0DCh,=@dest,postbyte + exit + st: + local modrm + asmcmd =x87.=parse_operand =@dest,dest + asmcmd =x87.=parse_operand =@src,src + check @dest.type = 'streg' & @src.type = 'streg' + jno invalid_combination_of_operands + check @dest.rm = 0 + jyes st0_sti + check @src.rm = 0 + jyes sti_st0 + jump invalid_combination_of_operands + st0_sti: + compute modrm, 11b shl 6 + postbyte shl 3 + @src.rm + asmcmd =db 0D8h, modrm + exit + sti_st0: + check postbyte >= 4 + jyes switched + compute modrm, 11b shl 6 + postbyte shl 3 + @dest.rm + asmcmd =db 0DCh, modrm + exit + switched: + compute modrm, 11b shl 6 + (postbyte xor 1) shl 3 + @dest.rm + asmcmd =db 0DCh, modrm + exit + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' + end calminstruction + + end iterate + + iterate , faddp,0, fmulp,1, fsubrp,4, fsubp,5, fdivrp,6, fdivp,7 + + calminstruction instr? operand& + local dest, src, modrm + match , operand + jyes default + match dest=,src, operand + jno invalid_combination_of_operands + asmcmd =x87.=parse_operand =@dest,dest + asmcmd =x87.=parse_operand =@src,src + check @dest.type = 'streg' & @src.type = 'streg' & @src.rm = 0 + jyes ok + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' + exit + default: + compute @dest.rm, 1 + ok: + compute modrm, 11b shl 6 + postbyte shl 3 + @dest.rm + asmcmd =db 0DEh, modrm + end calminstruction + + end iterate + + iterate , fcom,2, fcomp,3 + + calminstruction instr? src:st1 + asmcmd =x87.=parse_operand =@src,src + check @src.type = 'streg' + jyes st + check @src.type = 'mem' + jno invalid_operand + check @src.size = 4 + jyes mem_dword + check @src.size = 8 + jyes mem_qword + check @src.size + jno unknown_size + asmcmd =err 'invalid operand size' + jump mem_dword + unknown_size: + asmcmd =err 'operand size not specified' + mem_dword: st: + asmcmd =x86.=store_instruction 0D8h,=@src,postbyte + exit + mem_qword: + asmcmd =x86.=store_instruction 0DCh,=@src,postbyte + exit + invalid_operand: + asmcmd =err 'invalid operand' + end calminstruction + + end iterate + + iterate , fiadd,0, fimul,1, ficom,2, ficomp,3, fisub,4, fisubr,5, fidiv,6, fidivr,7 + + calminstruction instr? src* + asmcmd =x87.=parse_operand =@src,src + check @src.type = 'mem' + jno invalid_operand + check @src.size = 2 + jyes mem_word + check @src.size = 4 + jyes mem_dword + check @src.size + jno unknown_size + asmcmd =err 'invalid operand size' + jump mem_word + unknown_size: + asmcmd =err 'operand size not specified' + mem_word: + asmcmd =x86.=store_instruction 0DEh,=@src,postbyte + exit + mem_dword: + asmcmd =x86.=store_instruction 0DAh,=@src,postbyte + exit + invalid_operand: + asmcmd =err 'invalid operand' + end calminstruction + + end iterate + + calminstruction fld? src* + asmcmd =x87.=parse_operand =@src,src + check @src.type = 'streg' + jyes st + check @src.type = 'mem' + jno invalid_operand + check @src.size = 4 + jyes mem_dword + check @src.size = 8 + jyes mem_qword + check @src.size = 10 + jyes mem_tword + check @src.size + jno unknown_size + asmcmd =err 'invalid operand size' + jump mem_dword + unknown_size: + asmcmd =err 'operand size not specified' + mem_dword: st: + asmcmd =x86.=store_instruction 0D9h,=@src,0 + exit + mem_qword: + asmcmd =x86.=store_instruction 0DDh,=@src,0 + exit + mem_tword: + asmcmd =x86.=store_instruction 0DBh,=@src,5 + exit + invalid_operand: + asmcmd =err 'invalid operand' + end calminstruction + + calminstruction fst? dest* + asmcmd =x87.=parse_operand =@dest,dest + check @dest.type = 'streg' + jyes st + check @dest.type = 'mem' + jno invalid_operand + check @dest.size = 4 + jyes mem_dword + check @dest.size = 8 + jyes mem_qword + check @dest.size + jno unknown_size + asmcmd =err 'invalid operand size' + jump mem_dword + unknown_size: + asmcmd =err 'operand size not specified' + mem_dword: + asmcmd =x86.=store_instruction 0D9h,=@dest,2 + exit + mem_qword: st: + asmcmd =x86.=store_instruction 0DDh,=@dest,2 + exit + invalid_operand: + asmcmd =err 'invalid operand' + end calminstruction + + calminstruction fstp? dest* + asmcmd =x87.=parse_operand =@dest,dest + check @dest.type = 'streg' + jyes st + check @dest.type = 'mem' + jno invalid_operand + check @dest.size = 4 + jyes mem_dword + check @dest.size = 8 + jyes mem_qword + check @dest.size = 10 + jyes mem_tword + check @dest.size + jno unknown_size + asmcmd =err 'invalid operand size' + jump mem_dword + unknown_size: + asmcmd =err 'operand size not specified' + mem_dword: + asmcmd =x86.=store_instruction 0D9h,=@dest,3 + exit + mem_qword: st: + asmcmd =x86.=store_instruction 0DDh,=@dest,3 + exit + mem_tword: + asmcmd =x86.=store_instruction 0DBh,=@dest,7 + exit + invalid_operand: + asmcmd =err 'invalid operand' + end calminstruction + + calminstruction fild? src* + asmcmd =x87.=parse_operand =@src,src + check @src.type = 'mem' + jno invalid_operand + check @src.size = 2 + jyes mem_word + check @src.size = 4 + jyes mem_dword + check @src.size = 8 + jyes mem_qword + check @src.size + jno unknown_size + asmcmd =err 'invalid operand size' + jump mem_word + unknown_size: + asmcmd =err 'operand size not specified' + mem_word: + asmcmd =x86.=store_instruction 0DFh,=@src,0 + exit + mem_dword: + asmcmd =x86.=store_instruction 0DBh,=@src,0 + exit + mem_qword: + asmcmd =x86.=store_instruction 0DFh,=@src,5 + exit + invalid_operand: + asmcmd =err 'invalid operand' + end calminstruction + + calminstruction fist? dest* + asmcmd =x87.=parse_operand =@dest,dest + check @dest.type = 'mem' + jno invalid_operand + check @dest.size = 2 + jyes mem_word + check @dest.size = 4 + jyes mem_dword + check @dest.size + jno unknown_size + asmcmd =err 'invalid operand size' + jump mem_word + unknown_size: + asmcmd =err 'operand size not specified' + mem_word: + asmcmd =x86.=store_instruction 0DFh,=@dest,2 + exit + mem_dword: + asmcmd =x86.=store_instruction 0DBh,=@dest,2 + exit + invalid_operand: + asmcmd =err 'invalid operand' + end calminstruction + + calminstruction fistp? dest* + asmcmd =x87.=parse_operand =@dest,dest + check @dest.type = 'mem' + jno invalid_operand + check @dest.size = 2 + jyes mem_word + check @dest.size = 4 + jyes mem_dword + check @dest.size = 8 + jyes mem_qword + check @dest.size + jno unknown_size + asmcmd =err 'invalid operand size' + jump mem_word + unknown_size: + asmcmd =err 'operand size not specified' + mem_word: + asmcmd =x86.=store_instruction 0DFh,=@dest,3 + exit + mem_dword: + asmcmd =x86.=store_instruction 0DBh,=@dest,3 + exit + mem_qword: + asmcmd =x86.=store_instruction 0DFh,=@dest,7 + exit + invalid_operand: + asmcmd =err 'invalid operand' + end calminstruction + + calminstruction fisttp? dest* + asmcmd =x87.=parse_operand =@dest,dest + check @dest.type = 'mem' + jno invalid_operand + check @dest.size = 2 + jyes mem_word + check @dest.size = 4 + jyes mem_dword + check @dest.size = 8 + jyes mem_qword + check @dest.size + jno unknown_size + asmcmd =err 'invalid operand size' + jump mem_word + unknown_size: + asmcmd =err 'operand size not specified' + mem_word: + asmcmd =x86.=store_instruction 0DFh,=@dest,1 + exit + mem_dword: + asmcmd =x86.=store_instruction 0DBh,=@dest,1 + exit + mem_qword: + asmcmd =x86.=store_instruction 0DDh,=@dest,1 + exit + invalid_operand: + asmcmd =err 'invalid operand' + end calminstruction + + iterate , fbld,4, fbstp,6 + + calminstruction instr? src* + asmcmd =x87.=parse_operand =@src,src + check @src.type = 'mem' + jno invalid_operand + check @src.size and not 10 + jyes invalid_operand_size + asmcmd =x86.=store_instruction 0DFh,=@src,postbyte + exit + invalid_operand_size: + asmcmd =err 'invalid operand size' + exit + invalid_operand: + asmcmd =err 'invalid operand' + end calminstruction + + end iterate + + calminstruction fxch? src:st1 + asmcmd =x87.=parse_operand =@src,src + check @src.type = 'streg' + jno invalid_operand + local modrm + compute modrm, 11b shl 6 + 1 shl 3 + @src.rm + asmcmd =db 0D9h, modrm + exit + invalid_operand: + asmcmd =err 'invalid operand' + end calminstruction + + iterate , ffree,0DDh, ffreep,0DFh + + calminstruction instr? src* + asmcmd =x87.=parse_operand =@src,src + check @src.type = 'streg' + jno invalid_operand + local modrm + compute modrm, 11b shl 6 + @src.rm + asmcmd =db basecode, modrm + exit + invalid_operand: + asmcmd =err 'invalid operand' + end calminstruction + + end iterate + + define x87.fnstsw_ax? db 0DFh,0E0h + + calminstruction fnstsw? dest* + asmcmd =x86.=parse_operand =@dest,dest + check @dest.size and not 2 + jyes invalid_operand_size + check @dest.type = 'mem' + jyes mem + check @dest.type = 'reg' & @dest.rm = 0 + jno invalid_operand + assemble x87.fnstsw_ax + exit + mem: + asmcmd =x86.=store_instruction 0DDh,=@dest,7 + exit + invalid_operand_size: + asmcmd =err 'invalid operand size' + exit + invalid_operand: + asmcmd =err 'invalid operand' + end calminstruction + + iterate , fldcw,5, fnstcw,7 + + calminstruction instr? dest* + asmcmd =x86.=parse_operand =@dest,dest + check @dest.size and not 2 + jyes invalid_operand_size + check @dest.type = 'mem' + jno invalid_operand + asmcmd =x86.=store_instruction 0D9h,=@dest,postbyte + exit + invalid_operand_size: + asmcmd =err 'invalid operand size' + exit + invalid_operand: + asmcmd =err 'invalid operand' + end calminstruction + + end iterate + + iterate , fldenv,4, fnstenv,6 + + calminstruction instr? dest* + asmcmd =x86.=parse_operand =@dest,dest + check @dest.size and not 14 + jyes invalid_operand_size + check @dest.type = 'mem' + jno invalid_operand + asmcmd =x86.=store_instruction 0D9h,=@dest,postbyte + exit + invalid_operand_size: + asmcmd =err 'invalid operand size' + exit + invalid_operand: + asmcmd =err 'invalid operand' + end calminstruction + + end iterate + + iterate , frstor,4, fnsave,6 + + calminstruction instr? dest* + asmcmd =x86.=parse_operand =@dest,dest + check @dest.size and not 94 + jyes invalid_operand_size + check @dest.type = 'mem' + jno invalid_operand + asmcmd =x86.=store_instruction 0DDh,=@dest,postbyte + exit + invalid_operand_size: + asmcmd =err 'invalid operand size' + exit + invalid_operand: + asmcmd =err 'invalid operand' + end calminstruction + + end iterate + + iterate op, stsw, stcw, stenv, save + calminstruction f#op? dest* + assemble x87.fwait? + asmcmd =fn#op? dest + end calminstruction + end iterate + +end if \ No newline at end of file diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/adx.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/adx.inc new file mode 100644 index 0000000..d91626e --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/adx.inc @@ -0,0 +1,21 @@ + +iterate , adcx,66h, adox,0F3h + macro instr? dest*,src* + x86.parse_operand @dest,dest + x86.parse_operand @src,src + if @dest.type = 'reg' & (@src.type = 'mem' | @src.type = 'reg') + if @src.size <> 0 & @src.size <> @dest.size + err 'operand sizes do not match' + end if + if @dest.size = 8 & x86.mode = 64 + @src.prefix = 48h + else if @dest.size <> 4 + err 'invalid operand size' + end if + @src.opcode_prefix = pfx + x86.store_instruction <0Fh,38h,0F6h>,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro +end iterate diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/aes.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/aes.inc new file mode 100644 index 0000000..28f980a --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/aes.inc @@ -0,0 +1,43 @@ + +include 'sse.inc' + +iterate , aesdec,0DEh, aesenc,0DCh, aesimc,0DBh, aesdeclast,0DFh, aesenclast,0DDh + macro instr? dest*,src* + SSE.basic_instruction 66h,<38h,supp>,16,dest,src + end macro +end iterate + +iterate , aeskeygenassist,0DFh + macro instr? dest*,src*,imm* + SSE.basic_instruction_imm8 66h,<3Ah,supp>,16,dest,src,imm + end macro +end iterate + +if defined AVX + + iterate , aesenc,0DCh, aesenclast,0DDh, aesdec,0DEh, aesdeclast,0DFh + + macro v#instr? dest*,src*,src2* + AVX.basic_instruction VEX_66_0F38_W0,opcode,16,dest,src,src2 + end macro + + end iterate + + iterate , aesimc,0DBh + + macro v#instr? dest*,src* + AVX.single_source_instruction VEX_66_0F38_W0,opcode,16,dest,src + end macro + + end iterate + + iterate , aeskeygenassist,0DFh + + macro v#instr? dest*,src*,imm* + AVX.single_source_instruction_imm8 VEX_66_0F3A_W0,opcode,16,dest,src,imm + end macro + + end iterate + +end if + diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx.inc new file mode 100644 index 0000000..2678d46 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx.inc @@ -0,0 +1,1301 @@ + +if ~ defined AVX + + restore AVX ; this ensures that symbol cannot be forward-referenced + AVX = 1 + + include 'sse4.2.inc' + include 'xsave.inc' + + element AVX.reg + + repeat 8, i:0 + element ymm#i? : AVX.reg + i + end repeat + + if defined xmm8 + repeat 8, i:8 + element ymm#i? : AVX.reg + i + end repeat + end if + + define x86.qqword? :32 + define x86.yword? :32 + + VEX_0F_W0 = 1 + VEX_66_0F_W0 = 1 + 1 shl 8 + VEX_F3_0F_W0 = 1 + 10b shl 8 + VEX_F2_0F_W0 = 1 + 11b shl 8 + + VEX_0F38_W0 = 10b + VEX_66_0F38_W0 = 10b + 1 shl 8 + VEX_F3_0F38_W0 = 10b + 10b shl 8 + VEX_F2_0F38_W0 = 10b + 11b shl 8 + + VEX_0F3A_W0 = 11b + VEX_66_0F3A_W0 = 11b + 1 shl 8 + VEX_F3_0F3A_W0 = 11b + 10b shl 8 + VEX_F2_0F3A_W0 = 11b + 11b shl 8 + + VEX_0F_W1 = VEX_0F_W0 or 8000h + VEX_66_0F_W1 = VEX_66_0F_W0 or 8000h + VEX_F3_0F_W1 = VEX_F3_0F_W0 or 8000h + VEX_F2_0F_W1 = VEX_F2_0F_W0 or 8000h + + VEX_0F38_W1 = VEX_0F38_W0 or 8000h + VEX_66_0F38_W1 = VEX_66_0F38_W0 or 8000h + VEX_F3_0F38_W1 = VEX_F3_0F38_W0 or 8000h + VEX_F2_0F38_W1 = VEX_F2_0F38_W0 or 8000h + + VEX_0F3A_W1 = VEX_0F3A_W0 or 8000h + VEX_66_0F3A_W1 = VEX_66_0F3A_W0 or 8000h + VEX_F3_0F3A_W1 = VEX_F3_0F3A_W0 or 8000h + VEX_F2_0F3A_W1 = VEX_F2_0F3A_W0 or 8000h + + calminstruction AVX.parse_operand namespace, operand + + local size, type, mod, rm, imm + local i, sym, cmd + + asmcmd =x86.=parse_operand namespace, operand + + arrange type, namespace.=type + arrange size, namespace.=size + arrange imm, namespace.=imm + + check type = 'reg' & size = 1 & rm >= 4 & (~ defined x86.REX_FORBIDDEN | rm and x86.REX_FORBIDDEN) + jyes invalid_operand + check type = 'imm' & size = 0 + jno done + + check imm eq 1 elementof imm & 1 metadataof imm relativeto SSE.reg + jyes xmm_register + check imm eq 1 elementof imm & 1 metadataof imm relativeto AVX.reg + jyes ymm_register + exit + + invalid_operand: + asmcmd =err 'invalid operand' + + xmm_register: + + compute rm, 1 metadataof imm - SSE.reg + compute size, 16 + + jump export_mmreg + + ymm_register: + + compute rm, 1 metadataof imm - AVX.reg + compute size, 32 + + export_mmreg: + + compute type, 'mmreg' + compute mod, 11b + + arrange sym, namespace.=mod + publish sym, mod + + arrange sym, namespace.=rm + publish sym, rm + + arrange sym, namespace.=type + publish sym, type + + arrange sym, namespace.=size + publish sym, size + + done: + + end calminstruction + + calminstruction AVX.store_instruction vsize*,vex_mpw*,opcode*,rm_operand*,reg*,vreg:0,imm_size:0,imm + + local segment_prefix, vex + local mode, mod, rm + local scale, index, base + local displacement, displacement_size, auto_relative + local sym + + arrange segment_prefix, rm_operand.=segment_prefix + + arrange mode, rm_operand.=mode + arrange mod, rm_operand.=mod + arrange rm, rm_operand.=rm + + arrange scale, rm_operand.=scale + arrange index, rm_operand.=index + arrange base, rm_operand.=base + + arrange displacement_size, rm_operand.=displacement_size + arrange displacement, rm_operand.=displacement + arrange auto_relative, rm_operand.=auto_relative + + check segment_prefix + jno segment_prefix_ok + + check mode = 64 + jyes segment_in_long_mode + check mode = 16 & ( rm = 2 | rm = 3 | ( mod > 0 & rm = 6 ) ) + jyes ss_segment_default + check mode = 32 & ( ( mod > 0 & rm = 5 ) | ( rm = 4 & base = 4 ) | ( mod > 0 & rm = 4 & base = 5 ) ) + jyes ss_segment_default + + ds_segment_default: + check segment_prefix = 3Eh + jyes segment_prefix_ok + jump store_segment_prefix + ss_segment_default: + check segment_prefix = 36h + jyes segment_prefix_ok + jump store_segment_prefix + segment_in_long_mode: + check segment_prefix < 64h + jyes segment_prefix_ok + store_segment_prefix: + asmcmd =db segment_prefix + segment_prefix_ok: + + check mod <> 11b & mode <> x86.mode + jno addressing_prefix_ok + check mode = 64 | (mode = 16 & x86.mode = 64) + jno store_addressing_prefix + asmcmd =err 'illegal addressing mode' + store_addressing_prefix: + asmcmd =db 67h + addressing_prefix_ok: + + compute vex, vex_mpw + vex_L: + check vsize = 32 + jno vex_B + compute vex, vex or 1 shl 10 + vex_B: + check rm and 1000b | (mod <> 11b & mode > 16 & rm = 4 & base and 1000b) + jno vex_X + compute vex, vex or 1 shl 5 + vex_X: + check mod <> 11b & mode > 16 & rm = 4 & index and 1000b + jno vex_R + compute vex, vex or 1 shl 6 + vex_R: + check reg and 1000b + jno vex_vvvv + compute vex, vex or 1 shl 7 + vex_vvvv: + compute vex, vex or (vreg and 1111b) shl 11 + + check x86.mode < 64 & vex and 01000000_11100000b + jno allowed + asmcmd =err 'instruction requires long mode' + allowed: + + local byte2, byte3 + check vex and 10000000_01111111b <> 1 + jyes vex_3byte + vex_2byte: + compute byte2, ((vex and 10000000b) or ((vex shr 8) and 1111111b)) xor 11111000b + asmcmd =db 0C5h,byte2 + jump vex_done + vex_3byte: + compute byte2, (vex and 11111111b) xor 11100000b + compute byte3, (vex shr 8) xor 01111000b + asmcmd =db 0C4h,byte2,byte3 + vex_done: + + local modrm, sib + + compute modrm, mod shl 6 + (reg and 111b) shl 3 + rm and 111b + asmcmd =db opcode, modrm + + check mod <> 11b & rm = 4 & mode <> 16 + jno sib_ok + compute sib, (bsf scale) shl 6 + (index and 111b) shl 3 + base and 111b + asmcmd =db sib + sib_ok: + + check displacement_size = 1 + jyes displacement_8bit + check displacement_size = 2 + jyes displacement_16bit + check displacement_size = 4 | displacement_size = 8 + jno displacement_ok + + compute displacement, displacement + + check auto_relative + jno auto_relative_ok + check imm_size < 8 + jyes adjust_auto_relative_displacement + compute displacement, displacement - ($ + 4 + 4) + jump auto_relative_ok + adjust_auto_relative_displacement: + compute displacement, displacement - ($ + 4 + imm_size) + auto_relative_ok: + + check mode = 64 & displacement relativeto 0 + jno displacement_ready + check displacement - 1 shl 64 >= -80000000h & displacement < 1 shl 64 + jyes adjust_displacement_wrap + check displacement >= -80000000h & displacement < 80000000h + jyes displacement_ready + asmcmd =err 'address value out of signed range' + adjust_displacement_wrap: + compute displacement, displacement - 1 shl 64 + displacement_ready: + + arrange sym, rm_operand.=displacement + publish sym, displacement + + asmcmd =dd rm_operand.=displacement + + jump displacement_ok + displacement_16bit: + asmcmd =dw rm_operand.=displacement + jump displacement_ok + displacement_8bit: + asmcmd =db rm_operand.=displacement + displacement_ok: + + check imm_size = 1 + jyes immediate_8bit + check imm_size = 2 + jyes immediate_16bit + check imm_size = 4 + jyes immediate_32bit + check imm_size = 8 + jno immediate_ok + asmcmd =x86.=simm32 imm + jump immediate_ok + immediate_32bit: + asmcmd =dd imm + jump immediate_ok + immediate_16bit: + asmcmd =dw imm + jump immediate_ok + immediate_8bit: + asmcmd =db imm + immediate_ok: + + end calminstruction + + macro AVX.basic_instruction vex_mpw,opcode,msize,dest,src,src2 + AVX.parse_operand @dest,dest + AVX.parse_operand @src,src + AVX.parse_operand @src2,src2 + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') + if msize & (@dest.size < msize | (@dest.size > msize & @dest.size <> 16) | (@src2.type = 'mem' & @src2.size and not msize)) + err 'invalid operand size' + else if @src.size <> @dest.size | (@src2.size and not @dest.size & (@src2.type = 'mmreg' | msize = 0)) + err 'operand sizes do not match' + end if + AVX.store_instruction @dest.size,vex_mpw,opcode,@src2,@dest.rm,@src.rm + else + err 'invalid combination of operands' + end if + end macro + + macro AVX.basic_instruction_imm8 vex_mpw,opcode,msize,dest,src,src2,aux + AVX.parse_operand @dest,dest + AVX.parse_operand @src,src + AVX.parse_operand @src2,src2 + x86.parse_operand @aux,aux + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') & @aux.type = 'imm' + if (msize & (@dest.size < msize | (@dest.size > msize & @dest.size <> 16) | (@src2.type = 'mem' & @src2.size and not msize))) | @aux.size and not 1 + err 'invalid operand size' + else if @src.size <> @dest.size | (@src2.size and not @dest.size & (@src2.type = 'mmreg' | msize = 0)) + err 'operand sizes do not match' + end if + AVX.store_instruction @dest.size,vex_mpw,opcode,@src2,@dest.rm,@src.rm,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + + macro AVX.single_source_instruction vex_mpw,opcode,msize,dest,src + AVX.parse_operand @dest,dest + AVX.parse_operand @src,src + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') + if msize & (@dest.size < msize | (@dest.size > msize & @dest.size <> 16) | (@src.type = 'mem' & @src.size and not msize)) + err 'invalid operand size' + else if @src.size and not @dest.size & (@src.type = 'mmreg' | msize = 0) + err 'operand sizes do not match' + end if + AVX.store_instruction @dest.size,vex_mpw,opcode,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + macro AVX.single_source_instruction_imm8 vex_mpw,opcode,msize,dest,src,aux + AVX.parse_operand @dest,dest + AVX.parse_operand @src,src + x86.parse_operand @aux,aux + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') & @aux.type = 'imm' + if (msize & (@dest.size < msize | (@dest.size > msize & @dest.size <> 16) | (@src.type = 'mem' & @src.size and not msize))) | @aux.size and not 1 + err 'invalid operand size' + else if @src.size and not @dest.size & (@src.type = 'mmreg' | msize = 0) + err 'operand sizes do not match' + end if + AVX.store_instruction @dest.size,vex_mpw,opcode,@src,@dest.rm,,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + + iterate , add,58h, mul,59h, sub,5Ch, min,5Dh, div,5Eh, max,5Fh + + macro v#instr#pd? dest*,src*,src2* + AVX.basic_instruction VEX_66_0F_W0,opcode,0,dest,src,src2 + end macro + + macro v#instr#ps? dest*,src*,src2* + AVX.basic_instruction VEX_0F_W0,opcode,0,dest,src,src2 + end macro + + macro v#instr#sd? dest*,src*,src2* + AVX.basic_instruction VEX_F2_0F_W0,opcode,8,dest,src,src2 + end macro + + macro v#instr#ss? dest*,src*,src2* + AVX.basic_instruction VEX_F3_0F_W0,opcode,4,dest,src,src2 + end macro + + end iterate + + iterate , and,54h, andn,55h, or,56h, unpckh,15h, unpckl,14h, xor,57h + + macro v#instr#pd? dest*,src*,src2* + AVX.basic_instruction VEX_66_0F_W0,opcode,0,dest,src,src2 + end macro + + macro v#instr#ps? dest*,src*,src2* + AVX.basic_instruction VEX_0F_W0,opcode,0,dest,src,src2 + end macro + + end iterate + + iterate , addsub,0D0h, hadd,7Ch, hsub,7Dh + + macro v#instr#pd? dest*,src*,src2* + AVX.basic_instruction VEX_66_0F_W0,opcode,0,dest,src,src2 + end macro + + macro v#instr#ps? dest*,src*,src2* + AVX.basic_instruction VEX_F2_0F_W0,opcode,0,dest,src,src2 + end macro + + end iterate + + iterate , rsqrt,52h, rcp,53h + + macro v#instr#ps? dest*,src* + AVX.single_source_instruction VEX_0F_W0,opcode,0,dest,src + end macro + + macro v#instr#ss? dest*,src*,src2* + AVX.basic_instruction VEX_F3_0F_W0,opcode,4,dest,src,src2 + end macro + + end iterate + + macro vsqrtpd? dest*,src* + AVX.single_source_instruction VEX_66_0F_W0,51h,0,dest,src + end macro + + macro vsqrtps? dest*,src* + AVX.single_source_instruction VEX_0F_W0,51h,0,dest,src + end macro + + macro vsqrtsd? dest*,src*,src2* + AVX.basic_instruction VEX_F2_0F_W0,51h,8,dest,src,src2 + end macro + + macro vsqrtss? dest*,src*,src2* + AVX.basic_instruction VEX_F3_0F_W0,51h,4,dest,src,src2 + end macro + + macro vroundpd? dest*,src*,aux* + AVX.single_source_instruction_imm8 VEX_66_0F3A_W0,9,0,dest,src,aux + end macro + + macro vroundps? dest*,src*,aux* + AVX.single_source_instruction_imm8 VEX_66_0F3A_W0,8,0,dest,src,aux + end macro + + macro vroundsd? dest*,src*,src2*,aux* + AVX.basic_instruction_imm8 VEX_66_0F3A_W0,0Bh,8,dest,src,src2,aux + end macro + + macro vroundss? dest*,src*,src2*,aux* + AVX.basic_instruction_imm8 VEX_66_0F3A_W0,0Ah,4,dest,src,src2,aux + end macro + + macro vshufpd? dest*,src*,src2*,aux* + AVX.basic_instruction_imm8 VEX_66_0F_W0,0C6h,0,dest,src,src2,aux + end macro + + macro vshufps? dest*,src*,src2*,aux* + AVX.basic_instruction_imm8 VEX_0F_W0,0C6h,0,dest,src,src2,aux + end macro + + iterate , blendps,0Ch, blendpd,0Dh + + macro v#instr? dest*,src*,src2*,imm* + AVX.basic_instruction_imm8 VEX_66_0F3A_W0,opcode,0,dest,src,src2,imm + end macro + + end iterate + + iterate , blendvps,4Ah, blendvpd,4Bh + + macro v#instr? dest*,src*,src2*,mask* + AVX.parse_operand @dest,dest + AVX.parse_operand @src,src + AVX.parse_operand @src2,src2 + AVX.parse_operand @aux,mask + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') & @aux.type = 'mmreg' + if @src.size <> @dest.size | @src2.size and not @dest.size | @aux.size <> @dest.size + err 'operand sizes do not match' + end if + AVX.store_instruction @dest.size,VEX_66_0F3A_W0,opcode,@src2,@dest.rm,@src.rm,1,(@aux.rm and 1111b) shl 4 + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + macro vbroadcastss? dest*,src* + AVX.parse_operand @dest,dest + AVX.parse_operand @src,src + if @dest.type = 'mmreg' & @src.type = 'mem' + if @src.size and not 4 + err 'invalid operand size' + end if + AVX.store_instruction @dest.size,VEX_66_0F38_W0,18h,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + iterate , vbroadcastsd,19h,8, vbroadcastf128,1Ah,16 + + macro instr? dest*,src* + AVX.parse_operand @dest,dest + AVX.parse_operand @src,src + if @dest.type = 'mmreg' & @src.type = 'mem' + if @dest.size <> 32 | @src.size and not msize + err 'invalid operand size' + end if + AVX.store_instruction 32,VEX_66_0F38_W0,opcode,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + macro vextractps? dest*,src*,aux* + x86.parse_operand @dest,dest + AVX.parse_operand @src,src + x86.parse_operand @aux,aux + if (@dest.type = 'reg' | @dest.type = 'mem') & @src.type = 'mmreg' & @aux.type = 'imm' + if @dest.size and not 4 | @src.size <> 16 | @aux.size and not 1 + err 'invalid operand size' + end if + AVX.store_instruction 16,VEX_66_0F3A_W0,17h,@dest,@src.rm,,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + + macro vinsertps? dest*,src*,src2*,aux* + AVX.parse_operand @dest,dest + AVX.parse_operand @src,src + AVX.parse_operand @src2,src2 + x86.parse_operand @aux,aux + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'mmreg' | @src2.type = 'mem') & @aux.type = 'imm' + if @dest.size <> 16 | @src.size <> 16 | (@src2.type = 'mmreg' & @src2.size <> 16) | (@src2.type = 'mem' & @src2.size and not 4) | @aux.size and not 1 + err 'invalid operand size' + end if + AVX.store_instruction 16,VEX_66_0F3A_W0,21h,@src2,@dest.rm,@src.rm,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + + macro vextractf128? dest*,src*,aux* + AVX.parse_operand @dest,dest + AVX.parse_operand @src,src + x86.parse_operand @aux,aux + if (@dest.type = 'mmreg' | @dest.type = 'mem') & @src.type = 'mmreg' & @aux.type = 'imm' + if @dest.size and not 16 | @src.size <> 32 | @aux.size and not 1 + err 'invalid operand size' + end if + AVX.store_instruction 32,VEX_66_0F3A_W0,19h,@dest,@src.rm,,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + + macro vinsertf128? dest*,src*,src2*,aux* + AVX.parse_operand @dest,dest + AVX.parse_operand @src,src + AVX.parse_operand @src2,src2 + x86.parse_operand @aux,aux + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'mmreg' | @src2.type = 'mem') & @aux.type = 'imm' + if @dest.size <> 32 | @src.size <> 32 | @src2.size and not 16 | @aux.size and not 1 + err 'invalid operand size' + end if + AVX.store_instruction 32,VEX_66_0F3A_W0,18h,@src2,@dest.rm,@src.rm,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + + iterate , cmp,0C2h + + macro v#instr#pd? dest*,src*,src2*,imm* + AVX.basic_instruction_imm8 VEX_66_0F_W0,opcode,0,dest,src,src2,imm + end macro + + macro v#instr#ps? dest*,src*,src2*,imm* + AVX.basic_instruction_imm8 VEX_0F_W0,opcode,0,dest,src,src2,imm + end macro + + macro v#instr#sd? dest*,src*,src2*,imm* + AVX.basic_instruction_imm8 VEX_F2_0F_W0,opcode,8,dest,src,src2,imm + end macro + + macro v#instr#ss? dest*,src*,src2*,imm* + AVX.basic_instruction_imm8 VEX_F3_0F_W0,opcode,4,dest,src,src2,imm + end macro + + end iterate + + iterate , eq,0, lt,1, le,2, unord,3, neq,4, nlt,5, nle,6, ord,7, \ + eq_uq,8, nge,9, ngt,0Ah, false,0Bh, neq_qq,0Ch, ge,0Dh, gt,0Eh, true,0Fh, \ + eq_os,10h, lt_oq,11h, le_oq,12h, unord_s,13h, neq_us,14h, nlt_uq,15h, nle_uq,16h, ord_s,17h, \ + eq_us,18h, nge_uq,19h, ngt_uq,1Ah, false_os,1Bh, neq_os,1Ch, ge_oq,1Dh, gt_oq,1Eh, true_us,1Fh + + macro vcmp#cond#pd? dest*,src*,src2* + vcmppd dest,src,src2,code + end macro + + macro vcmp#cond#ps? dest*,src*,src2* + vcmpps dest,src,src2,code + end macro + + macro vcmp#cond#sd? dest*,src*,src2* + vcmpsd dest,src,src2,code + end macro + + macro vcmp#cond#ss? dest*,src*,src2* + vcmpss dest,src,src2,code + end macro + + end iterate + + iterate , vcomiss,VEX_0F_W0,2Fh,4, vcomisd,VEX_66_0F_W0,2Fh,8, vucomiss,VEX_0F_W0,2Eh,4, vucomisd,VEX_66_0F_W0,2Eh,8 + + macro instr? dest*,src* + AVX.single_source_instruction vex_mpw,opcode,msize,dest,src + end macro + + end iterate + + iterate , vcvtdq2pd,VEX_F3_0F_W0,0E6h, vcvtps2pd,VEX_0F_W0,5Ah + + macro instr? dest*,src* + AVX.parse_operand @dest,dest + AVX.parse_operand @src,src + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') + if (@src.type = 'mem' & @src.size and not (@dest.size shr 1)) | (@src.type = 'mmreg' & @src.size <> 16) + err 'invalid operand size' + end if + AVX.store_instruction @dest.size,vex_mpw,opcode,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , vcvtpd2dq,VEX_F2_0F_W0,0E6h, vcvttpd2dq,VEX_66_0F_W0,0E6h, vcvtpd2ps,VEX_66_0F_W0,5Ah + + macro instr? dest*,src* + AVX.parse_operand @dest,dest + AVX.parse_operand @src,src + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') + if @src.size = 0 + err ' operand size not specified' + else if @dest.size <> 16 | (@src.size <> 16 & @src.size <> 32) + err 'invalid operand size' + end if + AVX.store_instruction @dest.size,vex_mpw,opcode,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + macro vcvtdq2ps? dest*,src* + AVX.single_source_instruction VEX_0F_W0,5Bh,0,dest,src + end macro + + macro vcvtps2dq? dest*,src* + AVX.single_source_instruction VEX_66_0F_W0,5Bh,0,dest,src + end macro + + macro vcvttps2dq? dest*,src* + AVX.single_source_instruction VEX_F3_0F_W0,5Bh,0,dest,src + end macro + + iterate , vcvtsd2si,VEX_F2_0F,2Dh,8, vcvttsd2si,VEX_F2_0F,2Ch,8, vcvtss2si,VEX_F3_0F,2Dh,4, vcvttss2si,VEX_F3_0F,2Ch,4 + + macro instr? dest*,src* + x86.parse_operand @dest,dest + AVX.parse_operand @src,src + if @dest.type = 'reg' & (@src.type = 'mem' | @src.type = 'mmreg') + if (@dest.size <> 4 & @dest.size <> 8) | (@src.type = 'mem' & @src.size and not msize) | (@src.type = 'mmreg' & @src.size <> 16) + err 'invalid operand size' + end if + if @dest.size = 8 + if x86.mode < 64 + err 'instruction requires long mode' + end if + AVX.store_instruction 16,vex_mp#_W1,opcode,@src,@dest.rm + else + AVX.store_instruction 16,vex_mp#_W0,opcode,@src,@dest.rm + end if + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + macro vcvtsd2ss? dest*,src*,src2* + AVX.basic_instruction VEX_F2_0F_W0,5Ah,8,dest,src,src2 + end macro + + macro vcvtss2sd? dest*,src*,src2* + AVX.basic_instruction VEX_F3_0F_W0,5Ah,4,dest,src,src2 + end macro + + iterate , vcvtsi2sd,VEX_F2_0F,2Ah, vcvtsi2ss,VEX_F3_0F,2Ah + + macro instr? dest*,src*,src2* + AVX.parse_operand @dest,dest + AVX.parse_operand @src,src + x86.parse_operand @src2,src2 + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'reg' | @src2.type = 'mem') + if @src.size = 0 + err ' operand size not specified' + else if @dest.size <> 16 | @src.size <> 16 | (@src2.size <> 4 & @src2.size <> 8) + err 'invalid operand size' + end if + if @src2.size = 8 + if x86.mode < 64 + err 'instruction requires long mode' + end if + AVX.store_instruction 16,vex_mp#_W1,opcode,@src2,@dest.rm,@src.rm + else + AVX.store_instruction 16,vex_mp#_W0,opcode,@src2,@dest.rm,@src.rm + end if + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + macro vdppd? dest*,src*,src2*,imm* + AVX.basic_instruction_imm8 VEX_66_0F3A_W0,41h,16,dest,src,src2,imm + end macro + + macro vdpps? dest*,src*,src2*,imm* + AVX.basic_instruction_imm8 VEX_66_0F3A_W0,40h,16,dest,src,src2,imm + end macro + + macro vlddqu? dest*,src* + AVX.parse_operand @dest,dest + x86.parse_operand @src,src + if @dest.type = 'mmreg' & @src.type = 'mem' + if @src.size and not @dest.size + err 'operand sizes do not match' + end if + AVX.store_instruction @dest.size,VEX_F2_0F_W0,0F0h,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + iterate , vldmxcsr,2, vstmxcsr,3 + + macro instr? src* + x86.parse_operand @src,src + if @src.type = 'mem' + if @src.size and not 4 + err 'invalid operand size' + end if + AVX.store_instruction 16,VEX_0F_W0,0AEh,@src,postbyte + else + err 'invalid operand' + end if + end macro + + end iterate + + macro vmaskmovdqu? dest*,src* + AVX.parse_operand @dest,dest + AVX.parse_operand @src,src + if @dest.type = 'mmreg' & @src.type = 'mmreg' + if @dest.size <> 16 | @src.size <> 16 + err 'invalid operand size' + end if + AVX.store_instruction 16,VEX_66_0F_W0,0F7h,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + iterate , vmaskmovps,2Ch, vmaskmovpd,2Dh + + macro instr? dest*,src*,src2* + AVX.parse_operand @dest,dest + AVX.parse_operand @src,src + AVX.parse_operand @src2,src2 + if @dest.type = 'mmreg' & @src.type = 'mmreg' & @src2.type = 'mem' + if @src.size <> @dest.size | @src2.size and not @dest.size + err 'operand sizes do not match' + end if + AVX.store_instruction @dest.size,VEX_66_0F38_W0,opcode,@src2,@dest.rm,@src.rm + else if @dest.type = 'mem' & @src.type = 'mmreg' & @src2.type = 'mmreg' + if @src.size <> @src2.size | @dest.size and not @src.size + err 'operand sizes do not match' + end if + AVX.store_instruction @dest.size,VEX_66_0F38_W0,opcode+2,@dest,@src2.rm,@src.rm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , vmovapd,VEX_66_0F_W0,28h,29h, vmovaps,VEX_0F_W0,28h,29h, vmovdqa,VEX_66_0F_W0,6Fh,7Fh, vmovdqu,VEX_F3_0F_W0,6Fh,7Fh, vmovupd,VEX_66_0F_W0,10h,11h, vmovups,VEX_0F_W0,10h,11h + + macro instr? dest*,src* + AVX.parse_operand @dest,dest + AVX.parse_operand @src,src + if @dest.type = 'mmreg' & (@src.type = 'mmreg' | @src.type = 'mem') + if @src.size and not @dest.size + err 'operand sizes do not match' + end if + AVX.store_instruction @dest.size,vex_mpw,opcode_rm,@src,@dest.rm + else if @dest.type = 'mem' & @src.type = 'mmreg' + if @dest.size and not @src.size + err 'operand sizes do not match' + end if + AVX.store_instruction @src.size,vex_mpw,opcode_mr,@dest,@src.rm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + macro vmovd? dest*,src* + AVX.parse_operand @dest,dest + AVX.parse_operand @src,src + if @dest.type = 'mmreg' & (@src.type = 'reg' | @src.type = 'mem') + if @dest.size <> 16 | @src.size and not 4 + err 'invalid operand size' + end if + AVX.store_instruction 16,VEX_66_0F_W0,6Eh,@src,@dest.rm + else if (@dest.type = 'reg' | @dest.type = 'mem') & @src.type = 'mmreg' + if @dest.size and not 4 | @src.size <> 16 + err 'operand sizes do not match' + end if + AVX.store_instruction 16,VEX_66_0F_W0,7Eh,@dest,@src.rm + else + err 'invalid combination of operands' + end if + end macro + + macro vmovq? dest*,src* + AVX.parse_operand @dest,dest + AVX.parse_operand @src,src + if @dest.type = 'mmreg' & (@src.type = 'mmreg' | @src.type = 'mem') + if @dest.size <> 16 | (@src.type = 'mmreg' & @src.size <> 16) | (@src.type = 'mem' and @src.size and not 8) + err 'invalid operand size' + end if + AVX.store_instruction 16,VEX_F3_0F_W0,7Eh,@src,@dest.rm + else if @dest.type = 'mem' & @src.type = 'mmreg' + if @dest.size and not 8 | @src.size <> 16 + err 'invalid operand size' + end if + AVX.store_instruction 16,VEX_66_0F_W0,0D6h,@dest,@src.rm + else if @dest.type = 'mmreg' & @src.type = 'reg' + if @dest.size <> 16 | @src.size <> 8 + err 'invalid operand size' + end if + if x86.mode < 64 + err 'instruction requires long mode' + end if + AVX.store_instruction 16,VEX_66_0F_W1,6Eh,@src,@dest.rm + else if @dest.type = 'reg' & @src.type = 'mmreg' + if @dest.size <> 8 | @src.size <> 16 + err 'invalid operand size' + end if + if x86.mode < 64 + err 'instruction requires long mode' + end if + AVX.store_instruction 16,VEX_66_0F_W1,7Eh,@dest,@src.rm + else + err 'invalid combination of operands' + end if + end macro + + macro vmovddup? dest*,src* + AVX.parse_operand @dest,dest + AVX.parse_operand @src,src + if @dest.type = 'mmreg' & (@src.type = 'mmreg' | @src.type = 'mem') + if ((@src.type = 'mmreg' | @dest.size = 32) & @src.size and not @dest.size) | (@src.type = 'mem' & @dest.size = 16 & @src.size and not 8) + err 'operand sizes do not match' + end if + AVX.store_instruction @dest.size,VEX_F2_0F_W0,12h,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + iterate , vmovhlps,12h, vmovlhps,16h + + macro instr? dest*,src*,src2* + AVX.parse_operand @dest,dest + AVX.parse_operand @src,src + AVX.parse_operand @src2,src2 + if @dest.type = 'mmreg' & @src.type = 'mmreg' & @src2.type = 'mmreg' + if @dest.size <> 16 + err 'invalid operand size' + else if @src.size <> @dest.size | @src2.size <> @dest.size + err 'operand sizes do not match' + end if + AVX.store_instruction 16,VEX_0F_W0,opcode,@src2,@dest.rm,@src.rm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , vmovhpd,VEX_66_0F_W0,16h, vmovhps,VEX_0F_W0,16h, vmovlpd,VEX_66_0F_W0,12h, vmovlps,VEX_0F_W0,12h + + macro instr? dest*,src*,src2 + AVX.parse_operand @dest,dest + AVX.parse_operand @src,src + match , src2 + if @dest.type = 'mem' & @src.type = 'mmreg' + if @dest.size and not 8 | @src.size <> 16 + err 'invalid operand size' + end if + AVX.store_instruction 16,vex_mpw,opcode+1,@dest,@src.rm + else + err 'invalid combination of operands' + end if + else + AVX.parse_operand @src2,src2 + if @dest.type = 'mmreg' & @src.type = 'mmreg' & @src2.type = 'mem' + if @dest.size <> 16 | @src.size <> 16 | @src2.size and not 8 + err 'invalid operand size' + end if + AVX.store_instruction 16,vex_mpw,opcode,@src2,@dest.rm,@src.rm + else + err 'invalid combination of operands' + end if + end match + end macro + + end iterate + + iterate , vmovmskpd,VEX_66_0F_W0, vmovmskps,VEX_0F_W0 + + macro instr? dest*,src* + x86.parse_operand @dest,dest + AVX.parse_operand @src,src + if @dest.type = 'reg' & @src.type = 'mmreg' + if @dest.size <> 4 & (x86.mode < 64 | @dest.size <> 8) + err 'invalid operand size' + end if + AVX.store_instruction @src.size,vex_mpw,50h,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , vmovntdq,VEX_66_0F_W0,0E7h, vmovntpd,VEX_66_0F_W0,2Bh, vmovntps,VEX_0F_W0,2Bh + + macro instr? dest*,src* + x86.parse_operand @dest,dest + AVX.parse_operand @src,src + if @dest.type = 'mem' & @src.type = 'mmreg' + if @dest.size and not @src.size + err 'operand sizes do not match' + end if + AVX.store_instruction @src.size,vex_mpw,opcode,@dest,@src.rm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + macro vmovntdqa? dest*,src* + AVX.parse_operand @dest,dest + x86.parse_operand @src,src + if @dest.type = 'mmreg' & @src.type = 'mem' + if @dest.size <> 16 + err 'invalid operand size' + else if @src.size and not @dest.size + err 'operand sizes do not match' + end if + AVX.store_instruction 16,VEX_66_0F38_W0,2Ah,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + iterate , vmovsd,VEX_F2_0F_W0,8, vmovss,VEX_F3_0F_W0,4 + + macro instr? dest*,src*,src2 + AVX.parse_operand @dest,dest + AVX.parse_operand @src,src + match , src2 + if @dest.type = 'mmreg' & @src.type = 'mem' + if @dest.size <> 16 | @src.size and not msize + err 'invalid operand size' + end if + AVX.store_instruction 16,vex_mpw,10h,@src,@dest.rm + else if @dest.type = 'mem' & @src.type = 'mmreg' + if @dest.size and not msize | @src.size <> 16 + err 'invalid operand size' + end if + AVX.store_instruction 16,vex_mpw,11h,@dest,@src.rm + else + err 'invalid combination of operands' + end if + else + AVX.parse_operand @src2,src2 + if @dest.type = 'mmreg' & @src.type = 'mmreg' & @src2.type = 'mmreg' + if @dest.size <> 16 | @src.size <> 16 | @src2.size <> 16 + err 'invalid operand size' + end if + AVX.store_instruction 16,vex_mpw,10h,@src2,@dest.rm,@src.rm + else + err 'invalid combination of operands' + end if + end match + end macro + + end iterate + + macro vmovshdup? dest*,src* + AVX.single_source_instruction VEX_F3_0F_W0,16h,0,dest,src + end macro + + macro vmovsldup? dest*,src* + AVX.single_source_instruction VEX_F3_0F_W0,12h,0,dest,src + end macro + + macro vperm2f128? dest*,src*,src2*,imm* + AVX.basic_instruction_imm8 VEX_66_0F3A_W0,6,32,dest,src,src2,imm + end macro + + iterate , vpermilps,0Ch,4, vpermilpd,0Dh,5 + + macro instr? dest*,src*,src2* + AVX.parse_operand @dest,dest + AVX.parse_operand @src,src + AVX.parse_operand @src2,src2 + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') + if @src.size <> @dest.size | @src2.size and not @dest.size + err 'operand sizes do not match' + end if + AVX.store_instruction @dest.size,VEX_66_0F38_W0,opcode_rrm,@src2,@dest.rm,@src.rm + else if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') & @src2.type = 'imm' + if @src2.size and not 1 + err 'invalid operand size' + else if @src.size and not @dest.size + err 'operand sizes do not match' + end if + AVX.store_instruction @dest.size,VEX_66_0F3A_W0,opcode_rri,@src,@dest.rm,,1,@src2.imm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , packsswb,63h, packuswb,67h, packssdw,6Bh, paddb,0FCh, paddw,0FDh, paddd,0FEh, paddq,0D4h, paddsb,0ECh, paddsw,0EDh, paddusb,0DCh, paddusw,0DDh, \ + pand,0DBh, pandn,0DFh, pavgb,0E0h, pavgw,0E3h, pcmpeqb,74h, pcmpeqw,75h, pcmpeqd,76h, pcmpgtb,64h, pcmpgtw,65h, pcmpgtd,66h, \ + pmaddwd,0F5h, pmaxsw,0EEh, pmaxub,0DEh, pminsw,0EAh, pminub,0DAh, pmulhuw,0E4h, pmulhw,0E5h, pmullw,0D5h, pmuludq,0F4h, \ + por,0EBh, psadbw,0F6h, psubb,0F8h, psubw,0F9h, psubd,0FAh, psubq,0FBh, psubsb,0E8h, psubsw,0E9h, psubusb,0D8h, psubusw,0D9h, \ + punpckhbw,68h, punpckhwd,69h, punpckhdq,6Ah, punpckhqdq,6Dh, punpcklbw,60h, punpcklwd,61h, punpckldq,62h, punpcklqdq,6Ch, pxor,0EFh + + macro v#instr? dest*,src*,src2* + AVX.basic_instruction VEX_66_0F_W0,opcode,16,dest,src,src2 + end macro + + end iterate + + iterate , packusdw,2Bh, pcmpeqq,29h, pcmpgtq,37h, phaddw,1, phaddd,2, phaddsw,3, phsubw,5, phsubd,6, phsubsw,7, pmaddubsw,4, \ + pmaxsb,3Ch, pmaxsd,3Dh, pmaxuw,3Eh, pmaxud,3Fh, pminsb,38h, pminsd,39h, pminuw,3Ah, pminud,3Bh, pmulhrsw,0Bh, pmulld,40h, pmuldq,28h, \ + pshufb,0, psignb,8, psignw,9, psignd,0Ah + + macro v#instr? dest*,src*,src2* + AVX.basic_instruction VEX_66_0F38_W0,opcode,16,dest,src,src2 + end macro + + end iterate + + iterate , mpsadbw,42h, palignr,0Fh + + macro v#instr? dest*,src*,src2*,imm* + AVX.basic_instruction_imm8 VEX_66_0F3A_W0,opcode,16,dest,src,src2,imm + end macro + + end iterate + + iterate , pabsb,1Ch, pabsw,1Dh, pabsd,1Eh, pblendw,0Eh, phminposuw,41h + + macro v#instr? dest*,src* + AVX.single_source_instruction VEX_66_0F38_W0,opcode,16,dest,src + end macro + + end iterate + + iterate , pcmpestrm,60h, pcmpestri,61h, pcmpistrm,62h, pcmpistri,63h + + macro v#instr? dest*,src*,imm* + AVX.single_source_instruction_imm8 VEX_66_0F3A_W0,opcode,16,dest,src,imm + end macro + + end iterate + + iterate , pshufd,VEX_66_0F_W0, pshufhw,VEX_F3_0F_W0, pshuflw,VEX_F2_0F_W0 + + macro v#instr? dest*,src*,imm* + AVX.single_source_instruction_imm8 vex_mpw,70h,16,dest,src,imm + end macro + + end iterate + + macro vpblendvb? dest*,src*,src2*,mask* + AVX.parse_operand @dest,dest + AVX.parse_operand @src,src + AVX.parse_operand @src2,src2 + AVX.parse_operand @aux,mask + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') & @aux.type = 'mmreg' + if @dest.size <> 16 + err 'invalid operand size' + else if @src.size <> @dest.size | @src2.size and not @dest.size | @aux.size <> @dest.size + err 'operand sizes do not match' + end if + AVX.store_instruction 16,VEX_66_0F3A_W0,4Ch,@src2,@dest.rm,@src.rm,1,(@aux.rm and 1111b) shl 4 + else + err 'invalid combination of operands' + end if + end macro + + iterate , vpextrb,14h,1, vpextrd,16h,4 + + macro instr? dest*,src*,aux* + x86.parse_operand @dest,dest + AVX.parse_operand @src,src + x86.parse_operand @aux,aux + if (@dest.type = 'reg' | @dest.type = 'mem') & @src.type = 'mmreg' & @aux.type = 'imm' + if (@dest.type = 'reg' & @dest.size <> 4 & (x86.mode < 64 | @dest.size <> 8)) | (@dest.type = 'mem' & @dest.size and not msize) | @src.size <> 16 | @aux.size and not 1 + err 'invalid operand size' + end if + AVX.store_instruction 16,VEX_66_0F3A_W0,opcode,@dest,@src.rm,,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + macro vpextrw? dest*,src*,aux* + x86.parse_operand @dest,dest + AVX.parse_operand @src,src + x86.parse_operand @aux,aux + if @dest.type = 'reg' & @src.type = 'mmreg' & @aux.type = 'imm' + if @dest.size <> 4 & (x86.mode < 64 | @dest.size <> 8) | @src.size <> 16 | @aux.size and not 1 + err 'invalid operand size' + end if + AVX.store_instruction 16,VEX_66_0F_W0,0C5h,@src,@dest.rm,,1,@aux.imm + else if @dest.type = 'mem' & @src.type = 'mmreg' & @aux.type = 'imm' + if @dest.size and not 2 | @src.size <> 16 | @aux.size and not 1 + err 'invalid operand size' + end if + AVX.store_instruction 16,VEX_66_0F3A_W0,15h,@dest,@src.rm,,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + + macro vpextrq? dest*,src*,aux* + x86.parse_operand @dest,dest + AVX.parse_operand @src,src + x86.parse_operand @aux,aux + if (@dest.type = 'reg' | @dest.type = 'mem') & @src.type = 'mmreg' & @aux.type = 'imm' + if @dest.size and not 8 | @src.size <> 16 | @aux.size and not 1 + err 'invalid operand size' + end if + if x86.mode < 64 + err 'instruction requires long mode' + end if + AVX.store_instruction 16,VEX_66_0F3A_W1,16h,@dest,@src.rm,,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + + iterate , vpinsrb,VEX_66_0F3A_W0,20h,1, vpinsrw,VEX_66_0F_W0,0C4h,2, vpinsrd,VEX_66_0F3A_W0,22h,4 + + macro instr? dest*,src*,src2*,aux* + AVX.parse_operand @dest,dest + AVX.parse_operand @src,src + x86.parse_operand @src2,src2 + x86.parse_operand @aux,aux + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'reg' | @src2.type = 'mem') & @aux.type = 'imm' + if @dest.size <> 16 | @src.size <> 16 | (@src2.type = 'reg' & @src2.size <> 4) | (@src2.type = 'mem' & @src2.size and not msize) | @aux.size and not 1 + err 'invalid operand size' + end if + AVX.store_instruction 16,vex_mpw,opcode,@src2,@dest.rm,@src.rm,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + macro vpinsrq? dest*,src*,src2*,aux* + AVX.parse_operand @dest,dest + AVX.parse_operand @src,src + x86.parse_operand @src2,src2 + x86.parse_operand @aux,aux + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'reg' | @src2.type = 'mem') & @aux.type = 'imm' + if @dest.size <> 16 | @src.size <> 16 | @src2.size and not 8 | @aux.size and not 1 + err 'invalid operand size' + end if + if x86.mode < 64 + err 'instruction requires long mode' + end if + AVX.store_instruction 16,VEX_66_0F3A_W1,22h,@src2,@dest.rm,@src.rm,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + + macro vpmovmskb? dest*,src* + x86.parse_operand @dest,dest + AVX.parse_operand @src,src + if @dest.type = 'reg' & @src.type = 'mmreg' + if (@dest.size <> 4 & (x86.mode < 64 | @dest.size <> 8)) | @src.size <> 16 + err 'invalid operand size' + end if + AVX.store_instruction 16,VEX_66_0F_W0,0D7h,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + iterate , pmovsxbw,20h,8, pmovsxbd,21h,4, pmovsxbq,22h,2, pmovsxwd,23h,8, pmovsxwq,24h,4, pmovsxdq,25h,8, \ + pmovzxbw,30h,8, pmovzxbd,31h,4, pmovzxbq,32h,2, pmovzxwd,33h,8, pmovzxwq,34h,4, pmovzxdq,35h,8 + + macro v#instr? dest*,src* + AVX.single_source_instruction VEX_66_0F38_W0,opcode,msize,dest,src + end macro + + end iterate + + iterate , pslldq,7, psrldq,3 + + macro v#instr dest*,src*,src2* + AVX.parse_operand @dest,dest + AVX.parse_operand @src,src + x86.parse_operand @src2,src2 + if @dest.type = 'mmreg' & @src.type = 'mmreg' & @src2.type = 'imm' + if @dest.size <> 16 | @src2.size and not 1 + err 'invalid operand size' + else if @src.size <> @dest.size + err 'operand sizes do not match' + end if + AVX.store_instruction 16,VEX_66_0F_W0,73h,@src,postbyte,@dest.rm,1,@src2.imm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , psllw,0F1h,71h,6, pslld,0F2h,72h,6, psllq,0F3h,73h,6, psraw,0E1h,71h,4, psrad,0E2h,72h,4, psrlw,0D1h,71h,2, psrld,0D2h,72h,2, psrlq,0D3h,73h,2 + + macro v#instr? dest*,src*,src2* + AVX.parse_operand @dest,dest + AVX.parse_operand @src,src + AVX.parse_operand @src2,src2 + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') + if @dest.size <> 16 | @src2.size and not 16 + err 'invalid operand size' + else if @src.size <> @dest.size + err 'operand sizes do not match' + end if + AVX.store_instruction 16,VEX_66_0F_W0,opcode_rrm,@src2,@dest.rm,@src.rm + else if @dest.type = 'mmreg' & @src.type = 'mmreg' & @src2.type = 'imm' + if @dest.size <> 16 | @src2.size and not 1 + err 'invalid operand size' + else if @src.size <> @dest.size + err 'operand sizes do not match' + end if + AVX.store_instruction 16,VEX_66_0F_W0,opcode,@src,postbyte,@dest.rm,1,@src2.imm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , vtestps,0Eh, vtestpd,0Fh, vptest,17h + + macro instr? dest*,src* + AVX.single_source_instruction VEX_66_0F38_W0,opcode,0,dest,src + end macro + + end iterate + + macro vzeroall? + db 0C5h,11111100b,77h + end macro + + macro vzeroupper? + db 0C5h,11111000b,77h + end macro + + macro xsaveopt? src* + x86.parse_operand @src,src + if @src.type = 'mem' + x86.store_instruction <0Fh,0AEh>,@src,6 + else + err 'invalid operand' + end if + end macro + +end if diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx2.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx2.inc new file mode 100644 index 0000000..cc4a602 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx2.inc @@ -0,0 +1,540 @@ + +if ~ defined AVX2 + + restore AVX2 ; this ensures that symbol cannot be forward-referenced + AVX2 = 1 + + include 'avx.inc' + + calminstruction AVX.parse_vsib_operand namespace, operand + + local size, type, segment_prefix + local displacement, displacement_size, auto_relative + local address, base_registers, index_registers + local mode, mod, rm + local scale, index, base + local visize + + local i, pre, suf, sym + + compute segment_prefix, 0 + + compute size, 0 + compute displacement_size, 0 + + transform operand + + match pre suf, operand + jno no_size_prefix + transform pre, x86 + jno no_size_prefix + match :size, pre + jno no_size_prefix + arrange operand, suf + no_size_prefix: + + match [address], operand + jyes memory_operand + match =ptr? address, operand + jyes memory_operand + + jump invalid_operand + + memory_operand: + compute type, 'mem' + + match segment:address, address + jno segment_prefix_ok + check segment eq 1 elementof segment & 1 metadataof segment relativeto x86.sreg + jno invalid_operand + compute segment, 1 metadataof segment - x86.sreg + check segment >= 4 + jyes segment_prefix_386 + compute segment_prefix, 26h + segment shl 3 + jump segment_prefix_ok + segment_prefix_386: + compute segment_prefix, 64h + segment-4 + segment_prefix_ok: + + match pre suf, address + jno no_address_size_prefix + transform pre, x86 + jno no_address_size_prefix + match :pre, pre + jno no_address_size_prefix + arrange address, suf + check pre = 4 | pre = 8 + jno invalid_address_size + compute mode, pre shl 3 + no_address_size_prefix: + + compute mode, 0 + compute scale, 0 + compute index, 0 + compute base, 0 + compute auto_relative, 0 + + check size + jyes size_override + compute size, sizeof address + size_override: + + compute address, address + compute base_registers, 0 + compute index_registers, 0 + compute i, 1 + extract_registers: + check i > elementsof address + jyes registers_extracted + check i metadataof address relativeto SSE.reg | i metadataof address relativeto AVX.reg + jyes index_term + check i metadataof address relativeto x86.r32 | i metadataof address relativeto x86.r64 + jno next_term + compute base_registers, base_registers + i elementof address * i scaleof address + jump next_term + index_term: + compute index_registers, index_registers + i elementof address * i scaleof address + next_term: + compute i, i+1 + jump extract_registers + registers_extracted: + compute displacement, address - base_registers - index_registers + compute auto_relative, 0 + + check elementsof index_registers = 1 + jno invalid_address + compute scale, 1 scaleof index_registers + compute index, 0 scaleof (1 metadataof index_registers) + check scale > 2 & scale <> 4 & scale <> 8 + jyes invalid_address + check 1 metadataof index_registers relativeto SSE.reg + jyes xmm_index + compute visize, 32 + jump index_ok + xmm_index: + compute visize, 16 + index_ok: + + compute rm, 4 + check elementsof base_registers = 1 & 1 scaleof base_registers = 1 + jyes base_and_index + check elementsof base_registers = 0 + jno invalid_address + compute base, 5 + compute displacement_size, 4 + compute mod, 0 + compute mode, x86.mode + check mode > 16 + jyes export_address + compute mode, 32 + jump export_address + base_and_index: + compute base, 0 scaleof (1 metadataof base_registers) + check mode & mode <> 0 scaleof (1 metadataof (1 metadataof base_registers)) shl 3 + jyes invalid_address + compute mode, 0 scaleof (1 metadataof (1 metadataof base_registers)) shl 3 + + setup_displacement: + check displacement relativeto 0 + jno displacement_32bit + check displacement = 0 & rm and 111b <> 5 & (rm <> 4 | base and 111b <> 5) + jyes displacement_empty + check displacement < 80h & displacement >= -80h + jyes displacement_8bit + check displacement - 1 shl mode >= -80h & displacement < 1 shl mode + jyes displacement_8bit_wrap + displacement_32bit: + compute displacement_size, 4 + compute mod, 2 + jump export_address + displacement_8bit_wrap: + compute displacement, displacement - 1 shl mode + displacement_8bit: + compute displacement_size, 1 + compute mod, 1 + jump export_address + index_only: + compute displacement_size, 4 + compute mod, 0 + jump export_address + displacement_empty: + compute displacement_size, 0 + compute mod, 0 + + export_address: + + arrange sym, namespace.=address + publish sym, address + + arrange sym, namespace.=scale + publish sym, scale + + arrange sym, namespace.=index + publish sym, index + + arrange sym, namespace.=base + publish sym, base + + arrange sym, namespace.=auto_relative + publish sym, auto_relative + + arrange sym, namespace.=displacement + publish sym, displacement + + arrange sym, namespace.=mode + publish sym, mode + + arrange sym, namespace.=mod + publish sym, mod + + arrange sym, namespace.=rm + publish sym, rm + + arrange sym, namespace.=type + publish sym, type + + arrange sym, namespace.=size + publish sym, size + + arrange sym, namespace.=visize + publish sym, visize + + arrange sym, namespace.=displacement_size + publish sym, displacement_size + + arrange sym, namespace.=segment_prefix + publish sym, segment_prefix + exit + + invalid_operand: + asmcmd =err 'invalid operand' + exit + invalid_address: + asmcmd =err 'invalid address' + exit + invalid_address_size: + asmcmd =err 'invalid address size' + exit + + end calminstruction + + iterate , vpgatherdd,90h,4, vpgatherqd,91h,8, vgatherdps,92h,4, vgatherqps,93h,8 + + macro instr? dest*,src*,mask* + AVX.parse_operand @dest,dest + AVX.parse_vsib_operand @src,src + AVX.parse_operand @aux,mask + if @dest.type = 'mmreg' & @src.type = 'mem' & @aux.type = 'mmreg' + if @src.size and not 4 | (@dest.size > 16 & @dest.size * (asize shr 2) > @src.visize) | (@src.visize > 16 & @dest.size * (asize shr 2) < @src.visize) + err 'invalid operand size' + else if @aux.size <> @dest.size + err 'operand sizes do not match' + else if @dest.rm = @aux.rm | @dest.rm = @src.index | @aux.rm = @src.index + err 'disallowed combination of registers' + end if + AVX.store_instruction @src.visize,VEX_66_0F38_W0,opcode,@src,@dest.rm,@aux.rm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , vpgatherdq,90h,4, vpgatherqq,91h,8, vgatherdpd,92h,4, vgatherqpd,93h,8 + + macro instr? dest*,src*,mask* + AVX.parse_operand @dest,dest + AVX.parse_vsib_operand @src,src + AVX.parse_operand @aux,mask + if @dest.type = 'mmreg' & @src.type = 'mem' & @aux.type = 'mmreg' + if @src.size and not 8 | (@dest.size > 16 & @dest.size * (asize shr 2) > @src.visize * 2) | (@src.visize > 16 & @dest.size * (asize shr 2) < @src.visize * 2) + err 'invalid operand size' + else if @aux.size <> @dest.size + err 'operand sizes do not match' + else if @dest.rm = @aux.rm | @dest.rm = @src.index | @aux.rm = @src.index + err 'disallowed combination of registers' + end if + AVX.store_instruction @dest.size,VEX_66_0F38_W1,opcode,@src,@dest.rm,@aux.rm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , packsswb,63h, packuswb,67h, packssdw,6Bh, paddb,0FCh, paddw,0FDh, paddd,0FEh, paddq,0D4h, paddsb,0ECh, paddsw,0EDh, paddusb,0DCh, paddusw,0DDh, \ + pand,0DBh, pandn,0DFh, pavgb,0E0h, pavgw,0E3h, pcmpeqb,74h, pcmpeqw,75h, pcmpeqd,76h, pcmpgtb,64h, pcmpgtw,65h, pcmpgtd,66h, \ + pmaddwd,0F5h, pmaxsw,0EEh, pmaxub,0DEh, pminsw,0EAh, pminub,0DAh, pmulhuw,0E4h, pmulhw,0E5h, pmullw,0D5h, pmuludq,0F4h, \ + por,0EBh, psadbw,0F6h, psubb,0F8h, psubw,0F9h, psubd,0FAh, psubq,0FBh, psubsb,0E8h, psubsw,0E9h, psubusb,0D8h, psubusw,0D9h, \ + punpckhbw,68h, punpckhwd,69h, punpckhdq,6Ah, punpckhqdq,6Dh, punpcklbw,60h, punpcklwd,61h, punpckldq,62h, punpcklqdq,6Ch, pxor,0EFh + + macro v#instr? dest*,src*,src2* + AVX.basic_instruction VEX_66_0F_W0,opcode,0,dest,src,src2 + end macro + + end iterate + + iterate , packusdw,2Bh, pcmpeqq,29h, pcmpgtq,37h, phaddw,1, phaddd,2, phaddsw,3, phsubw,5, phsubd,6, phsubsw,7, pmaddubsw,4, \ + pmaxsb,3Ch, pmaxsd,3Dh, pmaxuw,3Eh, pmaxud,3Fh, pminsb,38h, pminsd,39h, pminuw,3Ah, pminud,3Bh, pmulhrsw,0Bh, pmulld,40h, pmuldq,28h, \ + pshufb,0, psignb,8, psignw,9, psignd,0Ah + + macro v#instr? dest*,src*,src2* + AVX.basic_instruction VEX_66_0F38_W0,opcode,0,dest,src,src2 + end macro + + end iterate + + iterate , mpsadbw,42h, palignr,0Fh + + macro v#instr? dest*,src*,src2*,imm* + AVX.basic_instruction_imm8 VEX_66_0F3A_W0,opcode,0,dest,src,src2,imm + end macro + + end iterate + + iterate , pabsb,1Ch, pabsw,1Dh, pabsd,1Eh, pblendw,0Eh + + macro v#instr? dest*,src* + AVX.single_source_instruction VEX_66_0F38_W0,opcode,0,dest,src + end macro + + end iterate + + iterate , pshufd,VEX_66_0F_W0, pshufhw,VEX_F3_0F_W0, pshuflw,VEX_F2_0F_W0 + + macro v#instr? dest*,src*,imm* + AVX.single_source_instruction_imm8 vex_mpw,70h,0,dest,src,imm + end macro + + end iterate + + iterate , vpsllvd,VEX_66_0F38_W0,47h, vpsllvq,VEX_66_0F38_W1,47h, vpsrlvd,VEX_66_0F38_W0,45h, vpsrlvq,VEX_66_0F38_W1,45h, vpsravd,VEX_66_0F38_W0,46h + + macro instr? dest*,src*,src2* + AVX.basic_instruction vex_mpw,opcode,0,dest,src,src2 + end macro + + end iterate + + macro vpblendvb? dest*,src*,src2*,mask* + AVX.parse_operand @dest,dest + AVX.parse_operand @src,src + AVX.parse_operand @src2,src2 + AVX.parse_operand @aux,mask + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') & @aux.type = 'mmreg' + if @src.size <> @dest.size | @src2.size and not @dest.size | @aux.size <> @dest.size + err 'operand sizes do not match' + end if + AVX.store_instruction @dest.size,VEX_66_0F3A_W0,4Ch,@src2,@dest.rm,@src.rm,1,(@aux.rm and 1111b) shl 4 + else + err 'invalid combination of operands' + end if + end macro + + macro vpmovmskb? dest*,src* + x86.parse_operand @dest,dest + AVX.parse_operand @src,src + if @dest.type = 'reg' & @src.type = 'mmreg' + if (@dest.size <> 4 & (x86.mode < 64 | @dest.size <> 8)) + err 'invalid operand size' + end if + AVX.store_instruction @src.size,VEX_66_0F_W0,0D7h,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + iterate , pmovsxbw,20h,8, pmovsxbd,21h,4, pmovsxbq,22h,2, pmovsxwd,23h,8, pmovsxwq,24h,4, pmovsxdq,25h,8, \ + pmovzxbw,30h,8, pmovzxbd,31h,4, pmovzxbq,32h,2, pmovzxwd,33h,8, pmovzxwq,34h,4, pmovzxdq,35h,8 + + macro v#instr? dest*,src* + AVX.parse_operand @dest,dest + AVX.parse_operand @src,src + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') + if (@src.type = 'mmreg' & @src.size <> 16) | (@src.type = 'mem' & @src.size and not (msize * (@dest.size shr 4))) + err 'invalid operand size' + end if + AVX.store_instruction @dest.size,VEX_66_0F38_W0,opcode,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , pslldq,7, psrldq,3 + + macro v#instr dest*,src*,src2* + AVX.parse_operand @dest,dest + AVX.parse_operand @src,src + x86.parse_operand @src2,src2 + if @dest.type = 'mmreg' & @src.type = 'mmreg' & @src2.type = 'imm' + if @src2.size and not 1 + err 'invalid operand size' + else if @src.size <> @dest.size + err 'operand sizes do not match' + end if + AVX.store_instruction @dest.size,VEX_66_0F_W0,73h,@src,postbyte,@dest.rm,1,@src2.imm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , psllw,0F1h,71h,6, pslld,0F2h,72h,6, psllq,0F3h,73h,6, psraw,0E1h,71h,4, psrad,0E2h,72h,4, psrlw,0D1h,71h,2, psrld,0D2h,72h,2, psrlq,0D3h,73h,2 + + macro v#instr? dest*,src*,src2* + AVX.parse_operand @dest,dest + AVX.parse_operand @src,src + AVX.parse_operand @src2,src2 + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') + if @src2.size and not 16 + err 'invalid operand size' + else if @src.size <> @dest.size + err 'operand sizes do not match' + end if + AVX.store_instruction @dest.size,VEX_66_0F_W0,opcode_rrm,@src2,@dest.rm,@src.rm + else if @dest.type = 'mmreg' & @src.type = 'mmreg' & @src2.type = 'imm' + if @src2.size and not 1 + err 'invalid operand size' + else if @src.size <> @dest.size + err 'operand sizes do not match' + end if + AVX.store_instruction @dest.size,VEX_66_0F_W0,opcode,@src,postbyte,@dest.rm,1,@src2.imm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + macro vmovntdqa? dest*,src* + AVX.parse_operand @dest,dest + x86.parse_operand @src,src + if @dest.type = 'mmreg' & @src.type = 'mem' + if @src.size and not @dest.size + err 'operand sizes do not match' + end if + AVX.store_instruction @dest.size,VEX_66_0F38_W0,2Ah,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + iterate , vpmaskmovd,0, vpmaskmovq,1 + + macro instr? dest*,src*,src2* + AVX.parse_operand @dest,dest + AVX.parse_operand @src,src + AVX.parse_operand @src2,src2 + if @dest.type = 'mmreg' & @src.type = 'mmreg' & @src2.type = 'mem' + if @src.size <> @dest.size | @src2.size and not @dest.size + err 'operand sizes do not match' + end if + AVX.store_instruction @dest.size,VEX_66_0F38_W#w,8Ch,@src2,@dest.rm,@src.rm + else if @dest.type = 'mem' & @src.type = 'mmreg' & @src2.type = 'mmreg' + if @src.size <> @src2.size | @dest.size and not @src.size + err 'operand sizes do not match' + end if + AVX.store_instruction @dest.size,VEX_66_0F38_W#w,8Eh,@dest,@src2.rm,@src.rm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + macro vbroadcasti128? dest*,src* + AVX.parse_operand @dest,dest + AVX.parse_operand @src,src + if @dest.type = 'mmreg' & @src.type = 'mem' + if @dest.size <> 32 | @src.size and not 16 + err 'invalid operand size' + end if + AVX.store_instruction 32,VEX_66_0F38_W0,5Ah,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + macro vextracti128? dest*,src*,aux* + AVX.parse_operand @dest,dest + AVX.parse_operand @src,src + x86.parse_operand @aux,aux + if (@dest.type = 'mmreg' | @dest.type = 'mem') & @src.type = 'mmreg' & @aux.type = 'imm' + if @dest.size and not 16 | @src.size <> 32 | @aux.size and not 1 + err 'invalid operand size' + end if + AVX.store_instruction 32,VEX_66_0F3A_W0,39h,@dest,@src.rm,,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + + macro vinserti128? dest*,src*,src2*,aux* + AVX.parse_operand @dest,dest + AVX.parse_operand @src,src + AVX.parse_operand @src2,src2 + x86.parse_operand @aux,aux + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'mmreg' | @src2.type = 'mem') & @aux.type = 'imm' + if @dest.size <> 32 | @src.size <> 32 | @src2.size and not 16 | @aux.size and not 1 + err 'invalid operand size' + end if + AVX.store_instruction 32,VEX_66_0F3A_W0,38h,@src2,@dest.rm,@src.rm,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + + macro vperm2i128? dest*,src*,src2*,imm* + AVX.basic_instruction_imm8 VEX_66_0F3A_W0,46h,32,dest,src,src2,imm + end macro + + iterate , vbroadcastss,18h,4, vpbroadcastb,78h,1, vpbroadcastw,79h,2, vpbroadcastd,58h,4, vpbroadcastq,59h,8 + + macro instr? dest*,src* + AVX.parse_operand @dest,dest + AVX.parse_operand @src,src + if @dest.type = 'mmreg' & (@src.type = 'mmreg' | @src.type = 'mem') + if (@src.type='mmreg' & @src.size <> 16) | (@src.type = 'mem' & @src.size and not msize) + err 'invalid operand size' + end if + AVX.store_instruction @dest.size,VEX_66_0F38_W0,opcode,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + macro vbroadcastsd? dest*,src* + AVX.parse_operand @dest,dest + AVX.parse_operand @src,src + if @dest.type = 'mmreg' & (@src.type = 'mmreg' | @src.type = 'mem') + if @dest.size <> 32 | (@src.type='mmreg' & @src.size <> 16) | (@src.type = 'mem' & @src.size and not 8) + err 'invalid operand size' + end if + AVX.store_instruction 32,VEX_66_0F38_W0,19h,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + iterate , vpermq,0, vpermpd,1 + + macro instr? dest*,src*,imm* + AVX.single_source_instruction_imm8 VEX_66_0F3A_W1,opcode,32,dest,src,imm + end macro + + end iterate + + iterate , vpermd,36h, vpermps,16h + + macro instr? dest*,src*,src2* + AVX.basic_instruction VEX_66_0F38_W0,opcode,32,dest,src,src2 + end macro + + end iterate + +end if diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512.inc new file mode 100644 index 0000000..1a7e9f5 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512.inc @@ -0,0 +1,10 @@ + +include 'avx512f.inc' +include 'avx512cd.inc' + +include 'avx512vl.inc' +include 'avx512bw.inc' +include 'avx512dq.inc' + +include 'avx512_ifma.inc' +include 'avx512_vbmi.inc' diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512_4vnniw.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512_4vnniw.inc new file mode 100644 index 0000000..ca9618c --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512_4vnniw.inc @@ -0,0 +1,31 @@ + +if ~ defined AVX_512 + + include 'avx512f.inc' + +end if + +iterate , vp4dpwssd,52h, vp4dpwssds,53h + + macro instr? dest*,src*,src2* + AVX_512.parse_operand_k1z @dest,dest + match rsrc+=3, src + AVX_512.parse_operand @src,rsrc + else + AVX_512.parse_operand @src,src + end match + AVX_512.parse_operand @src2,src2 + if @dest.type = 'mmreg' & @src.type = 'mmreg' & @src2.type = 'mem' + if @dest.size <> 64 | @src2.size and not 16 + err 'invalid operand size' + else if @dest.size <> @src.size + err 'operand sizes do not match' + end if + @src2.memsize = 0 + AVX_512.store_instruction @dest.size,VEX_F2_0F38_W0,EVEX_REQUIRED,opcode,@src2,@dest.mask,@dest.rm,@src.rm + else + err 'invalid combination of operands' + end if + end macro + +end iterate \ No newline at end of file diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512_bitalg.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512_bitalg.inc new file mode 100644 index 0000000..b74233b --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512_bitalg.inc @@ -0,0 +1,29 @@ + +if ~ defined AVX_512 + + include 'avx512f.inc' + +end if + +iterate , vpopcntb,VEX_66_0F38_W0,54h, vpopcntw,VEX_66_0F38_W1,54h + + + macro instr? dest*,src* + AVX_512.single_source_instruction vex_mpw,EVEX_REQUIRED+EVEX_VL,opcode,0,dest,src + end macro + +end iterate + +macro vpshufbitqmb? dest*,src*,src2* + AVX_512.parse_operand_k1 @dest,dest + AVX_512.parse_operand @src,src + AVX_512.parse_operand @src2,src2 + if @dest.type = 'maskreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') + if @src2.size and not @src.size + err 'operand sizes do not match' + end if + AVX_512.store_instruction @src.size,VEX_66_0F38_W0,EVEX_REQUIRED+EVEX_VL,8Fh,@src2,@dest.mask,@dest.rm,@src.rm + else + err 'invalid combination of operands' + end if +end macro diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512_ifma.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512_ifma.inc new file mode 100644 index 0000000..365fd01 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512_ifma.inc @@ -0,0 +1,15 @@ + +if ~ defined AVX_512 + + include 'avx512f.inc' + +end if + +iterate , vpmadd52luq,0B4h, vpmadd52huq,0B5h + + macro instr? dest*,src*,src2* + AVX_512.basic_instruction_bcst VEX_66_0F38_W1,EVEX_REQUIRED+EVEX_VL,opcode,8,dest,src,src2 + end macro + +end iterate + diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512_vbmi.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512_vbmi.inc new file mode 100644 index 0000000..661ca11 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512_vbmi.inc @@ -0,0 +1,22 @@ + +if ~ defined AVX_512 + + include 'avx512f.inc' + +end if + +iterate , vpermb,VEX_66_0F38_W0,8Dh, vpermi2b,VEX_66_0F38_W0,75h, vpermt2b,VEX_66_0F38_W0,7Dh + + macro instr? dest*,src*,src2* + AVX_512.basic_instruction vex_mpw,EVEX_REQUIRED+EVEX_VL,opcode,0,dest,src,src2 + end macro + +end iterate + +iterate , vpmultishiftqb,83h + + macro instr? dest*,src*,src2* + AVX_512.basic_instruction_bcst VEX_66_0F38_W1,EVEX_REQUIRED+EVEX_VL,opcode,8,dest,src,src2 + end macro + +end iterate diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512_vbmi2.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512_vbmi2.inc new file mode 100644 index 0000000..a4b00b6 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512_vbmi2.inc @@ -0,0 +1,65 @@ + +if ~ defined AVX_512 + + include 'avx512f.inc' + +end if + +iterate , vpshldw,VEX_66_0F3A_W1,70h, vpshrdw,VEX_66_0F3A_W1,72h + + macro instr? dest*,src*,src2*,aux*& + AVX_512.basic_instruction_imm8 vex_mpw,EVEX_REQUIRED+EVEX_VL,opcode,0,dest,src,src2,aux + end macro + +end iterate + +iterate , vpshldd,4,VEX_66_0F3A_W0,71h, vpshldq,8,VEX_66_0F3A_W1,71h, \ + vpshrdd,4,VEX_66_0F3A_W0,73h, vpshrdq,8,VEX_66_0F3A_W1,73h + + macro instr? dest*,src*,src2*,aux*& + AVX_512.basic_instruction_bcst_imm8 vex_mpw,EVEX_REQUIRED+EVEX_VL,opcode,unit,dest,src,src2,aux + end macro + +end iterate + +iterate , vpshldvw,VEX_66_0F38_W1,70h, vpshrdvw,VEX_66_0F38_W1,72h + + macro instr? dest*,src*,src2* + AVX_512.basic_instruction vex_mpw,EVEX_REQUIRED+EVEX_VL,opcode,0,dest,src,src2 + end macro + +end iterate + +iterate , vpshldvd,4,VEX_66_0F38_W0,71h, vpshldvq,8,VEX_66_0F38_W1,71h, \ + vpshrdvd,4,VEX_66_0F38_W0,73h, vpshrdvq,8,VEX_66_0F38_W1,73h + + macro instr? dest*,src*,src2* + AVX_512.basic_instruction_bcst vex_mpw,EVEX_REQUIRED+EVEX_VL,opcode,unit,dest,src,src2 + end macro + +end iterate + +iterate , vpcompressb,VEX_66_0F38_W0,63h, vpcompressw,VEX_66_0F38_W1,63h + + macro instr? dest*,src* + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand @src,src + if (@dest.type = 'mmreg' | @dest.type = 'mem') & @src.type = 'mmreg' + if @dest.size and not @src.size + err 'operand sizes do not match' + end if + AVX_512.store_instruction @src.size,vex_mpw,EVEX_REQUIRED+EVEX_VL,opcode,@dest,@dest.mask,@src.rm + else + err 'invalid combination of operands' + end if + end macro + +end iterate + +iterate , vpexpandb,VEX_66_0F38_W0,62h, vpexpandw,VEX_66_0F38_W1,62h + + macro instr? dest*,src* + AVX_512.single_source_instruction vex_mpw,EVEX_REQUIRED+EVEX_VL,opcode,0,dest,src + end macro + +end iterate diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512_vnni.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512_vnni.inc new file mode 100644 index 0000000..e6c9d0a --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512_vnni.inc @@ -0,0 +1,14 @@ + +if ~ defined AVX_512 + + include 'avx512f.inc' + +end if + +iterate , vpdpbusd,50h, vpdpbusds,51h, vpdpwssd,52h, vpdpwssds,53h + + macro instr? dest*,src*,src2* + AVX_512.basic_instruction_bcst VEX_66_0F38_W0,EVEX_REQUIRED+EVEX_VL,opcode,4,dest,src,src2 + end macro + +end iterate diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512_vpopcntdq.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512_vpopcntdq.inc new file mode 100644 index 0000000..2fbb80e --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512_vpopcntdq.inc @@ -0,0 +1,16 @@ + +if ~ defined AVX_512 + + include 'avx512f.inc' + +end if + +iterate , vpopcntd,4,VEX_66_0F38_W0,55h, vpopcntq,8,VEX_66_0F38_W1,55h + + + macro instr? dest*,src* + AVX_512.single_source_instruction_bcst vex_mpw,EVEX_REQUIRED+EVEX_VL,opcode,unit,dest,src + end macro + +end iterate + diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512bw.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512bw.inc new file mode 100644 index 0000000..7ea9383 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512bw.inc @@ -0,0 +1,494 @@ + + +if ~ defined AVX_512 + + include 'avx512f.inc' + +end if + +iterate , d,VEX_66_0F_W1,4, q,VEX_0F_W1,8 + + iterate , kand,41h, kandn,42h, knot,44h, kor,45h, kxnor,46h, kxor,47h, kadd,4Ah + + macro instr#t? dest*,src*,src2* + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + AVX_512.parse_operand @src2,src2 + if @dest.type = 'maskreg' & @src.type = 'maskreg' & @src2.type = 'maskreg' + AVX.store_instruction 32,vex_mpw,opcode,@src2,@dest.rm,@src.rm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , knot,44h, kortest,98h, ktest,99h + + macro instr#t? dest*,src* + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + if @dest.type = 'maskreg' & @src.type = 'maskreg' + AVX.store_instruction 16,vex_mpw,opcode,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + macro kmov#t? dest*,src* + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + if @dest.type = 'maskreg' & (@src.type = 'maskreg' | @src.type = 'mem') + if @src.type = 'mem' & @src.size and not msize + err 'invalid operand size' + end if + AVX.store_instruction 16,vex_mpw,90h,@src,@dest.rm + else if @dest.type = 'mem' & @src.type = 'maskreg' + if @dest.size and not msize + err 'invalid operand size' + end if + AVX.store_instruction 16,vex_mpw,91h,@dest,@src.rm + else if @dest.type = 'maskreg' & @src.type = 'reg' + if (msize < 8 & @src.size <> 4) | (msize = 8 & @src.size <> 8) + err 'invalid operand size' + else if msize = 8 & x86.mode < 64 + err 'instruction requires long mode' + end if + AVX.store_instruction 16,vex_mpw,92h,@src,@dest.rm + else if @dest.type = 'reg' & @src.type = 'maskreg' + if (msize < 8 & @dest.size <> 4) | (msize = 8 & @dest.size <> 8) + err 'invalid operand size' + else if msize = 8 & x86.mode < 64 + err 'instruction requires long mode' + end if + AVX.store_instruction 16,vex_mpw,93h,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + +end iterate + +iterate , kshiftrd,VEX_66_0F3A_W0,31h, kshiftrq,VEX_66_0F3A_W1,31h, \ + kshiftld,VEX_66_0F3A_W0,33h, kshiftlq,VEX_66_0F3A_W1,33h + + macro instr? dest*,src*,aux* + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + x86.parse_operand @aux,aux + if @dest.type = 'maskreg' & @src.type = 'maskreg' & @aux.type = 'imm' + if @aux.size and not 1 + err 'invalid operand size' + end if + AVX.store_instruction 16,vex_mpw,opcode,@src,@dest.rm,,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + +end iterate + +iterate , kunpckwd,VEX_0F_W0, kunpckdq,VEX_0F_W1 + + macro instr? dest*,src*,src2* + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + AVX_512.parse_operand @src2,src2 + if @dest.type = 'maskreg' & @src.type = 'maskreg' & @src2.type = 'maskreg' + AVX.store_instruction 32,vex_mpw,4Bh,@src2,@dest.rm,@src.rm + else + err 'invalid combination of operands' + end if + end macro + +end iterate + +iterate , vmovdqu8,VEX_F2_0F_W0,EVEX_REQUIRED+EVEX_VL,6Fh,7Fh, vmovdqu16,VEX_F2_0F_W1,EVEX_REQUIRED+EVEX_VL,6Fh,7Fh + + macro instr? dest*,src* + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand @src,src + if @dest.type = 'mmreg' & (@src.type = 'mmreg' | @src.type = 'mem') + if @src.size and not @dest.size + err 'operand sizes do not match' + end if + AVX_512.store_instruction @dest.size,vex_mpw,evex_f,opcode_rm,@src,@dest.mask,@dest.rm + else if @dest.type = 'mem' & @src.type = 'mmreg' + if @dest.size and not @src.size + err 'operand sizes do not match' + end if + AVX_512.store_instruction @src.size,vex_mpw,evex_f,opcode_mr,@dest,@dest.mask,@src.rm + else + err 'invalid combination of operands' + end if + end macro + +end iterate + +iterate , vpabsb,1Ch, vpabsw,1Dh + + macro instr? dest*,src* + AVX_512.single_source_instruction VEX_66_0F38_W0,EVEX_AS_VEX+EVEX_VL,opcode,0,dest,src + end macro + +end iterate + +iterate , vpacksswb,63h, vpackuswb,67h, vpaddb,0FCh, vpaddw,0FDh, vpaddsb,0ECh, vpaddsw,0EDh, vpaddusb,0DCh, vpaddusw,0DDh, vpavgb,0E0h, vpavgw,0E3h, \ + vpmaddwd,0F5h, vpmaxsw,0EEh, vpmaxub,0DEh, vpminsw,0EAh, vpminub,0DAh, vpmulhuw,0E4h, vpmulhw,0E5h, vpmullw,0D5h, \ + vpsadbw,0F6h, vpsubb,0F8h, vpsubw,0F9h, vpsubsb,0E8h, vpsubsw,0E9h, vpsubusb,0D8h, vpsubusw,0D9h, \ + vpunpckhbw,68h, vpunpckhwd,69h, vpunpcklbw,60h, vpunpcklwd,61h + + macro instr? dest*,src*,src2* + AVX_512.basic_instruction VEX_66_0F_W0,EVEX_AS_VEX+EVEX_VL,opcode,0,dest,src,src2 + end macro + +end iterate + +iterate , vpackssdw,6Bh + + macro instr? dest*,src*,src2* + AVX_512.basic_instruction_bcst VEX_66_0F_W0,EVEX_AS_VEX+EVEX_VL,opcode,4,dest,src,src2 + end macro + +end iterate + +iterate , vpackusdw,2Bh + + macro instr? dest*,src*,src2* + AVX_512.basic_instruction_bcst VEX_66_0F38_W0,EVEX_AS_VEX+EVEX_VL,opcode,4,dest,src,src2 + end macro + +end iterate + +iterate , vpalignr,0Fh + + macro instr? dest*,src*,src2*,imm* + AVX_512.basic_instruction_imm8 VEX_66_0F3A_W0,EVEX_AS_VEX+EVEX_VL,opcode,0,dest,src,src2,imm + end macro + +end iterate + +iterate , vpmaddubsw,4, vpmaxsb,3Ch, vpmaxuw,3Eh, vpminsb,38h, vpminuw,3Ah, vpmulhrsw,0Bh, vpshufb,0 + + macro instr? dest*,src*,src2* + AVX_512.basic_instruction VEX_66_0F38_W0,EVEX_AS_VEX+EVEX_VL,opcode,0,dest,src,src2 + end macro + +end iterate + +iterate , vpcmpeqb,74h, pcmpeqw,75h, vpcmpgtb,64h, vpcmpgtw,65h + + macro instr? dest*,src*,src2* + AVX_512.parse_operand_k1 @dest,dest + AVX_512.parse_operand @src,src + AVX_512.parse_operand @src2,src2 + if @dest.type = 'maskreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') + if @src2.size and not @src.size + err 'operand sizes do not match' + end if + AVX_512.store_instruction @src.size,VEX_66_0F_W0,EVEX_REQUIRED+EVEX_VL,opcode,@src2,@dest.mask,@dest.rm,@src.rm + else if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') + if @src.size <> @dest.size | @src2.size and not @dest.size + err 'operand sizes do not match' + end if + AVX_512.store_instruction @src.size,VEX_66_0F_W0,EVEX_FORBIDDEN,opcode,@src2,@dest.mask,@dest.rm,@src.rm + else + err 'invalid combination of operands' + end if + end macro + +end iterate + +macro vpextrb? dest*,src*,aux* + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + x86.parse_operand @aux,aux + if (@dest.type = 'reg' | @dest.type = 'mem') & @src.type = 'mmreg' & @aux.type = 'imm' + if (@dest.type = 'reg' & @dest.size <> 4 & (x86.mode < 64 | @dest.size <> 8)) | (@dest.type = 'mem' & @dest.size and not 1) | @src.size <> 16 | @aux.size and not 1 + err 'invalid operand size' + end if + @dest.memsize = 1 + AVX_512.store_instruction 16,VEX_66_0F3A_W0,EVEX_AS_VEX,14h,@dest,0,@src.rm,,1,@aux.imm + else + err 'invalid combination of operands' + end if +end macro + +macro vpextrw? dest*,src*,aux* + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + x86.parse_operand @aux,aux + if @dest.type = 'reg' & @src.type = 'mmreg' & @aux.type = 'imm' + if @dest.size <> 4 & (x86.mode < 64 | @dest.size <> 8) | @src.size <> 16 | @aux.size and not 1 + err 'invalid operand size' + end if + AVX_512.store_instruction 16,VEX_66_0F_W0,EVEX_AS_VEX,0C5h,@src,0,@dest.rm,,1,@aux.imm + else if @dest.type = 'mem' & @src.type = 'mmreg' & @aux.type = 'imm' + if @dest.size and not 2 | @src.size <> 16 | @aux.size and not 1 + err 'invalid operand size' + end if + @dest.memsize = 2 + AVX_512.store_instruction 16,VEX_66_0F3A_W0,EVEX_AS_VEX,15h,@dest,0,@src.rm,,1,@aux.imm + else + err 'invalid combination of operands' + end if +end macro + +iterate , vpinsrb,VEX_66_0F3A_W0,20h,1, vpinsrw,VEX_66_0F_W0,0C4h,2 + + macro instr? dest*,src*,src2*,aux* + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + AVX_512.parse_operand @src2,src2 + x86.parse_operand @aux,aux + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'reg' | @src2.type = 'mem') & @aux.type = 'imm' + if @dest.size <> 16 | @src.size <> 16 | (@src2.type = 'reg' & @src2.size <> 4) | (@src2.type = 'mem' & @src2.size and not msize) | @aux.size and not 1 + err 'invalid operand size' + end if + @src2.memsize = msize + AVX_512.store_instruction 16,vex_mpw,EVEX_AS_VEX,opcode,@src2,0,@dest.rm,@src.rm,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + +end iterate + +iterate , vpmovsxbw,20h,8, vpmovzxbw,30h,8 + + macro instr? dest*,src* + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand @src,src + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') + @src.memsize = msize * (@dest.size shr 4) + if (@src.type = 'mmreg' & @src.size <> (@src.memsize-1) and not 15 + 16) | (@src.type = 'mem' & @src.size and not @src.memsize) + err 'invalid operand size' + end if + AVX_512.store_instruction @dest.size,VEX_66_0F38_W0,EVEX_AS_VEX+EVEX_VL,opcode,@src,@dest.mask,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + +end iterate + +iterate , vpshufhw,VEX_F3_0F_W0, vpshuflw,VEX_F2_0F_W0 + + macro instr? dest*,src*,aux* + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand @src,src + x86.parse_operand @aux,aux + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') & @aux.type = 'imm' + if @aux.size and not 1 + err 'invalid operand size' + else if @src.size and not @dest.size + err 'operand sizes do not match' + end if + AVX_512.store_instruction @dest.size,vex_mpw,EVEX_AS_VEX+EVEX_VL,70h,@src,@dest.mask,@dest.rm,,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + +end iterate + +iterate , vpslldq,7, vpsrldq,3 + + macro instr? dest*,src*,aux* + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + x86.parse_operand @aux,aux + if @dest.type = 'mmreg' & (@src.type = 'mmreg' | @src.type = 'mem') & @aux.type = 'imm' + if @aux.size and not 1 + err 'invalid operand size' + else if @src.size <> @dest.size + err 'operand sizes do not match' + end if + if @src.type = 'mem' + AVX_512.store_instruction @dest.size,VEX_66_0F_W0,EVEX_REQUIRED+EVEX_VL,73h,@src,0,postbyte,@dest.rm,1,@aux.imm + else + AVX_512.store_instruction @dest.size,VEX_66_0F_W0,EVEX_AS_VEX+EVEX_VL,73h,@src,0,postbyte,@dest.rm,1,@aux.imm + end if + else + err 'invalid combination of operands' + end if + end macro + +end iterate + +iterate , vpsllw,0F1h,71h,6, vpsraw,0E1h,71h,4, vpsrlw,0D1h,71h,2 + + macro instr? dest*,src*,src2* + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand @src,src + AVX_512.parse_operand @src2,src2 + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') + @src2.memsize = 16 + if @src2.size and not @src2.memsize + err 'invalid operand size' + else if @src.size <> @dest.size + err 'operand sizes do not match' + end if + AVX_512.store_instruction @dest.size,VEX_66_0F_W0,EVEX_AS_VEX+EVEX_VL,opcode_rrm,@src2,@dest.mask,@dest.rm,@src.rm + else if @dest.type = 'mmreg' & (@src.type = 'mmreg' | @src.type = 'mem') & @src2.type = 'imm' + if @src2.size and not 1 + err 'invalid operand size' + else if @src.size <> @dest.size + err 'operand sizes do not match' + end if + if @src.type = 'mem' + AVX_512.store_instruction @dest.size,VEX_66_0F_W0,EVEX_REQUIRED+EVEX_VL,opcode,@src,@dest.mask,postbyte,@dest.rm,1,@src2.imm + else + AVX_512.store_instruction @dest.size,VEX_66_0F_W0,EVEX_AS_VEX+EVEX_VL,opcode,@src,@dest.mask,postbyte,@dest.rm,1,@src2.imm + end if + else + err 'invalid combination of operands' + end if + end macro + +end iterate + +iterate , vdbpsadbw,VEX_66_0F3A_W0,42h + + macro instr? dest*,src*,src2*,aux*& + AVX_512.basic_instruction_imm8 vex_mpw,EVEX_REQUIRED+EVEX_VL,opcode,0,dest,src,src2,aux + end macro + +end iterate + +iterate , vpblendmb,VEX_66_0F38_W0,66h, vpblendmw,VEX_66_0F38_W1,66h + + macro instr? dest*,src*,src2*& + AVX_512.basic_instruction vex_mpw,EVEX_REQUIRED+EVEX_VL,opcode,0,dest,src,src2 + end macro + +end iterate + +iterate , vpbroadcastb,78h,7Ah,1, vpbroadcastw,79h,7Bh,2 + + macro instr? dest*,src* + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand @src,src + if @dest.type = 'mmreg' & (@src.type = 'mmreg' | @src.type = 'mem') + if (@src.type='mmreg' & @src.size <> 16) | (@src.type = 'mem' & @src.size and not msize) + err 'invalid operand size' + end if + @src.memsize = msize + AVX_512.store_instruction @dest.size,VEX_66_0F38_W0,EVEX_AS_VEX+EVEX_VL,opcode,@src,@dest.mask,@dest.rm + else if @dest.type = 'mmreg' & @src.type = 'reg' + if @src.size <> msize & (@src.size <> 4 | msize = 8) + err 'invalid operand size' + end if + @src.memsize = msize + if msize = 8 + AVX_512.store_instruction @dest.size,VEX_66_0F38_W1,EVEX_REQUIRED+EVEX_VL,opcode_g,@src,@dest.mask,@dest.rm + else + AVX_512.store_instruction @dest.size,VEX_66_0F38_W0,EVEX_REQUIRED+EVEX_VL,opcode_g,@src,@dest.mask,@dest.rm + end if + else + err 'invalid combination of operands' + end if + end macro + +end iterate + +iterate , vpcmpb,VEX_66_0F3A_W0,3Fh, vpcmpub,VEX_66_0F3A_W0,3Eh, vpcmpw,VEX_66_0F3A_W1,3Fh, vpcmpuw,VEX_66_0F3A_W1,3Eh + + macro instr? dest*,src*,src2*,aux* + AVX_512.parse_operand_k1 @dest,dest + AVX_512.parse_operand @src,src + AVX_512.parse_operand @src2,src2 + x86.parse_operand @aux,aux + if @dest.type = 'maskreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') & @aux.type = 'imm' + if @src2.size and not @src.size | @aux.size and not 1 + err 'operand sizes do not match' + end if + AVX_512.store_instruction @src.size,vex_mpw,EVEX_REQUIRED+EVEX_VL,opcode,@src2,@dest.mask,@dest.rm,@src.rm,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + +end iterate + +iterate , vpermw,VEX_66_0F38_W1,8Dh, vpermi2w,VEX_66_0F38_W1,75h, vpermt2w,VEX_66_0F38_W1,7Dh + + macro instr? dest*,src*,src2* + AVX_512.basic_instruction vex_mpw,EVEX_REQUIRED+EVEX_VL,opcode,0,dest,src,src2 + end macro + +end iterate + +iterate , vpmovb2m,VEX_F3_0F38_W0,29h, vpmovw2m,VEX_F3_0F38_W1,29h + + macro instr? dest*,src* + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + if @dest.type = 'maskreg' & @src.type = 'mmreg' + AVX_512.store_instruction @src.size,vex_mpw,EVEX_REQUIRED+EVEX_VL,opcode,@src,0,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + +end iterate + +iterate , vpmovm2b,VEX_F3_0F38_W0,28h, vpmovm2w,VEX_F3_0F38_W1,28h + + macro instr? dest*,src* + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + if @dest.type = 'mmreg' & @src.type = 'maskreg' + AVX_512.store_instruction @dest.size,vex_mpw,EVEX_REQUIRED+EVEX_VL,opcode,@src,0,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + +end iterate + +iterate , vpmovuswb,2,10h, vpmovswb,2,20h, vpmovwb,2,30h + + macro instr? dest*,src* + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand @src,src + if (@dest.type = 'mmreg' | @dest.type = 'mem') & @src.type = 'mmreg' + @dest.memsize = @src.size / ratio + if (@dest.type = 'mmreg' & @dest.size <> (@dest.memsize-1) and not 15 + 16) | (@dest.type = 'mem' & @dest.size and not @dest.memsize) + err 'invalid operand size' + end if + AVX_512.store_instruction @src.size,VEX_F3_0F38_W0,EVEX_REQUIRED+EVEX_VL,opcode,@dest,@dest.mask,@src.rm + else + err 'invalid combination of operands' + end if + end macro + +end iterate + +iterate , vpsllvw,12h, vpsrlvw,10h, vpsravw,11h + + macro instr? dest*,src*,src2* + AVX_512.basic_instruction VEX_66_0F38_W1,EVEX_REQUIRED+EVEX_VL,opcode,0,dest,src,src2 + end macro + +end iterate + +iterate , vptestnmb,VEX_F3_0F38_W0,26h, vptestnmw,VEX_F3_0F38_W1,26h, vptestmb,VEX_66_0F38_W0,26h, vptestmw,VEX_66_0F38_W1,26h + + macro instr? dest*,src*,src2* + AVX_512.parse_operand_k1 @dest,dest + AVX_512.parse_operand @src,src + AVX_512.parse_operand @src2,src2 + if @dest.type = 'maskreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') + if @src2.size and not @src.size + err 'operand sizes do not match' + end if + AVX_512.store_instruction @src.size,vex_mpw,EVEX_REQUIRED+EVEX_VL,opcode,@src2,@dest.mask,@dest.rm,@src.rm + else + err 'invalid combination of operands' + end if + end macro + +end iterate diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512cd.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512cd.inc new file mode 100644 index 0000000..1567b1d --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512cd.inc @@ -0,0 +1,29 @@ + +if ~ defined AVX_512 + + include 'avx512f.inc' + +end if + +iterate , vpbroadcastmb2q,VEX_F3_0F38_W1,2Ah, vpbroadcastmw2d,VEX_F3_0F38_W0,3Ah + + macro instr? dest*,src* + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + if @dest.type = 'mmreg' & @src.type = 'maskreg' + AVX_512.store_instruction @dest.size,vex_mpw,EVEX_REQUIRED+EVEX_VL,opcode,@src,0,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + +end iterate + +iterate , vpconflictd,4,VEX_66_0F38_W0,0C4h, vpconflictq,8,VEX_66_0F38_W1,0C4h, vplzcntd,4,VEX_66_0F38_W0,44h, vplzcntq,8,VEX_66_0F38_W1,44h + + macro instr? dest*,src*& + AVX_512.single_source_instruction_bcst vex_mpw,EVEX_REQUIRED+EVEX_VL,opcode,unit,dest,src + end macro + +end iterate + diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512dq.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512dq.inc new file mode 100644 index 0000000..162987d --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512dq.inc @@ -0,0 +1,463 @@ + +if ~ defined AVX_512 + + include 'avx512f.inc' + +end if + +iterate , kandb,41h, kandnb,42h, knotb,44h, korb,45h, kxnorb,46h, kxorb,47h, kaddb,4Ah + + macro instr? dest*,src*,src2* + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + AVX_512.parse_operand @src2,src2 + if @dest.type = 'maskreg' & @src.type = 'maskreg' & @src2.type = 'maskreg' + AVX.store_instruction 32,VEX_66_0F_W0,opcode,@src2,@dest.rm,@src.rm + else + err 'invalid combination of operands' + end if + end macro + +end iterate + +iterate , knotb,44h, kortestb,98h, ktestb,99h + + macro instr? dest*,src* + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + if @dest.type = 'maskreg' & @src.type = 'maskreg' + AVX.store_instruction 16,VEX_66_0F_W0,opcode,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + +end iterate + +macro kmovb? dest*,src* + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + if @dest.type = 'maskreg' & (@src.type = 'maskreg' | @src.type = 'mem') + if @src.type = 'mem' & @src.size and not 1 + err 'invalid operand size' + end if + AVX.store_instruction 16,VEX_66_0F_W0,90h,@src,@dest.rm + else if @dest.type = 'mem' & @src.type = 'maskreg' + if @dest.size and not 1 + err 'invalid operand size' + end if + AVX.store_instruction 16,VEX_66_0F_W0,91h,@dest,@src.rm + else if @dest.type = 'maskreg' & @src.type = 'reg' + if @src.size <> 4 + err 'invalid operand size' + end if + AVX.store_instruction 16,VEX_66_0F_W0,92h,@src,@dest.rm + else if @dest.type = 'reg' & @src.type = 'maskreg' + if @dest.size <> 4 + err 'invalid operand size' + end if + AVX.store_instruction 16,VEX_66_0F_W0,93h,@src,@dest.rm + else + err 'invalid combination of operands' + end if +end macro + +macro kaddw? dest*,src*,src2* + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + AVX_512.parse_operand @src2,src2 + if @dest.type = 'maskreg' & @src.type = 'maskreg' & @src2.type = 'maskreg' + AVX.store_instruction 32,VEX_0F_W0,opcode,@src2,@dest.rm,@src.rm + else + err 'invalid combination of operands' + end if +end macro + +macro ktestw? dest*,src* + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + if @dest.type = 'maskreg' & @src.type = 'maskreg' + AVX.store_instruction 16,VEX_0F_W0,opcode,@src,@dest.rm + else + err 'invalid combination of operands' + end if +end macro + +iterate , kshiftrb,VEX_66_0F3A_W0,30h, kshiftlb,VEX_66_0F3A_W0,32h + + macro instr? dest*,src*,aux* + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + x86.parse_operand @aux,aux + if @dest.type = 'maskreg' & @src.type = 'maskreg' & @aux.type = 'imm' + if @aux.size and not 1 + err 'invalid operand size' + end if + AVX.store_instruction 16,vex_mpw,opcode,@src,@dest.rm,,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + +end iterate + +iterate , and,54h, andn,55h, or,56h, xor,57h + + macro v#instr#pd? dest*,src*,src2*& + AVX_512.basic_instruction_bcst VEX_66_0F_W0,EVEX_W1+EVEX_VL,opcode,8,dest,src,src2 + end macro + + macro v#instr#ps? dest*,src*,src2*& + AVX_512.basic_instruction_bcst VEX_0F_W0,EVEX_AS_VEX+EVEX_VL,opcode,4,dest,src,src2 + end macro + +end iterate + +iterate , vbroadcastf32x2,19h, vbroadcasti32x2,59h + + macro instr? dest*,src* + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand @src,src + if @dest.type = 'mmreg' & (@src.type = 'mmreg' | @src.type = 'mem') + if @dest.size = 16 | (@src.type = 'mmreg' & @src.size <> 16) | (@src.type = 'mem' & @src.size and not 8) + err 'invalid operand size' + end if + @src.memsize = 8 + AVX_512.store_instruction @dest.size,VEX_66_0F38_W0,EVEX_REQUIRED+EVEX_VL,opcode,@src,@dest.mask,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + +end iterate + +iterate , vbroadcastf32x8,VEX_66_0F38_W0,1Bh,32, vbroadcastf64x2,VEX_66_0F38_W1,1Ah,16, \ + vbroadcasti32x8,VEX_66_0F38_W0,5Bh,32, vbroadcasti64x2,VEX_66_0F38_W1,5Ah,16 + + macro instr? dest*,src* + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand @src,src + if @dest.type = 'mmreg' & @src.type = 'mem' + if @dest.size <= msize | @src.size and not msize + err 'invalid operand size' + end if + @src.memsize = msize + AVX_512.store_instruction @dest.size,vex_mpw,EVEX_REQUIRED+EVEX_VL,opcode,@src,@dest.mask,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + +end iterate + +iterate , vcvtpd2qq,VEX_66_0F_W1,7Bh, vcvtpd2uqq,VEX_66_0F_W1,79h, \ + vcvtqq2pd,VEX_F3_0F_W1,0E6h, vcvtuqq2pd,VEX_F3_0F_W1,7Ah + + macro instr? dest*,src*& + AVX_512.single_source_instruction_bcst_er vex_mpw,EVEX_REQUIRED+EVEX_VL,opcode,8,dest,src + end macro + +end iterate + +iterate , vcvttpd2qq,VEX_66_0F_W1,7Ah, vcvttpd2uqq,VEX_66_0F_W1,78h + + macro instr? dest*,src*& + AVX_512.single_source_instruction_bcst_sae vex_mpw,EVEX_REQUIRED+EVEX_VL,opcode,8,dest,src + end macro + +end iterate + +iterate , vcvtps2qq,VEX_66_0F_W0,7Bh, vcvtps2uqq,VEX_66_0F_W0,79h + + macro instr? dest*,src_er*& + AVX_512.parse_operand_k1z @dest,dest + match src=,er, src_er + AVX_512.parse_operand @src,src + AVX_512.parse_er @src,er,32 + else + AVX_512.parse_operand_bcst @src,src_er,4 + end match + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') + if (@src.type = 'mem' & @src.size and not (@dest.size shr 1)) | (@src.type = 'mmreg' & (@dest.size shr 1 - 1) and not 15 + 16 <> @src.size) + err 'invalid operand size' + end if + if @src.memsize = 0 + @src.memsize = @dest.size shr 1 + end if + AVX_512.store_instruction @dest.size,vex_mpw,EVEX_REQUIRED+EVEX_VL,opcode,@src,@dest.mask,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + +end iterate + +iterate , vcvtqq2ps,VEX_0F_W1,5Bh, vcvtuqq2ps,VEX_F2_0F_W1,7Ah + + macro instr? dest*,src_er*& + AVX_512.parse_operand_k1z @dest,dest + match src=,er, src_er + AVX_512.parse_operand @src,src + AVX_512.parse_er @src,er + else + AVX_512.parse_operand_bcst @src,src_er,8 + end match + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') + if @src.size = 0 + if @dest.size = 16 + err 'operand size not specified' + else + @src.size = 64 + end if + end if + if (@src.size shr 1 - 1) and not 15 + 16 <> @dest.size | @src.size > 64 + err 'invalid operand size' + end if + AVX_512.store_instruction @src.size,vex_mpw,EVEX_REQUIRED+EVEX_VL,opcode,@src,@dest.mask,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + +end iterate + +iterate , vcvttps2qq,VEX_66_0F_W0,7Ah, vcvttps2uqq,VEX_66_0F_W0,78h + + macro instr? dest*,src_sae*& + AVX_512.parse_operand_k1z @dest,dest + match src=,sae, src_sae + AVX_512.parse_operand @src,src + AVX_512.parse_sae @src,sae + else + AVX_512.parse_operand_bcst @src,src_sae,4 + end match + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') + if (@src.type = 'mem' & @src.size and not (@dest.size shr 1)) | (@src.type = 'mmreg' & (@dest.size shr 1 - 1) and not 15 + 16 <> @src.size) + err 'invalid operand size' + end if + if @src.memsize = 0 + @src.memsize = @dest.size shr 1 + end if + AVX_512.store_instruction @dest.size,vex_mpw,EVEX_REQUIRED+EVEX_VL,opcode,@src,@dest.mask,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + +end iterate + +iterate , vextractf32x8,VEX_66_0F3A_W0,1Bh,32, vextractf64x2,VEX_66_0F3A_W1,19h,16, \ + vextracti32x8,VEX_66_0F3A_W0,3Bh,32, vextracti64x2,VEX_66_0F3A_W1,39h,16 + + macro instr? dest*,src*,aux* + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand @src,src + x86.parse_operand @aux,aux + if (@dest.type = 'mmreg' | @dest.type = 'mem') & @src.type = 'mmreg' & @aux.type = 'imm' + if @dest.size and not msize | @src.size <= msize | @aux.size and not 1 + err 'invalid operand size' + end if + @dest.memsize = msize + AVX_512.store_instruction @src.size,vex_mpw,EVEX_REQUIRED+EVEX_VL,opcode,@dest,@dest.mask,@src.rm,,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + +end iterate + +iterate , vinsertf32x8,VEX_66_0F3A_W0,1Ah,32, vinsertf64x2,VEX_66_0F3A_W1,18h,16, \ + vinserti32x8,VEX_66_0F3A_W0,3Ah,32, vinserti64x2,VEX_66_0F3A_W1,38h,16 + + macro instr? dest*,src*,src2*,aux* + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand @src,src + AVX_512.parse_operand @src2,src2 + x86.parse_operand @aux,aux + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'mmreg' | @src2.type = 'mem') & @aux.type = 'imm' + if @dest.size <= msize | @src.size <= msize | @src2.size and not msize | @aux.size and not 1 + err 'invalid operand size' + end if + @src2.memsize = msize + AVX_512.store_instruction @dest.size,vex_mpw,EVEX_REQUIRED+EVEX_VL,opcode,@src2,@dest.mask,@dest.rm,@src.rm,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + +end iterate + +iterate , vfpclasspd,8,VEX_66_0F3A_W1, vfpclassps,4,VEX_66_0F3A_W0 + + macro instr? dest*,src*,aux* + AVX_512.parse_operand_k1 @dest,dest + AVX_512.parse_operand_bcst @src,src,unit + x86.parse_operand @aux,aux + if @dest.type = 'maskreg' & (@src.type = 'mem' | @src.type = 'mmreg') & @aux.type = 'imm' + if @src.size = 0 + err 'operand size not specified' + else if (@src.size <> 16 & @src.size <> 32 & @src.size <> 64) | @aux.size and not 1 + err 'invalid operand size' + end if + AVX_512.store_instruction @src.size,vex_mpw,EVEX_REQUIRED+EVEX_VL,66h,@src,@dest.mask,@dest.rm,,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + +end iterate + +iterate , vfpclasssd,8,VEX_66_0F3A_W1, vfpclassss,4,VEX_66_0F3A_W0 + + macro instr? dest*,src*,aux* + AVX_512.parse_operand_k1 @dest,dest + AVX_512.parse_operand @src,src + x86.parse_operand @aux,aux + if @dest.type = 'maskreg' & (@src.type = 'mem' | @src.type = 'mmreg') & @aux.type = 'imm' + if (@src.type = 'mem' & @src.size and not unit) | (@src.type = 'mmreg' & @src.size <> 16) | @aux.size and not 1 + err 'invalid operand size' + end if + @src.memsize = 16 + AVX_512.store_instruction @src.size,vex_mpw,EVEX_REQUIRED,67h,@src,@dest.mask,@dest.rm,,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + +end iterate + +macro vpextrd? dest*,src*,aux* + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + x86.parse_operand @aux,aux + if (@dest.type = 'reg' | @dest.type = 'mem') & @src.type = 'mmreg' & @aux.type = 'imm' + if (@dest.type = 'reg' & @dest.size <> 4 & (x86.mode < 64 | @dest.size <> 8)) | (@dest.type = 'mem' & @dest.size and not 4) | @src.size <> 16 | @aux.size and not 1 + err 'invalid operand size' + end if + @dest.memsize = 4 + AVX_512.store_instruction 16,VEX_66_0F3A_W0,EVEX_AS_VEX,16h,@dest,0,@src.rm,,1,@aux.imm + else + err 'invalid combination of operands' + end if +end macro + +macro vpinsrd? dest*,src*,src2*,aux* + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + AVX_512.parse_operand @src2,src2 + x86.parse_operand @aux,aux + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'reg' | @src2.type = 'mem') & @aux.type = 'imm' + if @dest.size <> 16 | @src.size <> 16 | (@src2.type = 'reg' & @src2.size <> 4) | (@src2.type = 'mem' & @src2.size and not 4) | @aux.size and not 1 + err 'invalid operand size' + end if + @src2.memsize = 4 + AVX_512.store_instruction 16,VEX_66_0F3A_W0,EVEX_AS_VEX,22h,@src2,0,@dest.rm,@src.rm,1,@aux.imm + else + err 'invalid combination of operands' + end if +end macro + +macro vpextrq? dest*,src*,aux* + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + x86.parse_operand @aux,aux + if (@dest.type = 'reg' | @dest.type = 'mem') & @src.type = 'mmreg' & @aux.type = 'imm' + if @dest.size and not 8 | @src.size <> 16 | @aux.size and not 1 + err 'invalid operand size' + end if + if x86.mode < 64 + err 'instruction requires long mode' + end if + @dest.memsize = 8 + AVX_512.store_instruction 16,VEX_66_0F3A_W1,EVEX_AS_VEX,16h,@dest,0,@src.rm,,1,@aux.imm + else + err 'invalid combination of operands' + end if +end macro + +macro vpinsrq? dest*,src*,src2*,aux* + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + x86.parse_operand @src2,src2 + x86.parse_operand @aux,aux + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'reg' | @src2.type = 'mem') & @aux.type = 'imm' + if @dest.size <> 16 | @src.size <> 16 | @src2.size and not 8 | @aux.size and not 1 + err 'invalid operand size' + end if + if x86.mode < 64 + err 'instruction requires long mode' + end if + @src2.memsize = 8 + AVX_512.store_instruction 16,VEX_66_0F3A_W1,EVEX_AS_VEX,22h,@src2,0,@dest.rm,@src.rm,1,@aux.imm + else + err 'invalid combination of operands' + end if +end macro + +macro vpmullq? dest*,src*,src2* + AVX_512.basic_instruction_bcst VEX_66_0F38_W1,EVEX_REQUIRED+EVEX_VL,40h,8,dest,src,src2 +end macro + +iterate , vpmovm2d,VEX_F3_0F38_W0,38h, vpmovm2q,VEX_F3_0F38_W1,38h + + macro instr? dest*,src* + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + if @dest.type = 'mmreg' & @src.type = 'maskreg' + AVX_512.store_instruction @dest.size,vex_mpw,EVEX_REQUIRED+EVEX_VL,opcode,@src,0,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + +end iterate + +iterate , vpmovd2m,VEX_F3_0F38_W0,39h, vpmovq2m,VEX_F3_0F38_W1,39h + + macro instr? dest*,src* + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + if @dest.type = 'maskreg' & @src.type = 'mmreg' + AVX_512.store_instruction @src.size,vex_mpw,EVEX_REQUIRED+EVEX_VL,opcode,@src,0,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + +end iterate + +iterate , range,50h + + macro v#instr#pd? dest*,src*,src2*,aux*& + AVX_512.basic_instruction_bcst_sae_imm8 VEX_66_0F3A_W1,EVEX_REQUIRED+EVEX_VL,opcode,8,dest,src,src2,aux + end macro + + macro v#instr#ps? dest*,src*,src2*,aux*& + AVX_512.basic_instruction_bcst_sae_imm8 VEX_66_0F3A_W0,EVEX_REQUIRED+EVEX_VL,opcode,4,dest,src,src2,aux + end macro + + macro v#instr#sd? dest*,src*,src2*,aux*& + AVX_512.basic_instruction_sae_imm8 VEX_66_0F3A_W1,EVEX_REQUIRED,opcode+1,8,dest,src,src2,aux + end macro + + macro v#instr#ss? dest*,src*,src2*,aux*& + AVX_512.basic_instruction_sae_imm8 VEX_66_0F3A_W0,EVEX_REQUIRED,opcode+1,4,dest,src,src2,aux + end macro + +end iterate + +macro vreducepd? dest*,src*,aux*& + AVX_512.single_source_instruction_bcst_sae_imm8 VEX_66_0F3A_W1,EVEX_REQUIRED+EVEX_VL,56h,8,dest,src,aux +end macro + +macro vreduceps? dest*,src*,aux*& + AVX_512.single_source_instruction_bcst_sae_imm8 VEX_66_0F3A_W0,EVEX_REQUIRED+EVEX_VL,56h,4,dest,src,aux +end macro + +macro vreducesd? dest*,src*,src2*,aux*& + AVX_512.basic_instruction_sae_imm8 VEX_66_0F3A_W1,EVEX_REQUIRED,57h,8,dest,src,src2,aux +end macro + +macro vreducess? dest*,src*,src2*,aux*& + AVX_512.basic_instruction_sae_imm8 VEX_66_0F3A_W0,EVEX_REQUIRED,57h,4,dest,src,src2,aux +end macro diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512er.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512er.inc new file mode 100644 index 0000000..27ec5d5 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512er.inc @@ -0,0 +1,39 @@ + +if ~ defined AVX_512 + + include 'avx512f.inc' + +end if + +iterate , vexp2ps,4,VEX_66_0F38_W0,0C8h, vexp2pd,4,VEX_66_0F38_W1,0C8h, \ + vrcp28ps,4,VEX_66_0F38_W0,0CAh, vrcp28pd,8,VEX_66_0F38_W1,0CAh, vrsqrt28ps,4,VEX_66_0F38_W0,0CCh, vrsqrt28pd,8,VEX_66_0F38_W1,0CCh + + macro instr? dest*,src_sae*& + AVX_512.parse_operand_k1z @dest,dest + match src=,sae, src_sae + AVX_512.parse_operand @src,src + AVX_512.parse_sae @src,sae + else + AVX_512.parse_operand_bcst @src,src_sae,unit + end match + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') + if @dest.size <> 64 + err 'invalid operand size' + else if @src.size and not @dest.size + err 'operand sizes do not match' + end if + AVX_512.store_instruction @dest.size,vex_mpw,EVEX_REQUIRED,opcode,@src,@dest.mask,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + +end iterate + +iterate , vrcp28ss,4,VEX_66_0F38_W0,0CBh, vrcp28sd,8,VEX_66_0F38_W1,0CBh, vrsqrt28ss,4,VEX_66_0F38_W0,0CDh, vrsqrt28sd,8,VEX_66_0F38_W1,0CDh + + macro instr? dest*,src*,src2*& + AVX_512.basic_instruction vex_mpw,EVEX_REQUIRED,opcode,unit,dest,src,src2 + end macro + +end iterate diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512f.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512f.inc new file mode 100644 index 0000000..dc79ffa --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512f.inc @@ -0,0 +1,2800 @@ + +if ~ defined AVX_512 + + restore AVX_512 ; this ensures that symbol cannot be forward-referenced + AVX_512 = 1 + + include 'avx2.inc' + + element AVX_512.reg + + element AVX_512.r128 : AVX_512.reg + 16 + element AVX_512.r256 : AVX_512.reg + 32 + element AVX_512.r512 : AVX_512.reg + 64 + + repeat 8, i:0 + element zmm#i? : AVX_512.r512 + i + end repeat + + if defined xmm8 + repeat 16, i:16 + element xmm#i? : AVX_512.r128 + i + element ymm#i? : AVX_512.r256 + i + end repeat + repeat 8+16, i:8 + element zmm#i? : AVX_512.r512 + i + end repeat + end if + + element AVX_512.maskreg + + repeat 8, i:0 + element k#i? : AVX_512.maskreg + i + end repeat + + define x86.dqqword? :64 + define x86.zword? :64 + + EVEX_AS_VEX = 0 + EVEX_W1 = 1 shl 15 + EVEX_REQUIRED = 1 shl 10 + EVEX_FORBIDDEN = 1 shl 2 + EVEX_VL = 1 shl 22 + + AVX512VL = 0 + + calminstruction AVX_512.parse_operand namespace, operand + + local size, type, mod, rm, imm + local i, sym, cmd + + asmcmd =x86.=parse_operand namespace, operand + + arrange type, namespace.=type + arrange size, namespace.=size + arrange imm, namespace.=imm + + check type = 'reg' & size = 1 & rm >= 4 & (~ defined x86.REX_FORBIDDEN | rm and x86.REX_FORBIDDEN) + jyes invalid_operand + check type = 'imm' & size = 0 + jno export_common + + check imm eq 1 elementof imm & 1 metadataof imm relativeto SSE.reg + jyes xmm_register + check imm eq 1 elementof imm & 1 metadataof imm relativeto AVX.reg + jyes ymm_register + check 1 metadataof (1 metadataof imm) relativeto AVX_512.reg & imm eq 1 elementof imm + jyes xyzmm_register + check imm eq 1 elementof imm & 1 metadataof imm relativeto AVX_512.maskreg + jyes mask_register + exit + + invalid_operand: + asmcmd =err 'invalid operand' + + xmm_register: + + compute rm, 1 metadataof imm - SSE.reg + compute size, 16 + + jump export_mmreg + + ymm_register: + + compute rm, 1 metadataof imm - AVX.reg + compute size, 32 + + jump export_mmreg + + mask_register: + + compute rm, 1 metadataof imm - AVX_512.maskreg + compute size, 8 + compute type, 'maskreg' + + jump export_reg + + xyzmm_register: + + compute rm, 1 metadataof imm - 1 elementof (1 metadataof imm) + compute size, 1 metadataof (1 metadataof imm) - AVX_512.reg + + export_mmreg: + + compute type, 'mmreg' + + export_reg: + + compute mod, 11b + + arrange sym, namespace.=mod + publish sym, mod + + arrange sym, namespace.=rm + publish sym, rm + + arrange sym, namespace.=type + publish sym, type + + arrange sym, namespace.=size + publish sym, size + + export_common: + + compute i, 0 + + arrange sym, namespace.=mask + publish sym, i + + arrange sym, namespace.=evex_b + publish sym, i + + arrange sym, namespace.=memsize + publish sym, i + + end calminstruction + + calminstruction AVX_512.parse_operand_k1z namespace,operand + + local k1, z, mask, sym + transform operand + match operand {k1} { =z? }, operand + jyes k1z + match operand {k1}, operand + jyes k1 + asmcmd =AVX_512.=parse_operand namespace, operand + exit + k1z: + compute z, 80h + jump masked + k1: + compute z, 0 + masked: + asmcmd =AVX_512.=parse_operand namespace, operand + arrange sym, namespace.=type + check z & sym = 'mem' + jyes invalid_mask + check k1 eq 1 elementof k1 & 1 metadataof k1 relativeto AVX_512.maskreg & 1 metadataof k1 - AVX_512.maskreg > 0 + jno invalid_mask + compute mask, (1 metadataof k1 - AVX_512.maskreg) or z + arrange sym, namespace.=mask + publish sym, mask + exit + invalid_mask: + asmcmd =err 'invalid mask' + + end calminstruction + + calminstruction AVX_512.parse_operand_k1 namespace,operand + + local k1, mask, sym + transform operand + match operand {k1}, operand + jyes k1 + asmcmd =AVX_512.=parse_operand namespace, operand + exit + k1: + asmcmd =AVX_512.=parse_operand namespace, operand + arrange sym, namespace.=type + check k1 eq 1 elementof k1 & 1 metadataof k1 relativeto AVX_512.maskreg & 1 metadataof k1 - AVX_512.maskreg > 0 + jno invalid_mask + compute mask, 1 metadataof k1 - AVX_512.maskreg + arrange sym, namespace.=mask + publish sym, mask + exit + invalid_mask: + asmcmd =err 'invalid mask' + + end calminstruction + + calminstruction AVX_512.parse_operand_bcst namespace,operand,unit + + local broadcast, memsize, size, b + local sym + + transform operand + match operand {broadcast}, operand + jyes mem_bcst + asmcmd =AVX_512.=parse_operand namespace, operand + exit + + invalid_operand: + asmcmd =err 'invalid operand' + + mem_bcst: + asmcmd =AVX_512.=parse_operand namespace, operand + arrange sym, namespace.=type + check sym = 'mem' + jno invalid_operand + + arrange size, namespace.=size + compute memsize, unit + check memsize + jyes implied_unit + check size + jno operand_size_not_specified + compute memsize, size + jump unit_ok + operand_size_not_specified: + asmcmd =err 'operand size not specified' + exit + implied_unit: + check size and not memsize + jno unit_ok + asmcmd =err 'invalid operand size' + exit + unit_ok: + + match =1to2?, broadcast + jyes bcst_2 + match =1to4?, broadcast + jyes bcst_4 + match =1to8?, broadcast + jyes bcst_8 + match =1to16?, broadcast + jyes bcst_16 + asmcmd =err 'invalid broadcast' + exit + bcst_2: + compute broadcast, 2 + jump bcst_ok + bcst_4: + compute broadcast, 4 + jump bcst_ok + bcst_8: + compute broadcast, 8 + jump bcst_ok + bcst_16: + compute broadcast, 16 + bcst_ok: + compute size, memsize * broadcast + compute b, 1 + + arrange sym, namespace.=memsize + publish sym, memsize + + arrange sym, namespace.=size + publish sym, size + + arrange sym, namespace.=broadcast + publish sym, broadcast + + arrange sym, namespace.=evex_b + publish sym, b + + end calminstruction + + calminstruction AVX_512.parse_er namespace,operand,vsize:64 + + local type, size, rounding, b + local sym + + arrange type, namespace.=type + arrange size, namespace.=size + check type = 'mem' | size <> vsize + jyes invalid_operand + + match { =rn?-=sae? }, operand + jyes rounding_0 + match { =rd?-=sae? }, operand + jyes rounding_1 + match { =ru?-=sae? }, operand + jyes rounding_2 + match { =rz?-=sae? }, operand + jyes rounding_3 + invalid_operand: + asmcmd =err 'invalid operand' + exit + rounding_0: + compute rounding, 0 + jump rounding_ok + rounding_1: + compute rounding, 1 + jump rounding_ok + rounding_2: + compute rounding, 2 + jump rounding_ok + rounding_3: + compute rounding, 3 + jump rounding_ok + + rounding_ok: + compute b, 1 + + arrange sym, namespace.=rounding + publish sym, rounding + + arrange sym, namespace.=evex_b + publish sym, b + + end calminstruction + + calminstruction AVX_512.parse_sae namespace,operand + + local type, rounding, b + local sym + + arrange type, namespace.=type + check type = 'mem' + jyes invalid_operand + + match { =sae? }, operand + jno invalid_operand + + compute b, 1 + compute rounding, -1 + + arrange sym, namespace.=rounding + publish sym, rounding + + arrange sym, namespace.=evex_b + publish sym, b + + exit + + invalid_operand: + asmcmd =err 'invalid operand' + + end calminstruction + + calminstruction AVX_512.store_instruction vsize*,vex_mpw*,evex_f*,opcode*,rm_operand*,mask*,reg*,vreg:0,imm_size:0,imm + + local evex, evex_flags + local segment_prefix, evex_b, rounding, memsize + local mode, mod, rm + local scale, index, base + local displacement, displacement_size, auto_relative + local evex_displacement_size, compressed_displacement + local sym + + arrange segment_prefix, rm_operand.=segment_prefix + arrange evex_b, rm_operand.=evex_b + arrange rounding, rm_operand.=rounding + arrange memsize, rm_operand.=memsize + + arrange mode, rm_operand.=mode + arrange mod, rm_operand.=mod + arrange rm, rm_operand.=rm + + arrange scale, rm_operand.=scale + arrange index, rm_operand.=index + arrange base, rm_operand.=base + + arrange displacement_size, rm_operand.=displacement_size + arrange displacement, rm_operand.=displacement + arrange auto_relative, rm_operand.=auto_relative + + check segment_prefix + jno segment_prefix_ok + + check mode = 64 + jyes segment_in_long_mode + check mode = 16 & ( rm = 2 | rm = 3 | ( mod > 0 & rm = 6 ) ) + jyes ss_segment_default + check mode = 32 & ( ( mod > 0 & rm = 5 ) | ( rm = 4 & base = 4 ) | ( mod > 0 & rm = 4 & base = 5 ) ) + jyes ss_segment_default + + ds_segment_default: + check segment_prefix = 3Eh + jyes segment_prefix_ok + jump store_segment_prefix + ss_segment_default: + check segment_prefix = 36h + jyes segment_prefix_ok + jump store_segment_prefix + segment_in_long_mode: + check segment_prefix < 64h + jyes segment_prefix_ok + store_segment_prefix: + asmcmd =db segment_prefix + segment_prefix_ok: + + check mod <> 11b & mode <> x86.mode + jno addressing_prefix_ok + check mode = 64 | (mode = 16 & x86.mode = 64) + jno store_addressing_prefix + asmcmd =err 'illegal addressing mode' + store_addressing_prefix: + asmcmd =db 67h + addressing_prefix_ok: + + compute evex, vex_mpw + compute evex_flags, evex_f + + check evex_b + jno evex_L'L + compute evex, evex or evex_b shl 20 + evex_L'L: + check mod = 11b & evex_b & rounding >= 0 + jyes evex_rounding + check vsize = 64 + jyes evex_L' + check evex_flags and EVEX_VL & AVX512VL = 0 + jno evex_L + compute evex_flags, evex_flags or EVEX_FORBIDDEN + evex_L: + check vsize = 32 + jno evex_mask + compute evex, evex or 1 shl 21 + jump evex_mask + evex_L': + compute evex, evex or 1 shl 22 + jump evex_mask + evex_rounding: + compute evex, evex or rounding shl 21 + + evex_mask: + check mask + jno evex_X + compute evex, evex or mask shl 16 + + evex_X: + check rm and 10000b | (mod <> 11b & mode > 16 & rm = 4 & index and 1000b) + jno evex_B + compute evex, evex or 1 shl 6 + evex_B: + check rm and 1000b | (mod <> 11b & mode > 16 & rm = 4 & base and 1000b) + jno evex_R' + compute evex, evex or 1 shl 5 + evex_R': + check reg and 10000b + jno evex_R + compute evex, evex or 1 shl 4 + evex_R: + check reg and 1000b + jno evex_V' + compute evex, evex or 1 shl 7 + evex_V': + check vreg and 10000b + jno evex_vvvv + compute evex, evex or 1 shl 19 + evex_vvvv: + compute evex, evex or (vreg and 1111b) shl 11 + + check x86.mode < 64 & evex and 00001000_01000000_11110000b + jno allowed + asmcmd =err 'instruction requires long mode' + allowed: + + check displacement_size + jno no_displacement_compression + compute displacement, displacement + check memsize + jyes displacement_compression + compute memsize, vsize + displacement_compression: + check displacement relativeto 0 & displacement mod? memsize = 0 + jno displacement_incompressible + compute compressed_displacement, displacement / memsize + check compressed_displacement < 80h & compressed_displacement >= -80h + jyes displacement_compressed + check compressed_displacement - 1 shl mode >= -80h & compressed_displacement < 1 shl mode + jno displacement_incompressible + compute compressed_displacement, compressed_displacement - 1 shl mode + displacement_compressed: + compute evex_displacement_size, 1 + jump choose_prefix + displacement_incompressible: + compute evex_displacement_size, 4 + check mode > 16 + jyes choose_prefix + compute evex_displacement_size, 2 + jump choose_prefix + no_displacement_compression: + compute evex_displacement_size, displacement_size + + choose_prefix: + check evex_flags and EVEX_REQUIRED | evex and 11011111_00000000_00010000b | rm and 10000b + jyes evex_required + check ~ evex_flags and EVEX_FORBIDDEN & evex_displacement_size + 1 < displacement_size + jyes evex + jump vex + evex_required: + check evex_flags and EVEX_FORBIDDEN + jno evex + asmcmd =err 'invalid operand' + + vex: + local byte2, byte3 + compute vex, evex and 11111011_11111111b or (evex and 1 shl 21) shr (21-10) + check vex and 10000000_01111111b <> 1 + jyes vex_3byte + vex_2byte: + compute byte2, ((vex and 10000000b) or ((vex shr 8) and 1111111b)) xor 11111000b + asmcmd =db 0C5h,byte2 + jump evex_done + vex_3byte: + compute byte2, (vex and 11111111b) xor 11100000b + compute byte3, (vex shr 8) xor 01111000b + asmcmd =db 0C4h,byte2,byte3 + jump evex_done + + evex: + compute evex, evex or 1 shl 10 + check evex_flags and EVEX_W1 + jno evex_4byte + compute evex, evex or 1 shl 15 + evex_4byte: + compute evex, 62h + (evex xor 00001000_01111000_11110000b) shl 8 + asmcmd =dd evex + check mod <> 11b & mod <> 0 & evex_displacement_size > 0 + jno evex_done + compute displacement_size, evex_displacement_size + check evex_displacement_size = 1 + jyes evex_compressed_displacement + compute mod, 2 + jump evex_done + evex_compressed_displacement: + arrange sym, rm_operand.=displacement + publish sym, compressed_displacement + compute mod, 1 + evex_done: + + local modrm, sib + + compute modrm, mod shl 6 + (reg and 111b) shl 3 + rm and 111b + asmcmd =db opcode, modrm + + check mod <> 11b & rm = 4 & mode <> 16 + jno sib_ok + compute sib, (bsf scale) shl 6 + (index and 111b) shl 3 + base and 111b + asmcmd =db sib + sib_ok: + + check displacement_size = 1 + jyes displacement_8bit + check displacement_size = 2 + jyes displacement_16bit + check displacement_size = 4 | displacement_size = 8 + jno displacement_ok + + check auto_relative + jno auto_relative_ok + check imm_size < 8 + jyes adjust_auto_relative_displacement + compute displacement, displacement - ($ + 4 + 4) + jump auto_relative_ok + adjust_auto_relative_displacement: + compute displacement, displacement - ($ + 4 + imm_size) + auto_relative_ok: + + check mode = 64 & displacement relativeto 0 + jno displacement_ready + check displacement - 1 shl 64 >= -80000000h & displacement < 1 shl 64 + jyes adjust_displacement_wrap + check displacement >= -80000000h & displacement < 80000000h + jyes displacement_ready + asmcmd =err 'address value out of signed range' + adjust_displacement_wrap: + compute displacement, displacement - 1 shl 64 + displacement_ready: + + arrange sym, rm_operand.=displacement + publish sym, displacement + + asmcmd =dd rm_operand.=displacement + + jump displacement_ok + displacement_16bit: + asmcmd =dw rm_operand.=displacement + jump displacement_ok + displacement_8bit: + asmcmd =db rm_operand.=displacement + displacement_ok: + + check imm_size = 1 + jyes immediate_8bit + check imm_size = 2 + jyes immediate_16bit + check imm_size = 4 + jyes immediate_32bit + check imm_size = 8 + jno immediate_ok + asmcmd =x86.=simm32 imm + jump immediate_ok + immediate_32bit: + asmcmd =dd imm + jump immediate_ok + immediate_16bit: + asmcmd =dw imm + jump immediate_ok + immediate_8bit: + asmcmd =db imm + immediate_ok: + + end calminstruction + + macro AVX_512.basic_instruction_bcst_er vex_mpw,evex_f,opcode,unit,dest,src,src_er& + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand @src,src + match src2=,er, src_er + AVX_512.parse_operand @src2,src2 + AVX_512.parse_er @src2,er + else + AVX_512.parse_operand_bcst @src2,src_er,unit + end match + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') + if @src.size <> @dest.size | @src2.size and not @dest.size + err 'operand sizes do not match' + end if + AVX_512.store_instruction @dest.size,vex_mpw,evex_f,opcode,@src2,@dest.mask,@dest.rm,@src.rm + else + err 'invalid combination of operands' + end if + end macro + + macro AVX_512.basic_instruction_bcst_sae vex_mpw,evex_f,opcode,unit,dest,src,src_sae& + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand @src,src + match src2=,sae, src_sae + AVX_512.parse_operand @src2,src2 + AVX_512.parse_sae @src2,sae + else + AVX_512.parse_operand_bcst @src2,src_sae,unit + end match + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') + if @src.size <> @dest.size | @src2.size and not @dest.size + err 'operand sizes do not match' + end if + AVX_512.store_instruction @dest.size,vex_mpw,evex_f,opcode,@src2,@dest.mask,@dest.rm,@src.rm + else + err 'invalid combination of operands' + end if + end macro + + macro AVX_512.basic_instruction_bcst_sae_imm8 vex_mpw,evex_f,opcode,unit,dest,src,src2,aux& + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand @src,src + AVX_512.parse_operand_bcst @src2,src2,unit + match sae=,imm, aux + AVX_512.parse_sae @src2,sae + x86.parse_operand @aux,imm + else + x86.parse_operand @aux,aux + end match + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') & @aux.type = 'imm' + if @src.size <> @dest.size | @src2.size and not @dest.size | @aux.size and not 1 + err 'operand sizes do not match' + end if + AVX_512.store_instruction @dest.size,vex_mpw,evex_f,opcode,@src2,@dest.mask,@dest.rm,@src.rm,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + + macro AVX_512.basic_instruction_bcst vex_mpw,evex_f,opcode,unit,dest,src,src2 + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand @src,src + AVX_512.parse_operand_bcst @src2,src2,unit + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') + if @src.size <> @dest.size | @src2.size and not @dest.size + err 'operand sizes do not match' + end if + AVX_512.store_instruction @dest.size,vex_mpw,evex_f,opcode,@src2,@dest.mask,@dest.rm,@src.rm + else + err 'invalid combination of operands' + end if + end macro + + macro AVX_512.basic_instruction_bcst_imm8 vex_mpw,evex_f,opcode,unit,dest,src,src2,aux + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand @src,src + AVX_512.parse_operand_bcst @src2,src2,unit + x86.parse_operand @aux,aux + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') & @aux.type = 'imm' + if @aux.size and not 1 + err 'invalid operand size' + else if @src.size <> @dest.size | @src2.size and not @dest.size + err 'operand sizes do not match' + end if + AVX_512.store_instruction @dest.size,vex_mpw,evex_f,opcode,@src2,@dest.mask,@dest.rm,@src.rm,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + + macro AVX_512.basic_instruction_er vex_mpw,evex_f,opcode,unit,dest,src,src_er& + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand @src,src + match src2=,er, src_er + AVX_512.parse_operand @src2,src2 + AVX_512.parse_er @src2,er,(unit-1) and not 15 + 16 + else + AVX_512.parse_operand @src2,src_er + end match + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') + if unit & ( @dest.size <> (unit-1) and not 15 + 16 | (@src2.type = 'mem' & @src2.size and not unit) ) + err 'invalid operand size' + else if @dest.size <> @src.size | (@src2.size and not @dest.size & (unit = 0 | @src2.type = 'mmreg')) + err 'operand sizes do not match' + end if + @src2.memsize = unit + AVX_512.store_instruction @dest.size,vex_mpw,evex_f,opcode,@src2,@dest.mask,@dest.rm,@src.rm + else + err 'invalid combination of operands' + end if + end macro + + macro AVX_512.basic_instruction_sae vex_mpw,evex_f,opcode,unit,dest,src,src_sae& + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand @src,src + match src2=,sae, src_sae + AVX_512.parse_operand @src2,src2 + AVX_512.parse_sae @src2,sae + else + AVX_512.parse_operand @src2,src_sae + end match + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') + if unit & ( @dest.size <> (unit-1) and not 15 + 16 | (@src2.type = 'mem' & @src2.size and not unit) ) + err 'invalid operand size' + else if @dest.size <> @src.size | (@src2.size and not @dest.size & (unit = 0 | @src2.type = 'mmreg')) + err 'operand sizes do not match' + end if + @src2.memsize = unit + AVX_512.store_instruction @dest.size,vex_mpw,evex_f,opcode,@src2,@dest.mask,@dest.rm,@src.rm + else + err 'invalid combination of operands' + end if + end macro + + macro AVX_512.basic_instruction_sae_imm8 vex_mpw,evex_f,opcode,unit,dest,src,src2,aux& + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand @src,src + AVX_512.parse_operand @src2,src2 + match sae=,imm, aux + AVX_512.parse_sae @src2,sae + x86.parse_operand @aux,imm + else + x86.parse_operand @aux,aux + end match + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') & @aux.type = 'imm' + if ( unit & ( @dest.size <> (unit-1) and not 15 + 16 | (@src2.type = 'mem' & @src2.size and not unit) ) ) | @aux.size and not 1 + err 'invalid operand size' + else if @dest.size <> @src.size | (@src2.size and not @dest.size & (unit = 0 | @src2.type = 'mmreg')) + err 'operand sizes do not match' + end if + @src2.memsize = unit + AVX_512.store_instruction @dest.size,vex_mpw,evex_f,opcode,@src2,@dest.mask,@dest.rm,@src.rm,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + + macro AVX_512.basic_instruction vex_mpw,evex_f,opcode,unit,dest,src,src2 + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand @src,src + AVX_512.parse_operand @src2,src2 + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') + if unit & ( @dest.size <> (unit-1) and not 15 + 16 | (@src2.type = 'mem' & @src2.size and not unit) ) + err 'invalid operand size' + else if @dest.size <> @src.size | (@src2.size and not @dest.size & (unit = 0 | @src2.type = 'mmreg')) + err 'operand sizes do not match' + end if + @src2.memsize = unit + AVX_512.store_instruction @dest.size,vex_mpw,evex_f,opcode,@src2,@dest.mask,@dest.rm,@src.rm + else + err 'invalid combination of operands' + end if + end macro + + macro AVX_512.basic_instruction_imm8 vex_mpw,evex_f,opcode,unit,dest,src,src2,aux& + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand @src,src + AVX_512.parse_operand @src2,src2 + x86.parse_operand @aux,aux + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') & @aux.type = 'imm' + if ( unit & ( @dest.size <> (unit-1) and not 15 + 16 | (@src2.type = 'mem' & @src2.size and not unit) ) ) | @aux.size and not 1 + err 'invalid operand size' + else if @dest.size <> @src.size | (@src2.size and not @dest.size & (unit = 0 | @src2.type = 'mmreg')) + err 'operand sizes do not match' + end if + @src2.memsize = unit + AVX_512.store_instruction @dest.size,vex_mpw,evex_f,opcode,@src2,@dest.mask,@dest.rm,@src.rm,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + + macro AVX_512.single_source_instruction_bcst_er vex_mpw,evex_f,opcode,unit,dest,src_er& + AVX_512.parse_operand_k1z @dest,dest + match src=,er, src_er + AVX_512.parse_operand @src,src + AVX_512.parse_er @src,er + else + AVX_512.parse_operand_bcst @src,src_er,unit + end match + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') + if @src.size and not @dest.size + err 'operand sizes do not match' + end if + AVX_512.store_instruction @dest.size,vex_mpw,evex_f,opcode,@src,@dest.mask,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + macro AVX_512.single_source_instruction_bcst_sae vex_mpw,evex_f,opcode,unit,dest,src_sae& + AVX_512.parse_operand_k1z @dest,dest + match src=,sae, src_sae + AVX_512.parse_operand @src,src + AVX_512.parse_sae @src,sae + else + AVX_512.parse_operand_bcst @src,src_sae,unit + end match + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') + if @src.size and not @dest.size + err 'operand sizes do not match' + end if + AVX_512.store_instruction @dest.size,vex_mpw,evex_f,opcode,@src,@dest.mask,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + macro AVX_512.single_source_instruction_bcst_sae_imm8 vex_mpw,evex_f,opcode,unit,dest,src,aux& + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand_bcst @src,src,unit + match sae=,imm, aux + AVX_512.parse_sae @src,sae + x86.parse_operand @aux,imm + else + x86.parse_operand @aux,aux + end match + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') & @aux.type = 'imm' + if @aux.size and not 1 + err 'invalid operand size' + else if @src.size and not @dest.size + err 'operand sizes do not match' + end if + AVX_512.store_instruction @dest.size,vex_mpw,evex_f,opcode,@src,@dest.mask,@dest.rm,,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + + macro AVX_512.single_source_instruction_bcst vex_mpw,evex_f,opcode,unit,dest,src& + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand_bcst @src,src,unit + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') + if @src.size and not @dest.size + err 'operand sizes do not match' + end if + AVX_512.store_instruction @dest.size,vex_mpw,evex_f,opcode,@src,@dest.mask,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + macro AVX_512.single_source_instruction vex_mpw,evex_f,opcode,unit,dest,src + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand @src,src + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') + if unit & ( @dest.size <> (unit-1) and not 15 + 16 | (@src.type = 'mem' & @src.size and not unit) ) + err 'invalid operand size' + else if @src.size and not @dest.size & (unit = 0 | @src.type = 'mmreg') + err 'operand sizes do not match' + end if + @src.memsize = unit + AVX_512.store_instruction @dest.size,vex_mpw,evex_f,opcode,@src,@dest.mask,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + iterate , add,58h, mul,59h, sub,5Ch, div,5Eh + + macro v#instr#pd? dest*,src*,src2*& + AVX_512.basic_instruction_bcst_er VEX_66_0F_W0,EVEX_W1+EVEX_VL,opcode,8,dest,src,src2 + end macro + + macro v#instr#ps? dest*,src*,src2*& + AVX_512.basic_instruction_bcst_er VEX_0F_W0,EVEX_AS_VEX+EVEX_VL,opcode,4,dest,src,src2 + end macro + + macro v#instr#sd? dest*,src*,src2*& + AVX_512.basic_instruction_er VEX_F2_0F_W0,EVEX_W1,opcode,8,dest,src,src2 + end macro + + macro v#instr#ss? dest*,src*,src2*& + AVX_512.basic_instruction_er VEX_F3_0F_W0,EVEX_AS_VEX,opcode,4,dest,src,src2 + end macro + + end iterate + + iterate , min,5Dh, max,5Fh + + macro v#instr#pd? dest*,src*,src2*& + AVX_512.basic_instruction_bcst_sae VEX_66_0F_W0,EVEX_W1+EVEX_VL,opcode,8,dest,src,src2 + end macro + + macro v#instr#ps? dest*,src*,src2*& + AVX_512.basic_instruction_bcst_sae VEX_0F_W0,EVEX_AS_VEX+EVEX_VL,opcode,4,dest,src,src2 + end macro + + macro v#instr#sd? dest*,src*,src2*& + AVX_512.basic_instruction_sae VEX_F2_0F_W0,EVEX_W1,opcode,8,dest,src,src2 + end macro + + macro v#instr#ss? dest*,src*,src2*& + AVX_512.basic_instruction_sae VEX_F3_0F_W0,EVEX_AS_VEX,opcode,4,dest,src,src2 + end macro + + end iterate + + iterate , unpckh,15h, unpckl,14h + + macro v#instr#pd? dest*,src*,src2*& + AVX_512.basic_instruction_bcst VEX_66_0F_W0,EVEX_W1+EVEX_VL,opcode,8,dest,src,src2 + end macro + + macro v#instr#ps? dest*,src*,src2*& + AVX_512.basic_instruction_bcst VEX_0F_W0,EVEX_AS_VEX+EVEX_VL,opcode,4,dest,src,src2 + end macro + + end iterate + + macro vsqrtpd? dest*,src*& + AVX_512.single_source_instruction_bcst_er VEX_66_0F_W0,EVEX_W1+EVEX_VL,51h,8,dest,src + end macro + + macro vsqrtps? dest*,src*& + AVX_512.single_source_instruction_bcst_er VEX_0F_W0,EVEX_AS_VEX+EVEX_VL,51h,4,dest,src + end macro + + macro vsqrtsd? dest*,src*,src2*& + AVX_512.basic_instruction_er VEX_F2_0F_W0,EVEX_W1,51h,8,dest,src,src2 + end macro + + macro vsqrtss? dest*,src*,src2*& + AVX_512.basic_instruction_er VEX_F3_0F_W0,EVEX_AS_VEX,51h,4,dest,src,src2 + end macro + + macro vshufpd? dest*,src*,src2*,aux*& + AVX_512.basic_instruction_bcst_imm8 VEX_66_0F_W0,EVEX_W1+EVEX_VL,0C6h,8,dest,src,src2,aux + end macro + + macro vshufps? dest*,src*,src2*,aux*& + AVX_512.basic_instruction_bcst_imm8 VEX_0F_W0,EVEX_AS_VEX+EVEX_VL,0C6h,4,dest,src,src2,aux + end macro + + macro vbroadcastss? dest*,src* + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand @src,src + if @dest.type = 'mmreg' & (@src.type = 'mmreg' | @src.type = 'mem') + if (@src.type = 'mmreg' & @src.size <> 16) | (@src.type = 'mem' & @src.size and not 4) + err 'invalid operand size' + end if + @src2.memsize = 4 + AVX_512.store_instruction @dest.size,VEX_66_0F38_W0,EVEX_AS_VEX+EVEX_VL,18h,@src,@dest.mask,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + macro vbroadcastsd? dest*,src* + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand @src,src + if @dest.type = 'mmreg' & (@src.type = 'mmreg' | @src.type = 'mem') + if @dest.size = 16 | (@src.type = 'mmreg' & @src.size <> 16) | (@src.type = 'mem' & @src.size and not 8) + err 'invalid operand size' + end if + @src.memsize = 8 + AVX_512.store_instruction @dest.size,VEX_66_0F38_W0,EVEX_W1+EVEX_VL,19h,@src,@dest.mask,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + iterate , vpbroadcastd,58h,7Ch,4, vpbroadcastq,59h,7Ch,8 + + macro instr? dest*,src* + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand @src,src + if @dest.type = 'mmreg' & (@src.type = 'mmreg' | @src.type = 'mem') + if (@src.type='mmreg' & @src.size <> 16) | (@src.type = 'mem' & @src.size and not msize) + err 'invalid operand size' + end if + @src.memsize = msize + AVX_512.store_instruction @dest.size,VEX_66_0F38_W0,EVEX_AS_VEX,opcode,@src,@dest.mask,@dest.rm + else if @dest.type = 'mmreg' & @src.type = 'reg' + if @src.size <> msize & (@src.size <> 4 | msize = 8) + err 'invalid operand size' + end if + @src.memsize = msize + if msize = 8 + AVX_512.store_instruction @dest.size,VEX_66_0F38_W1,EVEX_REQUIRED+EVEX_VL,opcode_g,@src,@dest.mask,@dest.rm + else + AVX_512.store_instruction @dest.size,VEX_66_0F38_W0,EVEX_REQUIRED+EVEX_VL,opcode_g,@src,@dest.mask,@dest.rm + end if + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , vbroadcastf32x4,VEX_66_0F38_W0,1Ah,16, vbroadcastf64x4,VEX_66_0F38_W1,1Bh,32, \ + vbroadcasti32x4,VEX_66_0F38_W0,5Ah,16, vbroadcasti64x4,VEX_66_0F38_W1,5Bh,32 + + macro instr? dest*,src* + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand @src,src + if @dest.type = 'mmreg' & @src.type = 'mem' + if @dest.size <= msize | @src.size and not msize + err 'invalid operand size' + end if + @src.memsize = msize + AVX_512.store_instruction @dest.size,vex_mpw,EVEX_REQUIRED+EVEX_VL,opcode,@src,@dest.mask,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , vshuff32x4,VEX_66_0F3A_W0,23h,4, vshuff64x2,VEX_66_0F3A_W1,23h,4, \ + vshufi32x4,VEX_66_0F3A_W0,43h,4, vshufi64x2,VEX_66_0F3A_W1,43h,4 + + macro instr? dest*,src*,src2*,aux* + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand @src,src + AVX_512.parse_operand_bcst @src2,src2,unit + x86.parse_operand @aux,aux + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') & @aux.type = 'imm' + if @dest.size < 32 | @aux.size and not 1 + err 'invalid operand size' + else if @src.size <> @dest.size | @src2.size and not @dest.size + err 'operand sizes do not match' + end if + AVX_512.store_instruction @dest.size,vex_mpw,EVEX_REQUIRED+EVEX_VL,opcode,@src2,@dest.mask,@dest.rm,@src.rm,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + macro vextractps? dest*,src*,aux* + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + x86.parse_operand @aux,aux + if (@dest.type = 'reg' | @dest.type = 'mem') & @src.type = 'mmreg' & @aux.type = 'imm' + if @dest.size and not 4 | @src.size <> 16 | @aux.size and not 1 + err 'invalid operand size' + end if + @dest.memsize = 4 + AVX_512.store_instruction 16,VEX_66_0F3A_W0,EVEX_AS_VEX,17h,@dest,0,@src.rm,,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + + macro vinsertps? dest*,src*,src2*,aux* + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + AVX_512.parse_operand @src2,src2 + x86.parse_operand @aux,aux + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'mmreg' | @src2.type = 'mem') & @aux.type = 'imm' + if @dest.size <> 16 | @src.size <> 16 | (@src2.type = 'mmreg' & @src2.size <> 16) | (@src2.type = 'mem' & @src2.size and not 4) | @aux.size and not 1 + err 'invalid operand size' + end if + @src2.memsize = 4 + AVX_512.store_instruction 16,VEX_66_0F3A_W0,EVEX_AS_VEX,21h,@src2,0,@dest.rm,@src.rm,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + + iterate , vextractf32x4,VEX_66_0F3A_W0,19h,16, vextractf64x4,VEX_66_0F3A_W1,1Bh,32, \ + vextracti32x4,VEX_66_0F3A_W0,39h,16, vextracti64x4,VEX_66_0F3A_W1,3Bh,32 + + macro instr? dest*,src*,aux* + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand @src,src + x86.parse_operand @aux,aux + if (@dest.type = 'mmreg' | @dest.type = 'mem') & @src.type = 'mmreg' & @aux.type = 'imm' + if @dest.size and not msize | @src.size <= msize | @aux.size and not 1 + err 'invalid operand size' + end if + @dest.memsize = msize + AVX_512.store_instruction @src.size,vex_mpw,EVEX_REQUIRED+EVEX_VL,opcode,@dest,@dest.mask,@src.rm,,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , vinsertf32x4,VEX_66_0F3A_W0,18h,16, vinsertf64x4,VEX_66_0F3A_W1,1Ah,32, \ + vinserti32x4,VEX_66_0F3A_W0,38h,16, vinserti64x4,VEX_66_0F3A_W1,3Ah,32 + + macro instr? dest*,src*,src2*,aux* + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand @src,src + AVX_512.parse_operand @src2,src2 + x86.parse_operand @aux,aux + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'mmreg' | @src2.type = 'mem') & @aux.type = 'imm' + if @dest.size <= msize | @src.size <= msize | @src2.size and not msize | @aux.size and not 1 + err 'invalid operand size' + end if + @src2.memsize = msize + AVX_512.store_instruction @dest.size,vex_mpw,EVEX_REQUIRED+EVEX_VL,opcode,@src2,@dest.mask,@dest.rm,@src.rm,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , vcmpps,VEX_0F_W0,VEX_0F_W0,4, vcmppd,VEX_66_0F_W0,VEX_66_0F_W1,8 + + macro instr? dest*,src*,src2*,aux*& + AVX_512.parse_operand_k1 @dest,dest + AVX_512.parse_operand @src,src + AVX_512.parse_operand_bcst @src2,src2,unit + match sae=,imm, aux + AVX_512.parse_sae @src2,sae + x86.parse_operand @aux,imm + else + x86.parse_operand @aux,aux + end match + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') & @aux.type = 'imm' + if @aux.size and not 1 + err 'invalid operand size' + else if @src.size <> @dest.size | @src2.size and not @dest.size + err 'operand sizes do not match' + end if + AVX_512.store_instruction @src.size,vex_mpw,EVEX_FORBIDDEN,0C2h,@src2,@dest.mask,@dest.rm,@src.rm,1,@aux.imm + else if @dest.type = 'maskreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') & @aux.type = 'imm' + if @aux.size and not 1 + err 'invalid operand size' + else if @src2.size and not @src.size + err 'operand sizes do not match' + end if + AVX_512.store_instruction @src.size,evex_mpw,EVEX_REQUIRED+EVEX_VL,0C2h,@src2,@dest.mask,@dest.rm,@src.rm,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , vcmpss,VEX_F3_0F_W0,VEX_F3_0F_W0,4, vcmpsd,VEX_F2_0F_W0,VEX_F2_0F_W1,8 + + macro instr? dest*,src*,src2*,aux*& + AVX_512.parse_operand_k1 @dest,dest + AVX_512.parse_operand @src,src + AVX_512.parse_operand @src2,src2 + match sae=,imm, aux + AVX_512.parse_sae @src2,sae + x86.parse_operand @aux,imm + else + x86.parse_operand @aux,aux + end match + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') & @aux.type = 'imm' + if @dest.size <> 16 | (@src2.type = 'mem' & @src2.size and not unit) + err 'invalid operand size' + else if @dest.size <> @src.size | (@src2.type = 'mmreg' & @src2.size <> @dest.size) + err 'operand sizes do not match' + end if + @src2.memsize = unit + AVX_512.store_instruction @src.size,vex_mpw,EVEX_FORBIDDEN,0C2h,@src2,@dest.mask,@dest.rm,@src.rm,1,@aux.imm + else if @dest.type = 'maskreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') & @aux.type = 'imm' + if @src.size <> 16 | (@src2.type = 'mem' & @src2.size and not unit) | @aux.size and not 1 + err 'invalid operand size' + else if @src2.type = 'mmreg' & @src2.size <> @src.size + err 'operand sizes do not match' + end if + @src2.memsize = unit + AVX_512.store_instruction @src.size,evex_mpw,EVEX_REQUIRED,0C2h,@src2,@dest.mask,@dest.rm,@src.rm,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , eq,0, lt,1, le,2, unord,3, neq,4, nlt,5, nle,6, ord,7, \ + eq_uq,8, nge,9, ngt,0Ah, false,0Bh, neq_qq,0Ch, ge,0Dh, gt,0Eh, true,0Fh, \ + eq_os,10h, lt_oq,11h, le_oq,12h, unord_s,13h, neq_us,14h, nlt_uq,15h, nle_uq,16h, ord_s,17h, \ + eq_us,18h, nge_uq,19h, ngt_uq,1Ah, false_os,1Bh, neq_os,1Ch, ge_oq,1Dh, gt_oq,1Eh, true_us,1Fh + + macro vcmp#cond#pd? dest*,src*,src2*& + vcmppd dest,src,src2,code + end macro + + macro vcmp#cond#ps? dest*,src*,src2*& + vcmpps dest,src,src2,code + end macro + + macro vcmp#cond#sd? dest*,src*,src2*& + vcmpsd dest,src,src2,code + end macro + + macro vcmp#cond#ss? dest*,src*,src2*& + vcmpss dest,src,src2,code + end macro + + end iterate + + iterate , vcomiss,VEX_0F_W0,EVEX_AS_VEX,2Fh,4, vcomisd,VEX_66_0F_W0,EVEX_W1,2Fh,8, vucomiss,VEX_0F_W0,EVEX_AS_VEX,2Eh,4, vucomisd,VEX_66_0F_W0,EVEX_W1,2Eh,8 + + macro instr? dest*,src_sae*& + AVX_512.parse_operand_k1z @dest,dest + match src=,sae, src_sae + AVX_512.parse_operand @src,src + AVX_512.parse_sae @src,sae + else + AVX_512.parse_operand @src,src_sae + end match + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') + if unit & ( @dest.size <> (unit-1) and not 15 + 16 | (@src.type = 'mem' & @src.size and not unit) ) + err 'invalid operand size' + else if @src.size and not @dest.size & (unit = 0 | @src.type = 'mmreg') + err 'operand sizes do not match' + end if + @src.memsize = unit + AVX_512.store_instruction @dest.size,vex_mpw,evex_f,opcode,@src,@dest.mask,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , kandw,41h, kandnw,42h, knotw,44h, korw,45h, kxnorw,46h, kxorw,47h + + macro instr? dest*,src*,src2* + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + AVX_512.parse_operand @src2,src2 + if @dest.type = 'maskreg' & @src.type = 'maskreg' & @src2.type = 'maskreg' + AVX.store_instruction 32,VEX_0F_W0,opcode,@src2,@dest.rm,@src.rm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , knotw,44h, kortestw,98h + + macro instr? dest*,src* + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + if @dest.type = 'maskreg' & @src.type = 'maskreg' + AVX.store_instruction 16,VEX_0F_W0,opcode,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + macro kmovw? dest*,src* + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + if @dest.type = 'maskreg' & (@src.type = 'maskreg' | @src.type = 'mem') + if @src.type = 'mem' & @src.size and not 2 + err 'invalid operand size' + end if + AVX.store_instruction 16,VEX_0F_W0,90h,@src,@dest.rm + else if @dest.type = 'mem' & @src.type = 'maskreg' + if @dest.size and not 2 + err 'invalid operand size' + end if + AVX.store_instruction 16,VEX_0F_W0,91h,@dest,@src.rm + else if @dest.type = 'maskreg' & @src.type = 'reg' + if @src.size <> 4 + err 'invalid operand size' + end if + AVX.store_instruction 16,VEX_0F_W0,92h,@src,@dest.rm + else if @dest.type = 'reg' & @src.type = 'maskreg' + if @dest.size <> 4 + err 'invalid operand size' + end if + AVX.store_instruction 16,VEX_0F_W0,93h,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + iterate , kshiftrw,VEX_66_0F3A_W1,30h, kshiftlw,VEX_66_0F3A_W1,32h + + macro instr? dest*,src*,aux* + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + x86.parse_operand @aux,aux + if @dest.type = 'maskreg' & @src.type = 'maskreg' & @aux.type = 'imm' + if @aux.size and not 1 + err 'invalid operand size' + end if + AVX.store_instruction 16,vex_mpw,opcode,@src,@dest.rm,,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + macro kunpckbw? dest*,src*,src2* + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + AVX_512.parse_operand @src2,src2 + if @dest.type = 'maskreg' & @src.type = 'maskreg' & @src2.type = 'maskreg' + AVX.store_instruction 32,VEX_66_0F_W0,4Bh,@src2,@dest.rm,@src.rm + else + err 'invalid combination of operands' + end if + end macro + + iterate , vcvtdq2pd,EVEX_AS_VEX+EVEX_VL,0E6h, vcvtudq2pd,EVEX_REQUIRED+EVEX_VL,7Ah + + macro instr? dest*,src* + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand_bcst @src,src,4 + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') + if (@src.type = 'mem' & @src.size and not (@dest.size shr 1)) | (@src.type = 'mmreg' & (@dest.size shr 1 - 1) and not 15 + 16 <> @src.size) + err 'invalid operand size' + end if + if @src.memsize = 0 + @src.memsize = @dest.size shr 1 + end if + AVX_512.store_instruction @dest.size,VEX_F3_0F_W0,evex_f,opcode,@src,@dest.mask,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , vcvtpd2dq,VEX_F2_0F_W0,EVEX_W1+EVEX_VL,0E6h, vcvtpd2ps,VEX_66_0F_W0,EVEX_W1+EVEX_VL,5Ah, vcvtpd2udq,VEX_0F_W1,EVEX_REQUIRED+EVEX_VL,79h + + macro instr? dest*,src_er*& + AVX_512.parse_operand_k1z @dest,dest + match src=,er, src_er + AVX_512.parse_operand @src,src + AVX_512.parse_er @src,er + else + AVX_512.parse_operand_bcst @src,src_er,8 + end match + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') + if @src.size = 0 + if @dest.size = 16 + err 'operand size not specified' + else + @src.size = 64 + end if + end if + if (@src.size shr 1 - 1) and not 15 + 16 <> @dest.size | @src.size > 64 + err 'invalid operand size' + end if + AVX_512.store_instruction @src.size,vex_mpw,evex_f,opcode,@src,@dest.mask,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , vcvtps2pd,VEX_0F_W0,EVEX_AS_VEX+EVEX_VL,5Ah + + macro instr? dest*,src_sae*& + AVX_512.parse_operand_k1z @dest,dest + match src=,sae, src_sae + AVX_512.parse_operand @src,src + AVX_512.parse_sae @src,sae + else + AVX_512.parse_operand_bcst @src,src_sae,4 + end match + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') + if (@src.type = 'mem' & @src.size and not (@dest.size shr 1)) | (@src.type = 'mmreg' & (@dest.size shr 1 - 1) and not 15 + 16 <> @src.size) + err 'invalid operand size' + end if + if @src.memsize = 0 + @src.memsize = @dest.size shr 1 + end if + AVX_512.store_instruction @dest.size,vex_mpw,evex_f,opcode,@src,@dest.mask,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , vcvttpd2dq,VEX_66_0F_W0,EVEX_W1+EVEX_VL,0E6h, vcvttpd2udq,VEX_0F_W1,EVEX_REQUIRED+EVEX_VL,78h + + macro instr? dest*,src_sae*& + AVX_512.parse_operand_k1z @dest,dest + match src=,sae, src_sae + AVX_512.parse_operand @src,src + AVX_512.parse_sae @src,sae + else + AVX_512.parse_operand_bcst @src,src_sae,8 + end match + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') + if @src.size = 0 + if @dest.size = 16 + err 'operand size not specified' + else + @src.size = 64 + end if + end if + if (@src.size shr 1 - 1) and not 15 + 16 <> @dest.size | @src.size > 64 + err 'invalid operand size' + end if + AVX_512.store_instruction @src.size,vex_mpw,evex_f,opcode,@src,@dest.mask,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , vcvtdq2ps,VEX_0F_W0,EVEX_AS_VEX+EVEX_VL,5Bh, vcvtudq2ps,VEX_F2_0F_W0,EVEX_REQUIRED+EVEX_VL,7Ah, \ + vcvtps2dq,VEX_66_0F_W0,EVEX_AS_VEX+EVEX_VL,5Bh, vcvtps2udq,VEX_0F_W0,EVEX_REQUIRED+EVEX_VL,79h + + macro instr? dest*,src*& + AVX_512.single_source_instruction_bcst_er vex_mpw,evex_f,opcode,4,dest,src + end macro + + end iterate + + + iterate , vcvttps2dq,VEX_F3_0F_W0,EVEX_AS_VEX+EVEX_VL,5Bh, vcvttps2udq,VEX_0F_W0,EVEX_REQUIRED+EVEX_VL,78h + + macro instr? dest*,src*& + AVX_512.single_source_instruction_bcst_sae vex_mpw,evex_f,opcode,4,dest,src + end macro + + end iterate + + macro vcvtph2ps? dest*,src_sae*& + AVX_512.parse_operand_k1z @dest,dest + match src=,sae, src_sae + AVX_512.parse_operand @src,src + AVX_512.parse_sae @src,sae + else + AVX_512.parse_operand @src,src_sae + end match + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') + if (@src.type = 'mem' & @src.size and not (@dest.size shr 1)) | (@src.type = 'mmreg' & (@dest.size shr 1 - 1) and not 15 + 16 <> @src.size) + err 'invalid operand size' + end if + if @src.memsize = 0 + @src.memsize = @dest.size shr 1 + end if + AVX_512.store_instruction @dest.size,VEX_66_0F38_W0,EVEX_AS_VEX+EVEX_VL,13h,@src,@dest.mask,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + macro vcvtps2ph? dest*,src*,aux*& + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand @src,src + match sae=,imm, aux + AVX_512.parse_sae @src,sae + x86.parse_operand @aux,imm + else + x86.parse_operand @aux,aux + end match + if (@dest.type = 'mem' | @dest.type = 'mmreg') & @src.type = 'mmreg' + if (@dest.type = 'mem' & @dest.size and not (@src.size shr 1)) | (@dest.type = 'mmreg' & (@src.size shr 1 - 1) and not 15 + 16 <> @dest.size) + err 'invalid operand size' + end if + if @dest.memsize = 0 + @dest.memsize = @src.size shr 1 + end if + AVX_512.store_instruction @src.size,VEX_66_0F3A_W0,EVEX_AS_VEX+EVEX_VL,1Dh,@dest,@dest.mask,@src.rm,,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + + iterate , vcvtsd2si,VEX_F2_0F,EVEX_AS_VEX,2Dh,8, vcvtss2si,VEX_F3_0F,EVEX_AS_VEX,2Dh,4, \ + vcvtsd2usi,VEX_F2_0F,EVEX_REQUIRED,79h,8, vcvtss2usi,VEX_F3_0F,EVEX_REQUIRED,79h,4 + + macro instr? dest*,src_er*& + x86.parse_operand @dest,dest + match src=,er, src_er + AVX_512.parse_operand @src,src + AVX_512.parse_er @src,er,16 + else + AVX_512.parse_operand @src,src_er + end match + if @dest.type = 'reg' & (@src.type = 'mem' | @src.type = 'mmreg') + if (@dest.size <> 4 & @dest.size <> 8) | (@src.type = 'mem' & @src.size and not msize) | (@src.type = 'mmreg' & @src.size <> 16) + err 'invalid operand size' + end if + if @dest.size = 8 + if x86.mode < 64 + err 'instruction requires long mode' + end if + AVX_512.store_instruction 16,vex_mp#_W1,evex_f,opcode,@src,0,@dest.rm + else + AVX_512.store_instruction 16,vex_mp#_W0,evex_f,opcode,@src,0,@dest.rm + end if + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , vcvttsd2si,VEX_F2_0F,EVEX_AS_VEX,2Ch,8, vcvttss2si,VEX_F3_0F,EVEX_AS_VEX,2Ch,4, \ + vcvttsd2usi,VEX_F2_0F,EVEX_REQUIRED,78h,8, vcvttss2usi,VEX_F3_0F,EVEX_REQUIRED,78h,4 + + macro instr? dest*,src_sae*& + x86.parse_operand @dest,dest + match src=,sae, src_sae + AVX_512.parse_operand @src,src + AVX_512.parse_sae @src,sae + else + AVX_512.parse_operand @src,src_sae + end match + if @dest.type = 'reg' & (@src.type = 'mem' | @src.type = 'mmreg') + if (@dest.size <> 4 & @dest.size <> 8) | (@src.type = 'mem' & @src.size and not msize) | (@src.type = 'mmreg' & @src.size <> 16) + err 'invalid operand size' + end if + if @dest.size = 8 + if x86.mode < 64 + err 'instruction requires long mode' + end if + AVX_512.store_instruction 16,vex_mp#_W1,evex_f,opcode,@src,0,@dest.rm + else + AVX_512.store_instruction 16,vex_mp#_W0,evex_f,opcode,@src,0,@dest.rm + end if + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + macro vcvtsd2ss? dest*,src*,src2*& + AVX_512.basic_instruction_er VEX_F2_0F_W0,EVEX_W1,5Ah,8,dest,src,src2 + end macro + + macro vcvtss2sd? dest*,src*,src2*& + AVX_512.basic_instruction_sae VEX_F3_0F_W0,EVEX_AS_VEX,5Ah,4,dest,src,src2 + end macro + + iterate , vcvtsi2sd,EVEX_AS_VEX,2Ah, vcvtusi2sd,EVEX_REQUIRED,7Bh + + macro vcvtsi2sd? dest*,src*,src_er*& + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + match src2=,er, src_er + AVX_512.parse_operand @src2,src2 + AVX_512.parse_er @src2,er,8 + else + AVX_512.parse_operand @src2,src_er + end match + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'reg' | @src2.type = 'mem') + if @src.size = 0 + err ' operand size not specified' + else if @dest.size <> 16 | @src.size <> 16 | (@src2.size <> 4 & @src2.size <> 8) + err 'invalid operand size' + end if + if @src2.size = 8 + if x86.mode < 64 + err 'instruction requires long mode' + end if + AVX_512.store_instruction 16,VEX_F2_0F_W1,evex_f,opcode,@src2,0,@dest.rm,@src.rm + else + AVX_512.store_instruction 16,VEX_F2_0F_W0,evex_f,opcode,@src2,0,@dest.rm,@src.rm + end if + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , vcvtsi2ss,EVEX_AS_VEX,2Ah, vcvtusi2ss,EVEX_REQUIRED,7Bh + + macro vcvtsi2ss? dest*,src*,src_er*& + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + match src2=,er, src_er + AVX_512.parse_operand @src2,src2 + AVX_512.parse_er @src2,er,@src2.size + else + AVX_512.parse_operand @src2,src_er + end match + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'reg' | @src2.type = 'mem') + if @src.size = 0 + err ' operand size not specified' + else if @dest.size <> 16 | @src.size <> 16 | (@src2.size <> 4 & @src2.size <> 8) + err 'invalid operand size' + end if + if @src2.size = 8 + if x86.mode < 64 + err 'instruction requires long mode' + end if + AVX_512.store_instruction 16,VEX_F3_0F_W1,evex_f,opcode,@src2,0,@dest.rm,@src.rm + else + AVX_512.store_instruction 16,VEX_F3_0F_W0,evex_f,opcode,@src2,0,@dest.rm,@src.rm + end if + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , vmovapd,VEX_66_0F_W0,EVEX_W1+EVEX_VL,28h,29h, vmovaps,VEX_0F_W0,EVEX_AS_VEX+EVEX_VL,28h,29h, \ + vmovupd,VEX_66_0F_W0,EVEX_W1+EVEX_VL,10h,11h, vmovups,VEX_0F_W0,EVEX_AS_VEX+EVEX_VL,10h,11h, \ + vmovdqa32,VEX_66_0F_W0,EVEX_REQUIRED+EVEX_VL,6Fh,7Fh, vmovdqa64,VEX_66_0F_W1,EVEX_REQUIRED+EVEX_VL,6Fh,7Fh, \ + vmovdqu32,VEX_F3_0F_W0,EVEX_REQUIRED+EVEX_VL,6Fh,7Fh, vmovdqu64,VEX_F3_0F_W1,EVEX_REQUIRED+EVEX_VL,6Fh,7Fh + + macro instr? dest*,src* + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand @src,src + if @dest.type = 'mmreg' & (@src.type = 'mmreg' | @src.type = 'mem') + if @src.size and not @dest.size + err 'operand sizes do not match' + end if + AVX_512.store_instruction @dest.size,vex_mpw,evex_f,opcode_rm,@src,@dest.mask,@dest.rm + else if @dest.type = 'mem' & @src.type = 'mmreg' + if @dest.size and not @src.size + err 'operand sizes do not match' + end if + AVX_512.store_instruction @src.size,vex_mpw,evex_f,opcode_mr,@dest,@dest.mask,@src.rm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + macro vmovd? dest*,src* + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + if @dest.type = 'mmreg' & (@src.type = 'reg' | @src.type = 'mem') + if @dest.size <> 16 | @src.size and not 4 + err 'invalid operand size' + end if + @src.memsize = 4 + AVX_512.store_instruction 16,VEX_66_0F_W0,EVEX_AS_VEX,6Eh,@src,0,@dest.rm + else if (@dest.type = 'reg' | @dest.type = 'mem') & @src.type = 'mmreg' + if @dest.size and not 4 | @src.size <> 16 + err 'operand sizes do not match' + end if + @dest.memsize = 4 + AVX_512.store_instruction 16,VEX_66_0F_W0,EVEX_AS_VEX,7Eh,@dest,0,@src.rm + else + err 'invalid combination of operands' + end if + end macro + + macro vmovq? dest*,src* + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + if @dest.type = 'mmreg' & (@src.type = 'mmreg' | @src.type = 'mem') + if @dest.size <> 16 | (@src.type = 'mmreg' & @src.size <> 16) | (@src.type = 'mem' and @src.size and not 8) + err 'invalid operand size' + end if + @src.memsize = 8 + AVX_512.store_instruction 16,VEX_F3_0F_W0,EVEX_W1,7Eh,@src,0,@dest.rm + else if @dest.type = 'mem' & @src.type = 'mmreg' + if @dest.size and not 8 | @src.size <> 16 + err 'invalid operand size' + end if + @dest.memsize = 8 + AVX_512.store_instruction 16,VEX_66_0F_W0,EVEX_W1,0D6h,@dest,0,@src.rm + else if @dest.type = 'mmreg' & @src.type = 'reg' + if @dest.size <> 16 | @src.size <> 8 + err 'invalid operand size' + end if + if x86.mode < 64 + err 'instruction requires long mode' + end if + AVX_512.store_instruction 16,VEX_66_0F_W1,EVEX_W1,6Eh,@src,0,@dest.rm + else if @dest.type = 'reg' & @src.type = 'mmreg' + if @dest.size <> 8 | @src.size <> 16 + err 'invalid operand size' + end if + if x86.mode < 64 + err 'instruction requires long mode' + end if + AVX_512.store_instruction 16,VEX_66_0F_W1,EVEX_W1,7Eh,@dest,0,@src.rm + else + err 'invalid combination of operands' + end if + end macro + + macro vmovddup? dest*,src* + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand @src,src + if @dest.type = 'mmreg' & (@src.type = 'mmreg' | @src.type = 'mem') + if @src.type = 'mem' & @dest.size = 16 + if @src.size and not 8 + err 'invalid operand size' + end if + @src.memsize = 8 + else + if @src.size and not @dest.size + err 'operand sizes do not match' + end if + @src.memsize = @dest.size + end if + AVX_512.store_instruction @dest.size,VEX_F2_0F_W0,EVEX_W1+EVEX_VL,12h,@src,@dest.mask,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + iterate , vmovhlps,12h, vmovlhps,16h + + macro instr? dest*,src*,src2* + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + AVX_512.parse_operand @src2,src2 + if @dest.type = 'mmreg' & @src.type = 'mmreg' & @src2.type = 'mmreg' + if @dest.size <> 16 + err 'invalid operand size' + else if @src.size <> @dest.size | @src2.size <> @dest.size + err 'operand sizes do not match' + end if + AVX_512.store_instruction 16,VEX_0F_W0,EVEX_AS_VEX,opcode,@src2,0,@dest.rm,@src.rm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , vmovhpd,VEX_66_0F_W0,EVEX_W1,16h, vmovhps,VEX_0F_W0,EVEX_AS_VEX,16h, vmovlpd,VEX_66_0F_W0,EVEX_W1,12h, vmovlps,VEX_0F_W0,EVEX_AS_VEX,12h + + macro instr? dest*,src*,src2 + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + match , src2 + if @dest.type = 'mem' & @src.type = 'mmreg' + if @dest.size and not 8 | @src.size <> 16 + err 'invalid operand size' + end if + @dest.memsize = 8 + AVX_512.store_instruction 16,vex_mpw,evex_f,opcode+1,@dest,0,@src.rm + else + err 'invalid combination of operands' + end if + else + AVX_512.parse_operand @src2,src2 + if @dest.type = 'mmreg' & @src.type = 'mmreg' & @src2.type = 'mem' + if @dest.size <> 16 | @src.size <> 16 | @src2.size and not 8 + err 'invalid operand size' + end if + @src2.memsize = 8 + AVX_512.store_instruction 16,vex_mpw,evex_f,opcode,@src2,0,@dest.rm,@src.rm + else + err 'invalid combination of operands' + end if + end match + end macro + + end iterate + + iterate , vmovntdq,VEX_66_0F_W0,EVEX_AS_VEX+EVEX_VL,0E7h, vmovntpd,VEX_66_0F_W0,EVEX_W1+EVEX_VL,2Bh, vmovntps,VEX_0F_W0,EVEX_AS_VEX+EVEX_VL,2Bh + + macro instr? dest*,src* + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + if @dest.type = 'mem' & @src.type = 'mmreg' + if @dest.size and not @src.size + err 'operand sizes do not match' + end if + AVX_512.store_instruction @src.size,vex_mpw,evex_f,opcode,@dest,0,@src.rm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + macro vmovntdqa? dest*,src* + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + if @dest.type = 'mmreg' & @src.type = 'mem' + if @src.size and not @dest.size + err 'operand sizes do not match' + end if + AVX_512.store_instruction @dest.size,VEX_66_0F38_W0,EVEX_AS_VEX+EVEX_VL,2Ah,@src,0,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + iterate , vmovsd,VEX_F2_0F_W0,EVEX_W1,8, vmovss,VEX_F3_0F_W0,EVEX_AS_VEX,4 + + macro instr? dest*,src*,src2 + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand @src,src + match , src2 + if @dest.type = 'mmreg' & @src.type = 'mem' + if @dest.size <> 16 | @src.size and not msize + err 'invalid operand size' + end if + @src.memsize = msize + AVX_512.store_instruction 16,vex_mpw,evex_f,10h,@src,@dest.mask,@dest.rm + else if @dest.type = 'mem' & @src.type = 'mmreg' + if @dest.size and not msize | @src.size <> 16 + err 'invalid operand size' + end if + @dest.memsize = msize + AVX_512.store_instruction 16,vex_mpw,evex_f,11h,@dest,@dest.mask,@src.rm + else + err 'invalid combination of operands' + end if + else + AVX_512.parse_operand @src2,src2 + if @dest.type = 'mmreg' & @src.type = 'mmreg' & @src2.type = 'mmreg' + if @dest.size <> 16 | @src.size <> 16 | @src2.size <> 16 + err 'invalid operand size' + end if + AVX_512.store_instruction 16,vex_mpw,evex_f,10h,@src2,@dest.mask,@dest.rm,@src.rm + else + err 'invalid combination of operands' + end if + end match + end macro + + end iterate + + macro vmovshdup? dest*,src* + AVX_512.single_source_instruction VEX_F3_0F_W0,EVEX_AS_VEX+EVEX_VL,16h,0,dest,src + end macro + + macro vmovsldup? dest*,src* + AVX_512.single_source_instruction VEX_F3_0F_W0,EVEX_AS_VEX+EVEX_VL,12h,0,dest,src + end macro + + iterate , vpermilps,4,EVEX_AS_VEX+EVEX_VL,0Ch,4, vpermilpd,8,EVEX_W1+EVEX_VL,0Dh,5 + + macro instr? dest*,src*,src2* + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand_bcst @src,src,unit + AVX_512.parse_operand_bcst @src2,src2,unit + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') + if @src.size <> @dest.size | @src2.size and not @dest.size + err 'operand sizes do not match' + end if + AVX_512.store_instruction @dest.size,VEX_66_0F38_W0,evex_f,opcode_rrm,@src2,@dest.mask,@dest.rm,@src.rm + else if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') & @src2.type = 'imm' + if @src2.size and not 1 + err 'invalid operand size' + else if @src.size and not @dest.size + err 'operand sizes do not match' + end if + AVX_512.store_instruction @dest.size,VEX_66_0F3A_W0,evex_f,opcode_rri,@src,@dest.mask,@dest.rm,,1,@src2.imm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , vpaddd,0FEh, vpsubd,0FAh, vpunpckhdq,6Ah, vpunpckldq,62h + + macro instr? dest*,src*,src2* + AVX_512.basic_instruction_bcst VEX_66_0F_W0,EVEX_AS_VEX+EVEX_VL,opcode,4,dest,src,src2 + end macro + + end iterate + + iterate , vpaddq,0D4h, vpmuludq,0F4h, vpsubq,0FBh, vpunpckhqdq,6Dh, vpunpcklqdq,6Ch + + macro instr? dest*,src*,src2* + AVX_512.basic_instruction_bcst VEX_66_0F_W0,EVEX_W1+EVEX_VL,opcode,8,dest,src,src2 + end macro + + end iterate + + iterate , vpandd,0DBh, vpandnd,0DFh, vpord,0EBh, vpxord,0EFh + + macro instr? dest*,src*,src2* + AVX_512.basic_instruction_bcst VEX_66_0F_W0,EVEX_REQUIRED+EVEX_VL,opcode,4,dest,src,src2 + end macro + + end iterate + + iterate , vpandq,0DBh, vpandnq,0DFh, vporq,0EBh, vpxorq,0EFh + + macro instr? dest*,src*,src2* + AVX_512.basic_instruction_bcst VEX_66_0F_W1,EVEX_REQUIRED+EVEX_VL,opcode,8,dest,src,src2 + end macro + + end iterate + + iterate , vpmaxsd,3Dh, vpmaxud,3Fh, vpminsd,39h, vpminud,3Bh, vpmulld,40h + + macro instr? dest*,src*,src2* + AVX_512.basic_instruction_bcst VEX_66_0F38_W0,EVEX_AS_VEX+EVEX_VL,opcode,4,dest,src,src2 + end macro + + end iterate + + iterate , vpmuldq,28h + + macro instr? dest*,src*,src2* + AVX_512.basic_instruction_bcst VEX_66_0F38_W0,EVEX_W1+EVEX_VL,opcode,8,dest,src,src2 + end macro + + end iterate + + iterate , vpmuldq,28h, vpmaxsq,3Dh, vpmaxuq,3Fh, vpminsq,39h, vpminuq,3Bh, vpmullq,40h + + macro instr? dest*,src*,src2* + AVX_512.basic_instruction_bcst VEX_66_0F38_W1,EVEX_REQUIRED+EVEX_VL,opcode,8,dest,src,src2 + end macro + + end iterate + + iterate , vpabsd,1Eh + + macro instr? dest*,src* + AVX_512.single_source_instruction_bcst VEX_66_0F38_W0,EVEX_AS_VEX+EVEX_VL,opcode,4,dest,src + end macro + + end iterate + + iterate , vpabsq,1Fh + + macro instr? dest*,src* + AVX_512.single_source_instruction_bcst VEX_66_0F38_W1,EVEX_REQUIRED+EVEX_VL,opcode,8,dest,src + end macro + + end iterate + + iterate , vpshufd,VEX_66_0F_W0 + + macro instr? dest*,src*,aux* + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand @src,src + x86.parse_operand @aux,aux + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') & @aux.type = 'imm' + if @aux.size and not 1 + err 'invalid operand size' + else if @src.size and not @dest.size + err 'operand sizes do not match' + end if + AVX_512.store_instruction @dest.size,vex_mpw,EVEX_AS_VEX+EVEX_VL,70h,@src,@dest.mask,@dest.rm,,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , vpsllvd,47h, vpsrlvd,45h, vpsravd,46h + + macro instr? dest*,src*,src2* + AVX_512.basic_instruction_bcst VEX_66_0F38_W0,EVEX_AS_VEX+EVEX_VL,opcode,4,dest,src,src2 + end macro + + end iterate + + iterate , vpsllvq,EVEX_AS_VEX+EVEX_VL,47h, vpsrlvq,EVEX_AS_VEX+EVEX_VL,45h, vpsravq,EVEX_REQUIRED+EVEX_VL,46h + + macro instr? dest*,src*,src2* + AVX_512.basic_instruction_bcst VEX_66_0F38_W1,evex_f,opcode,8,dest,src,src2 + end macro + + end iterate + + iterate , vpermi2d,4,VEX_66_0F38_W0,76h, vpermi2q,8,VEX_66_0F38_W1,76h, \ + vpermt2d,4,VEX_66_0F38_W0,7Eh, vpermt2q,8,VEX_66_0F38_W1,7Eh, \ + vprorvd,4,VEX_66_0F38_W0,14h, vprorvq,8,VEX_66_0F38_W1,14h, \ + vprolvd,4,VEX_66_0F38_W0,15h, vprolvq,8,VEX_66_0F38_W1,15h + + macro instr? dest*,src*,src2* + AVX_512.basic_instruction_bcst vex_mpw,EVEX_REQUIRED+EVEX_VL,opcode,unit,dest,src,src2 + end macro + + end iterate + + iterate , vprord,4,VEX_66_0F_W0,0, vprorq,8,VEX_66_0F_W1,0, vprold,4,VEX_66_0F_W0,1, vprolq,8,VEX_66_0F_W1,1 + + macro instr? dest*,src*,aux* + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand_bcst @src,src,unit + x86.parse_operand @aux,aux + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') & @aux.type = 'imm' + if @src.size and not @dest.size | @aux.size and not 1 + err 'operand sizes do not match' + end if + AVX_512.store_instruction @dest.size,vex_mpw,EVEX_REQUIRED+EVEX_VL,72h,@src,@dest.mask,postbyte,@dest.rm,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , vpslld,0F2h,72h,6, vpsrad,0E2h,72h,4, vpsrld,0D2h,72h,2 + + macro instr? dest*,src*,src2* + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand @src2,src2 + if @src2.type = 'imm' + AVX_512.parse_operand_bcst @src,src,4 + else + AVX_512.parse_operand @src,src + end if + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') + @src2.memsize = 16 + if @src2.size and not @src2.memsize + err 'invalid operand size' + else if @src.size <> @dest.size + err 'operand sizes do not match' + end if + AVX_512.store_instruction @dest.size,VEX_66_0F_W0,EVEX_AS_VEX,opcode_rrm,@src2,@dest.mask,@dest.rm,@src.rm + else if @dest.type = 'mmreg' & (@src.type = 'mmreg' | @src.type = 'mem') & @src2.type = 'imm' + if @src2.size and not 1 + err 'invalid operand size' + else if @src.size <> @dest.size + err 'operand sizes do not match' + end if + if @src.type = 'mem' + AVX_512.store_instruction @dest.size,VEX_66_0F_W0,EVEX_REQUIRED+EVEX_VL,opcode,@src,@dest.mask,postbyte,@dest.rm,1,@src2.imm + else + AVX_512.store_instruction @dest.size,VEX_66_0F_W0,EVEX_AS_VEX+EVEX_VL,opcode,@src,@dest.mask,postbyte,@dest.rm,1,@src2.imm + end if + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , vpsllq,0F3h,73h,6, vpsraq,0E2h,72h,4, vpsrlq,0D3h,73h,2 + + macro instr? dest*,src*,src2* + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand @src2,src2 + if @src2.type = 'imm' + AVX_512.parse_operand_bcst @src,src,8 + else + AVX_512.parse_operand @src,src + end if + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') + @src2.memsize = 16 + if @src2.size and not @src2.memsize + err 'invalid operand size' + else if @src.size <> @dest.size + err 'operand sizes do not match' + end if + if `instr = 'vpsraq' + AVX_512.store_instruction @dest.size,VEX_66_0F_W1,EVEX_REQUIRED+EVEX_VL,opcode_rrm,@src2,@dest.mask,@dest.rm,@src.rm + else + AVX_512.store_instruction @dest.size,VEX_66_0F_W0,EVEX_W1+EVEX_VL,opcode_rrm,@src2,@dest.mask,@dest.rm,@src.rm + end if + else if @dest.type = 'mmreg' & (@src.type = 'mmreg' | @src.type = 'mem') & @src2.type = 'imm' + if @src2.size and not 1 + err 'invalid operand size' + else if @src.size and not @dest.size + err 'operand sizes do not match' + end if + if @src.type = 'mem' | `instr = 'vpsraq' + AVX_512.store_instruction @dest.size,VEX_66_0F_W1,EVEX_REQUIRED+EVEX_VL,opcode,@src,@dest.mask,postbyte,@dest.rm,1,@src2.imm + else + AVX_512.store_instruction @dest.size,VEX_66_0F_W0,EVEX_W1+EVEX_VL,opcode,@src,@dest.mask,postbyte,@dest.rm,1,@src2.imm + end if + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , vpcmpeqd,76h, vpcmpgtd,66h + + macro instr? dest*,src*,src2* + AVX_512.parse_operand_k1 @dest,dest + AVX_512.parse_operand @src,src + AVX_512.parse_operand_bcst @src2,src2,4 + if @dest.type = 'maskreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') + if @src2.size and not @src.size + err 'operand sizes do not match' + end if + AVX_512.store_instruction @src.size,VEX_66_0F_W0,EVEX_REQUIRED+EVEX_VL,opcode,@src2,@dest.mask,@dest.rm,@src.rm + else if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') + if @src.size <> @dest.size | @src2.size and not @dest.size + err 'operand sizes do not match' + end if + AVX_512.store_instruction @src.size,VEX_66_0F_W0,EVEX_FORBIDDEN,opcode,@src2,@dest.mask,@dest.rm,@src.rm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , vpcmpeqq,29h, vpcmpgtq,37h + + macro instr? dest*,src*,src2* + AVX_512.parse_operand_k1 @dest,dest + AVX_512.parse_operand @src,src + AVX_512.parse_operand_bcst @src2,src2,8 + if @dest.type = 'maskreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') + if @src2.size and not @src.size + err 'operand sizes do not match' + end if + AVX_512.store_instruction @src.size,VEX_66_0F38_W1,EVEX_REQUIRED+EVEX_VL,opcode,@src2,@dest.mask,@dest.rm,@src.rm + else if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') + if @src.size <> @dest.size | @src2.size and not @dest.size + err 'operand sizes do not match' + end if + AVX_512.store_instruction @src.size,VEX_66_0F38_W0,EVEX_FORBIDDEN,opcode,@src2,@dest.mask,@dest.rm,@src.rm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , vptestnmd,4,VEX_F3_0F38_W0,27h, vptestnmq,8,VEX_F3_0F38_W1,27h, vptestmd,4,VEX_66_0F38_W0,27h, vptestmq,8,VEX_66_0F38_W1,27h + + macro instr? dest*,src*,src2* + AVX_512.parse_operand_k1 @dest,dest + AVX_512.parse_operand @src,src + AVX_512.parse_operand_bcst @src2,src2,unit + if @dest.type = 'maskreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') + if @src2.size and not @src.size + err 'operand sizes do not match' + end if + AVX_512.store_instruction @src.size,vex_mpw,EVEX_REQUIRED+EVEX_VL,opcode,@src2,@dest.mask,@dest.rm,@src.rm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , vpcmpd,4,VEX_66_0F3A_W0,1Fh, vpcmpud,4,VEX_66_0F3A_W0,1Eh, vpcmpq,8,VEX_66_0F3A_W1,1Fh, vpcmpuq,8,VEX_66_0F3A_W1,1Eh + + macro instr? dest*,src*,src2*,aux* + AVX_512.parse_operand_k1 @dest,dest + AVX_512.parse_operand @src,src + AVX_512.parse_operand_bcst @src2,src2,unit + x86.parse_operand @aux,aux + if @dest.type = 'maskreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') & @aux.type = 'imm' + if @src2.size and not @src.size | @aux.size and not 1 + err 'operand sizes do not match' + end if + AVX_512.store_instruction @src.size,vex_mpw,EVEX_REQUIRED+EVEX_VL,opcode,@src2,@dest.mask,@dest.rm,@src.rm,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , vpmovsxbd,21h,4, vpmovsxbq,22h,2, vpmovsxwd,23h,8, vpmovsxwq,24h,4, vpmovsxdq,25h,8, \ + vpmovzxbd,31h,4, vpmovzxbq,32h,2, vpmovzxwd,33h,8, vpmovzxwq,34h,4, vpmovzxdq,35h,8 + + macro instr? dest*,src* + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand @src,src + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') + @src.memsize = msize * (@dest.size shr 4) + if (@src.type = 'mmreg' & @src.size <> (@src.memsize-1) and not 15 + 16) | (@src.type = 'mem' & @src.size and not @src.memsize) + err 'invalid operand size' + end if + AVX_512.store_instruction @dest.size,VEX_66_0F38_W0,EVEX_AS_VEX+EVEX_VL,opcode,@src,@dest.mask,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , vpermq,0, vpermpd,1 + + macro instr? dest*,src*,aux* + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand_bcst @src,src,8 + x86.parse_operand @aux,aux + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') & @aux.type = 'imm' + if @dest.size < 32 | @aux.size and not 1 + err 'invalid operand size' + else if @src.size and not @dest.size + err 'operand sizes do not match' + end if + AVX_512.store_instruction @dest.size,VEX_66_0F3A_W1,EVEX_AS_VEX+EVEX_VL,opcode,@src,@dest.mask,@dest.rm,,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , vpermd,36h, vpermps,16h + + macro instr? dest*,src*,src2* + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand @src,src + AVX_512.parse_operand_bcst @src2,src2,4 + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') + if @dest.size < 32 + err 'invalid operand size' + else if @src.size <> @dest.size | @src2.size and not @dest.size + err 'operand sizes do not match' + end if + AVX_512.store_instruction @dest.size,VEX_66_0F38_W0,EVEX_AS_VEX+EVEX_VL,opcode,@src2,@dest.mask,@dest.rm,@src.rm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , vfmaddsub,6, vfmsubadd,7, vfmaddsub,8, vfmsub,0Ah, vfnmadd,0Ch, vfnmsub,0Eh + + iterate , 132,90h, 213,0A0h, 231,0B0h + + macro instr#order#pd? dest*,src*,src2*& + AVX_512.basic_instruction_bcst_er VEX_66_0F38_W1,EVEX_AS_VEX+EVEX_VL,hcode+lcode,8,dest,src,src2 + end macro + + macro instr#order#ps? dest*,src*,src2*& + AVX_512.basic_instruction_bcst_er VEX_66_0F38_W0,EVEX_AS_VEX+EVEX_VL,hcode+lcode,4,dest,src,src2 + end macro + + if lcode > 7 + + macro instr#order#sd? dest*,src*,src2*& + AVX_512.basic_instruction_er VEX_66_0F38_W1,EVEX_AS_VEX,hcode+lcode+1,8,dest,src,src2 + end macro + + macro instr#order#ss? dest*,src*,src2*& + AVX_512.basic_instruction_er VEX_66_0F38_W0,EVEX_AS_VEX,hcode+lcode+1,4,dest,src,src2 + end macro + + end if + + end iterate + + end iterate + + iterate , valignd,4,VEX_66_0F3A_W0,3, vpternlogd,4,VEX_66_0F3A_W0,25h, vpternlogq,8,VEX_66_0F3A_W1,25h + + macro instr? dest*,src*,src2*,aux*& + AVX_512.basic_instruction_bcst_imm8 vex_mpw,EVEX_REQUIRED+EVEX_VL,opcode,unit,dest,src,src2,aux + end macro + + end iterate + + iterate , valignq,3 + + macro instr? dest*,src*,src2*,aux*& + AVX_512.basic_instruction_bcst_imm8 VEX_66_0F3A_W1,EVEX_REQUIRED+EVEX_VL,opcode,8,dest,src,src2,aux + end macro + + end iterate + + iterate , vblendmps,65h, vpblendmd,64h + + macro instr? dest*,src*,src2*& + AVX_512.basic_instruction_bcst VEX_66_0F38_W0,EVEX_REQUIRED+EVEX_VL,opcode,4,dest,src,src2 + end macro + + end iterate + + iterate , vblendmpd,65h, vpblendmq,64h + + macro instr? dest*,src*,src2*& + AVX_512.basic_instruction_bcst VEX_66_0F38_W1,EVEX_REQUIRED+EVEX_VL,opcode,8,dest,src,src2 + end macro + + end iterate + + iterate , vrcp14ps,4,VEX_66_0F38_W0,4Ch, vrcp14pd,8,VEX_66_0F38_W1,4Ch, vrsqrt14ps,4,VEX_66_0F38_W0,4Eh, vrsqrt14pd,8,VEX_66_0F38_W1,4Eh + + macro instr? dest*,src*& + AVX_512.single_source_instruction_bcst vex_mpw,EVEX_REQUIRED+EVEX_VL,opcode,unit,dest,src + end macro + + end iterate + + iterate , vrcp14ss,4,VEX_66_0F38_W0,4Dh, vrcp14sd,8,VEX_66_0F38_W1,4Dh, vrsqrt14ss,4,VEX_66_0F38_W0,4Fh, vrsqrt14sd,8,VEX_66_0F38_W1,4Fh + + macro instr? dest*,src*& + AVX_512.basic_instruction vex_mpw,EVEX_REQUIRED,opcode,unit,dest,src + end macro + + end iterate + + iterate , vcompressps,VEX_66_0F_W0,8Ah, vcompresspd,VEX_66_0F_W1,8Ah, vpcompressd,VEX_66_0F38_W0,8Bh, vpcompressq,VEX_66_0F38_W1,8Bh + + macro instr? dest*,src* + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand @src,src + if (@dest.type = 'mmreg' | @dest.type = 'mem') & @src.type = 'mmreg' + if @dest.size and not @src.size + err 'operand sizes do not match' + end if + AVX_512.store_instruction @src.size,vex_mpw,EVEX_REQUIRED+EVEX_VL,opcode,@dest,@dest.mask,@src.rm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , vexpandps,VEX_66_0F38_W0,88h, vexpandpd,VEX_66_0F38_W1,88h, vpexpandd,VEX_66_0F38_W0,89h, vpexpandq,VEX_66_0F38_W1,89h + + macro instr? dest*,src* + AVX_512.single_source_instruction vex_mpw,EVEX_REQUIRED+EVEX_VL,opcode,0,dest,src + end macro + + end iterate + + iterate , fixupimm,54h + + macro v#instr#pd? dest*,src*,src2*,aux*& + AVX_512.basic_instruction_bcst_sae_imm8 VEX_66_0F3A_W1,EVEX_REQUIRED+EVEX_VL,opcode,8,dest,src,src2,aux + end macro + + macro v#instr#ps? dest*,src*,src2*,aux*& + AVX_512.basic_instruction_bcst_sae_imm8 VEX_66_0F3A_W0,EVEX_REQUIRED+EVEX_VL,opcode,4,dest,src,src2,aux + end macro + + macro v#instr#sd? dest*,src*,src2*,aux*& + AVX_512.basic_instruction_sae_imm8 VEX_66_0F3A_W1,EVEX_REQUIRED,opcode+1,8,dest,src,src2,aux + end macro + + macro v#instr#ss? dest*,src*,src2*,aux*& + AVX_512.basic_instruction_sae_imm8 VEX_66_0F3A_W0,EVEX_REQUIRED,opcode+1,4,dest,src,src2,aux + end macro + + end iterate + + iterate , getexp,42h + + macro v#instr#pd? dest*,src*& + AVX_512.single_source_instruction_bcst_sae VEX_66_0F38_W1,EVEX_REQUIRED+EVEX_VL,opcode,8,dest,src + end macro + + macro v#instr#ps? dest*,src*& + AVX_512.single_source_instruction_bcst_sae VEX_66_0F38_W0,EVEX_REQUIRED+EVEX_VL,opcode,4,dest,src + end macro + + macro v#instr#sd? dest*,src*,src2*& + AVX_512.basic_instruction_sae VEX_66_0F38_W1,EVEX_REQUIRED,opcode+1,8,dest,src,src2 + end macro + + macro v#instr#ss? dest*,src*,src2*& + AVX_512.basic_instruction_sae VEX_66_0F38_W0,EVEX_REQUIRED,opcode+1,4,dest,src,src2 + end macro + + end iterate + + iterate , getmant,26h,26h,27h,27h, rndscale,8,9,0Ah,0Bh + + macro v#instr#pd? dest*,src*,aux*& + AVX_512.single_source_instruction_bcst_sae_imm8 VEX_66_0F3A_W1,EVEX_REQUIRED+EVEX_VL,opcode_pd,8,dest,src,aux + end macro + + macro v#instr#ps? dest*,src*,aux*& + AVX_512.single_source_instruction_bcst_sae_imm8 VEX_66_0F3A_W0,EVEX_REQUIRED+EVEX_VL,opcode_ps,4,dest,src,aux + end macro + + macro v#instr#sd? dest*,src*,src2*,aux*& + AVX_512.basic_instruction_sae_imm8 VEX_66_0F3A_W1,EVEX_REQUIRED,opcode_sd,8,dest,src,src2,aux + end macro + + macro v#instr#ss? dest*,src*,src2*,aux*& + AVX_512.basic_instruction_sae_imm8 VEX_66_0F3A_W0,EVEX_REQUIRED,opcode_ss,4,dest,src,src2,aux + end macro + + end iterate + + iterate , vscalefpd,8,VEX_66_0F38_W1, vscalefps,4,VEX_66_0F38_W0 + + macro instr? dest*,src*,src2*& + AVX_512.basic_instruction_bcst_er vex_mpw,EVEX_REQUIRED+EVEX_VL,2Ch,unit,dest,src,src2 + end macro + + end iterate + + iterate , vscalefsd,8,VEX_66_0F38_W1, vscalefss,4,VEX_66_0F38_W0 + + macro instr? dest*,src*,src2*& + AVX_512.basic_instruction_er vex_mpw,EVEX_REQUIRED,2Dh,unit,dest,src,src2 + end macro + + end iterate + + iterate , vpmovusdb,4,11h, vpmovsdb,4,21h, vpmovdb,4,31h, \ + vpmovusqb,8,12h, vpmovsqb,8,22h, vpmovqb,8,32h, \ + vpmovusdw,2,13h, vpmovsdw,2,23h, vpmovdw,2,33h, \ + vpmovusqw,4,14h, vpmovsqw,4,24h, vpmovqw,4,34h, \ + vpmovusqd,2,15h, vpmovsqd,2,25h, vpmovqd,2,35h + + macro instr? dest*,src* + AVX_512.parse_operand_k1z @dest,dest + AVX_512.parse_operand @src,src + if (@dest.type = 'mmreg' | @dest.type = 'mem') & @src.type = 'mmreg' + @dest.memsize = @src.size / ratio + if (@dest.type = 'mmreg' & @dest.size <> (@dest.memsize-1) and not 15 + 16) | (@dest.type = 'mem' & @dest.size and not @dest.memsize) + err 'invalid operand size' + end if + AVX_512.store_instruction @src.size,VEX_F3_0F38_W0,EVEX_REQUIRED+EVEX_VL,opcode,@dest,@dest.mask,@src.rm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + calminstruction AVX_512.parse_vsib_operand namespace, operand + + local size, type, segment_prefix + local displacement, displacement_size, auto_relative + local address, base_registers, index_registers + local mode, mod, rm + local scale, index, base + local visize + + local i, pre, suf, sym + + compute segment_prefix, 0 + + compute size, 0 + compute displacement_size, 0 + + transform operand + + match pre suf, operand + jno no_size_prefix + transform pre, x86 + jno no_size_prefix + match :size, pre + jno no_size_prefix + arrange operand, suf + no_size_prefix: + + match [address], operand + jyes memory_operand + match =ptr? address, operand + jyes memory_operand + + jump invalid_operand + + memory_operand: + compute type, 'mem' + + match segment:address, address + jno segment_prefix_ok + check segment eq 1 elementof segment & 1 metadataof segment relativeto x86.sreg + jno invalid_operand + compute segment, 1 metadataof segment - x86.sreg + check segment >= 4 + jyes segment_prefix_386 + compute segment_prefix, 26h + segment shl 3 + jump segment_prefix_ok + segment_prefix_386: + compute segment_prefix, 64h + segment-4 + segment_prefix_ok: + + match pre suf, address + jno no_address_size_prefix + transform pre, x86 + jno no_address_size_prefix + match :pre, pre + jno no_address_size_prefix + arrange address, suf + check pre = 4 | pre = 8 + jno invalid_address_size + compute mode, pre shl 3 + no_address_size_prefix: + + compute mode, 0 + compute scale, 0 + compute index, 0 + compute base, 0 + compute auto_relative, 0 + + check size + jyes size_override + compute size, sizeof address + size_override: + + compute address, address + compute base_registers, 0 + compute index_registers, 0 + compute i, 1 + extract_registers: + check i > elementsof address + jyes registers_extracted + check i metadataof address relativeto SSE.reg | i metadataof address relativeto AVX.reg | 1 metadataof (i metadataof address) relativeto AVX_512.reg + jyes index_term + check i metadataof address relativeto x86.r32 | i metadataof address relativeto x86.r64 + jno next_term + compute base_registers, base_registers + i elementof address * i scaleof address + jump next_term + index_term: + compute index_registers, index_registers + i elementof address * i scaleof address + next_term: + compute i, i+1 + jump extract_registers + registers_extracted: + compute displacement, address - base_registers - index_registers + compute auto_relative, 0 + + check elementsof index_registers = 1 + jno invalid_address + compute scale, 1 scaleof index_registers + compute index, 0 scaleof (1 metadataof index_registers) + check scale > 2 & scale <> 4 & scale <> 8 + jyes invalid_address + check 1 metadataof index_registers relativeto SSE.reg + jyes xmm_index + check 1 metadataof index_registers relativeto AVX.reg + jyes ymm_index + compute visize, 1 metadataof (1 metadataof index_registers) - AVX_512.reg + jump index_ok + ymm_index: + compute visize, 32 + jump index_ok + xmm_index: + compute visize, 16 + index_ok: + + compute rm, 4 + check elementsof base_registers = 1 & 1 scaleof base_registers = 1 + jyes base_and_index + check elementsof base_registers = 0 + jno invalid_address + compute base, 5 + compute displacement_size, 4 + compute mod, 0 + compute mode, x86.mode + check mode > 16 + jyes export_address + compute mode, 32 + jump export_address + base_and_index: + compute base, 0 scaleof (1 metadataof base_registers) + check mode & mode <> 0 scaleof (1 metadataof (1 metadataof base_registers)) shl 3 + jyes invalid_address + compute mode, 0 scaleof (1 metadataof (1 metadataof base_registers)) shl 3 + + setup_displacement: + check displacement relativeto 0 + jno displacement_32bit + check displacement = 0 & rm and 111b <> 5 & (rm <> 4 | base and 111b <> 5) + jyes displacement_empty + check displacement < 80h & displacement >= -80h + jyes displacement_8bit + check displacement - 1 shl mode >= -80h & displacement < 1 shl mode + jyes displacement_8bit_wrap + displacement_32bit: + compute displacement_size, 4 + compute mod, 2 + jump export_address + displacement_8bit_wrap: + compute displacement, displacement - 1 shl mode + displacement_8bit: + compute displacement_size, 1 + compute mod, 1 + jump export_address + index_only: + compute displacement_size, 4 + compute mod, 0 + jump export_address + displacement_empty: + compute displacement_size, 0 + compute mod, 0 + + export_address: + + arrange sym, namespace.=address + publish sym, address + + arrange sym, namespace.=scale + publish sym, scale + + arrange sym, namespace.=index + publish sym, index + + arrange sym, namespace.=base + publish sym, base + + arrange sym, namespace.=auto_relative + publish sym, auto_relative + + arrange sym, namespace.=displacement + publish sym, displacement + + arrange sym, namespace.=mode + publish sym, mode + + arrange sym, namespace.=mod + publish sym, mod + + arrange sym, namespace.=rm + publish sym, rm + + arrange sym, namespace.=type + publish sym, type + + arrange sym, namespace.=size + publish sym, size + + arrange sym, namespace.=visize + publish sym, visize + + arrange sym, namespace.=displacement_size + publish sym, displacement_size + + arrange sym, namespace.=segment_prefix + publish sym, segment_prefix + + compute i, 0 + + arrange sym, namespace.=mask + publish sym, i + + arrange sym, namespace.=evex_b + publish sym, i + + arrange sym, namespace.=memsize + publish sym, i + + exit + + invalid_operand: + asmcmd =err 'invalid operand' + exit + invalid_address: + asmcmd =err 'invalid address' + exit + invalid_address_size: + asmcmd =err 'invalid address size' + exit + + end calminstruction + + calminstruction AVX_512.parse_vsib_operand_k1 namespace,operand + + local k1, mask, sym + transform operand + match operand {k1}, operand + jyes k1 + asmcmd =AVX_512.=parse_vsib_operand namespace, operand + exit + k1: + asmcmd =AVX_512.=parse_vsib_operand namespace, operand + arrange sym, namespace.=type + check k1 eq 1 elementof k1 & 1 metadataof k1 relativeto AVX_512.maskreg & 1 metadataof k1 - AVX_512.maskreg > 0 + jno invalid_mask + compute mask, 1 metadataof k1 - AVX_512.maskreg + arrange sym, namespace.=mask + publish sym, mask + exit + invalid_mask: + asmcmd =err 'invalid mask' + + end calminstruction + + iterate , vpgatherdd,90h,4, vpgatherqd,91h,8, vgatherdps,92h,4, vgatherqps,93h,8 + + macro instr? dest*,src*,aux + match , aux + AVX_512.parse_operand_k1 @dest,dest + AVX_512.parse_vsib_operand @src,src + if @dest.type = 'mmreg' & @dest.mask & @src.type = 'mem' + if @src.size and not 4 | (@dest.size > 16 & @dest.size * (asize shr 2) > @src.visize) | (@src.visize > 16 & @dest.size * (asize shr 2) < @src.visize) + err 'invalid operand size' + else if @dest.rm = @src.index + err 'disallowed combination of registers' + end if + @src.memsize = 4 + AVX_512.store_instruction @src.visize,VEX_66_0F38_W0,EVEX_REQUIRED+EVEX_VL,opcode,@src,@dest.mask,@dest.rm,@src.index and 10000b + else + err 'invalid combination of operands' + end if + else + AVX.parse_operand @dest,dest + AVX.parse_vsib_operand @src,src + AVX.parse_operand @aux,aux + if @dest.type = 'mmreg' & @src.type = 'mem' & @aux.type = 'mmreg' + if @src.size and not 4 | (@dest.size > 16 & @dest.size * (asize shr 2) > @src.visize) | (@src.visize > 16 & @dest.size * (asize shr 2) < @src.visize) + err 'invalid operand size' + else if @aux.size <> @dest.size + err 'operand sizes do not match' + else if @dest.rm = @aux.rm | @dest.rm = @src.index | @aux.rm = @src.index + err 'disallowed combination of registers' + end if + AVX.store_instruction @src.visize,VEX_66_0F38_W0,opcode,@src,@dest.rm,@aux.rm + else + err 'invalid combination of operands' + end if + end match + end macro + + end iterate + + iterate , vpgatherdq,90h,4, vpgatherqq,91h,8, vgatherdpd,92h,4, vgatherqpd,93h,8 + + macro instr? dest*,src*,aux + match , aux + AVX_512.parse_operand_k1 @dest,dest + AVX_512.parse_vsib_operand @src,src + if @dest.type = 'mmreg' & @dest.mask & @src.type = 'mem' + if @src.size and not 8 | (@dest.size > 16 & @dest.size * (asize shr 2) > @src.visize * 2) | (@src.visize > 16 & @dest.size * (asize shr 2) < @src.visize * 2) + err 'invalid operand size' + else if @dest.rm = @src.index + err 'disallowed combination of registers' + end if + @src.memsize = 8 + AVX_512.store_instruction @dest.size,VEX_66_0F38_W1,EVEX_REQUIRED+EVEX_VL,opcode,@src,@dest.mask,@dest.rm,@src.index and 10000b + else + err 'invalid combination of operands' + end if + else + AVX.parse_operand @dest,dest + AVX.parse_vsib_operand @src,src + AVX.parse_operand @aux,aux + if @dest.type = 'mmreg' & @src.type = 'mem' & @aux.type = 'mmreg' + if @src.size and not 8 | (@dest.size > 16 & @dest.size * (asize shr 2) > @src.visize * 2) | (@src.visize > 16 & @dest.size * (asize shr 2) < @src.visize * 2) + err 'invalid operand size' + else if @aux.size <> @dest.size + err 'operand sizes do not match' + else if @dest.rm = @aux.rm | @dest.rm = @src.index | @aux.rm = @src.index + err 'disallowed combination of registers' + end if + AVX.store_instruction @dest.size,VEX_66_0F38_W1,opcode,@src,@dest.rm,@aux.rm + else + err 'invalid combination of operands' + end if + end match + end macro + + end iterate + + iterate , vpscatterdd,0A0h,4, vpscatterqd,0A1h,8, vscatterdps,0A2h,4, vscatterqps,0A3h,8 + + macro instr? dest*,src* + AVX_512.parse_vsib_operand_k1 @dest,dest + AVX_512.parse_operand @src,src + if @dest.type = 'mem' & @dest.mask & @src.type = 'mmreg' + if @dest.size and not 4 | (@src.size > 16 & @src.size * (asize shr 2) > @dest.visize) | (@dest.visize > 16 & @src.size * (asize shr 2) < @dest.visize) + err 'invalid operand size' + else if @src.rm = @dest.index + err 'disallowed combination of registers' + end if + @dest.memsize = 4 + AVX_512.store_instruction @dest.visize,VEX_66_0F38_W0,EVEX_REQUIRED+EVEX_VL,opcode,@dest,@dest.mask,@src.rm,@dest.index and 10000b + else + err 'invalid combination of operands' + end if + end macro + + end iterate + + iterate , vpscatterdq,0A0h,4, vpscatterqq,0A1h,8, vscatterdpd,0A2h,4, vscatterqpd,0A3h,8 + + macro instr? dest*,src* + AVX_512.parse_vsib_operand_k1 @dest,dest + AVX_512.parse_operand @src,src + if @dest.type = 'mem' & @dest.mask & @src.type = 'mmreg' + if @dest.size and not 8 | (@src.size > 16 & @src.size * (asize shr 2) > @dest.visize * 2) | (@dest.visize > 16 & @src.size * (asize shr 2) < @dest.visize * 2) + err 'invalid operand size' + else if @src.rm = @dest.index + err 'disallowed combination of registers' + end if + @dest.memsize = 8 + AVX_512.store_instruction @src.size,VEX_66_0F38_W1,EVEX_REQUIRED+EVEX_VL,opcode,@dest,@dest.mask,@src.rm,@dest.index and 10000b + else + err 'invalid combination of operands' + end if + end macro + + end iterate + +end if diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512pf.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512pf.inc new file mode 100644 index 0000000..fd28cac --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512pf.inc @@ -0,0 +1,62 @@ + +if ~ defined AVX_512 + + include 'avx512f.inc' + +end if + +iterate , gatherpf0,1 ,gatherpf1,2 ,scatterpf0,5, scatterpf1,6 + + macro v#instr#dps? src* + AVX_512.parse_vsib_operand_k1 @src,src + if @src.type = 'mem' & @src.mask + if @src.size and not 4 | @src.visize <> 64 + err 'invalid operand size' + end if + @src.memsize = 4 + AVX_512.store_instruction 64,VEX_66_0F38_W0,EVEX_REQUIRED,0C6h,@src,@src.mask,postbyte,@src.index and 10000b + else + err 'invalid combination of operands' + end if + end macro + + macro v#instr#qps? src* + AVX_512.parse_vsib_operand_k1 @src,src + if @src.type = 'mem' & @src.mask + if @src.size and not 8 | @src.visize <> 64 + err 'invalid operand size' + end if + @src.memsize = 4 + AVX_512.store_instruction 64,VEX_66_0F38_W0,EVEX_REQUIRED,0C7h,@src,@src.mask,postbyte,@src.index and 10000b + else + err 'invalid combination of operands' + end if + end macro + + macro v#instr#dpd? src* + AVX_512.parse_vsib_operand_k1 @src,src + if @src.type = 'mem' & @src.mask + if @src.size and not 4 | @src.visize <> 32 + err 'invalid operand size' + end if + @src.memsize = 8 + AVX_512.store_instruction 64,VEX_66_0F38_W1,EVEX_REQUIRED,0C6h,@src,@src.mask,postbyte,@src.index and 10000b + else + err 'invalid combination of operands' + end if + end macro + + macro v#instr#qpd? src* + AVX_512.parse_vsib_operand_k1 @src,src + if @src.type = 'mem' & @src.mask + if @src.size and not 8 | @src.visize <> 64 + err 'invalid operand size' + end if + @src.memsize = 8 + AVX_512.store_instruction 64,VEX_66_0F38_W1,EVEX_REQUIRED,0C7h,@src,@src.mask,postbyte,@src.index and 10000b + else + err 'invalid combination of operands' + end if + end macro + +end iterate diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512vl.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512vl.inc new file mode 100644 index 0000000..50987e6 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/avx512vl.inc @@ -0,0 +1,8 @@ + +if ~ defined AVX_512 + + include 'avx512f.inc' + +end if + +AVX512VL = 1 diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/bmi1.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/bmi1.inc new file mode 100644 index 0000000..c8d5edd --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/bmi1.inc @@ -0,0 +1,97 @@ + +include 'avx.inc' + +macro andn? dest*,src*,src2* + x86.parse_operand @dest,dest + x86.parse_operand @src,src + x86.parse_operand @src2,src2 + if @dest.type = 'reg' & @src.type = 'reg' & (@src2.type = 'mem' | @src2.type = 'reg') + if @dest.size < 4 + err 'invalid operand size' + else if @src.size <> @dest.size | @src2.size and not @dest.size + err 'operand sizes do not match' + end if + if @dest.size = 8 + if x86.mode < 64 + err 'instruction requires long mode' + end if + AVX.store_instruction 16,VEX_0F38_W1,0F2h,@src2,@dest.rm,@src.rm + else + AVX.store_instruction 16,VEX_0F38_W0,0F2h,@src2,@dest.rm,@src.rm + end if + else + err 'invalid combination of operands' + end if +end macro + +macro bextr? dest*,src*,src2* + x86.parse_operand @dest,dest + x86.parse_operand @src,src + x86.parse_operand @src2,src2 + if @dest.type = 'reg' & (@src.type = 'mem' | @src.type = 'reg') & @src2.type = 'reg' + if @dest.size < 4 + err 'invalid operand size' + else if @src.size and not @dest.size | @src2.size <> @dest.size + err 'operand sizes do not match' + end if + if @dest.size = 8 + if x86.mode < 64 + err 'instruction requires long mode' + end if + AVX.store_instruction 16,VEX_0F38_W1,0F7h,@src,@dest.rm,@src2.rm + else + AVX.store_instruction 16,VEX_0F38_W0,0F7h,@src,@dest.rm,@src2.rm + end if + else + err 'invalid combination of operands' + end if +end macro + +iterate , blsi,0F3h,3, blmsk,0F3h,2, blsr,0F3h,1 + + macro instr? dest*,src* + x86.parse_operand @dest,dest + x86.parse_operand @src,src + if @dest.type = 'reg' & (@src.type = 'mem' | @src.type = 'reg') + if @dest.size < 4 + err 'invalid operand size' + else if @src.size and not @dest.size + err 'operand sizes do not match' + end if + if @dest.size = 8 + if x86.mode < 64 + err 'instruction requires long mode' + end if + AVX.store_instruction 16,VEX_0F38_W1,opcode,@src,postbyte,@dest.rm + else + AVX.store_instruction 16,VEX_0F38_W0,opcode,@src,postbyte,@dest.rm + end if + else + err 'invalid combination of operands' + end if + end macro + +end iterate + +iterate , lzcnt,0BDh, tzcnt,0BCh + + macro instr? dest*,src* + x86.parse_operand @dest,dest + x86.parse_operand @src,src + if @dest.type = 'reg' & ( @src.type = 'reg' | @src.type = 'mem' ) + if @src.size and not @dest.size + err 'operand sizes do not match' + end if + @src.opcode_prefix = 0F3h + if @dest.size > 1 + x86.select_operand_prefix @src,@dest.size + x86.store_instruction <0Fh,opcode>,@src,@dest.rm + else + err 'invalid operand size' + end if + else + err 'invalid combination of operands' + end if + end macro + +end iterate diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/bmi2.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/bmi2.inc new file mode 100644 index 0000000..cb5930f --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/bmi2.inc @@ -0,0 +1,106 @@ + +include 'bmi1.inc' + +iterate , bzhi,0F5h + + macro instr? dest*,src*,src2* + x86.parse_operand @dest,dest + x86.parse_operand @src,src + x86.parse_operand @src2,src2 + if @dest.type = 'reg' & (@src.type = 'mem' | @src.type = 'reg') & @src2.type = 'reg' + if @dest.size < 4 + err 'invalid operand size' + else if @src.size and not @dest.size | @src2.size <> @dest.size + err 'operand sizes do not match' + end if + if @dest.size = 8 + if x86.mode < 64 + err 'instruction requires long mode' + end if + AVX.store_instruction 16,VEX_0F38_W1,opcode,@src,@dest.rm,@src2.rm + else + AVX.store_instruction 16,VEX_0F38_W0,opcode,@src,@dest.rm,@src2.rm + end if + else + err 'invalid combination of operands' + end if + end macro + +end iterate + +iterate , mulx,VEX_F2_0F38,0F6h, pdep,VEX_F2_0F38,0F5h, pext,VEX_F3_0F38,0F5h + + macro instr? dest*,src*,src2* + x86.parse_operand @dest,dest + x86.parse_operand @src,src + x86.parse_operand @src2,src2 + if @dest.type = 'reg' & @src.type = 'reg' & (@src2.type = 'mem' | @src2.type = 'reg') + if @dest.size < 4 + err 'invalid operand size' + else if @src.size <> @dest.size | @src2.size and not @dest.size + err 'operand sizes do not match' + end if + if @dest.size = 8 + if x86.mode < 64 + err 'instruction requires long mode' + end if + AVX.store_instruction 16,vex_mp#_W1,opcode,@src2,@dest.rm,@src.rm + else + AVX.store_instruction 16,vex_mp#_W0,opcode,@src2,@dest.rm,@src.rm + end if + else + err 'invalid combination of operands' + end if + end macro + +end iterate + +macro rorx? dest*,src*,src2* + x86.parse_operand @dest,dest + x86.parse_operand @src,src + x86.parse_operand @src2,src2 + if @dest.type = 'reg' & (@src.type = 'mem' | @src.type = 'reg') & @src2.type = 'imm' + if @dest.size < 4 | @src2.size and not 1 + err 'invalid operand size' + else if @src.size and not @dest.size + err 'operand sizes do not match' + end if + if @dest.size = 8 + if x86.mode < 64 + err 'instruction requires long mode' + end if + AVX.store_instruction 16,VEX_F2_0F3A_W1,0F0h,@src,@dest.rm,,1,@src2.imm + else + AVX.store_instruction 16,VEX_F2_0F3A_W0,0F0h,@src,@dest.rm,,1,@src2.imm + end if + else + err 'invalid combination of operands' + end if +end macro + +iterate , sarx,VEX_F3_0F38,0F7h, shlx,VEX_66_0F38,0F7h, shrx,VEX_F2_0F38,0F7h + + macro instr? dest*,src*,src2* + x86.parse_operand @dest,dest + x86.parse_operand @src,src + x86.parse_operand @src2,src2 + if @dest.type = 'reg' & (@src.type = 'mem' | @src.type = 'reg') & @src2.type = 'reg' + if @dest.size < 4 + err 'invalid operand size' + else if @src.size and not @dest.size | @src2.size <> @dest.size + err 'operand sizes do not match' + end if + if @dest.size = 8 + if x86.mode < 64 + err 'instruction requires long mode' + end if + AVX.store_instruction 16,vex_mp#_W1,opcode,@src,@dest.rm,@src2.rm + else + AVX.store_instruction 16,vex_mp#_W0,opcode,@src,@dest.rm,@src2.rm + end if + else + err 'invalid combination of operands' + end if + end macro + +end iterate diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/cet_ibt.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/cet_ibt.inc new file mode 100644 index 0000000..09acfb1 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/cet_ibt.inc @@ -0,0 +1,8 @@ + +iterate , endbr32,0FBh, endbr64,0FAh + + macro instr? + db 0F3h,0Fh,1Eh,modrm + end macro + +end iterate \ No newline at end of file diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/cet_ss.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/cet_ss.inc new file mode 100644 index 0000000..8352dd9 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/cet_ss.inc @@ -0,0 +1,106 @@ + +iterate , clrssbsy,0AEh,6, rstorssp,01h,5 + + macro instr? src* + x86.parse_operand @src,src + if @src.type = 'mem' + if @src.size and not 8 + err 'invalid operand size' + else + @src.opcode_prefix = 0F3h + x86.store_instruction <0Fh,ext>,@src,postbyte + end if + else + err 'invalid operand' + end if + end macro + +end iterate + +iterate , incsspd,0AEh,5, rdsspd,1Eh,1 + + macro instr? src* + x86.parse_operand @src,src + if @src.type = 'reg' + if @src.size <> 4 + err 'invalid operand size' + else + @src.opcode_prefix = 0F3h + x86.store_instruction <0Fh,ext>,@src,postbyte + end if + else + err 'invalid operand' + end if + end macro + +end iterate + +iterate , incsspq,0AEh,5, rdsspq,1Eh,1 + + macro instr? src* + x86.parse_operand @src,src + if @src.type = 'reg' + if @src.size <> 8 + err 'invalid operand size' + else + x86.select_operand_prefix @src,8 + @src.opcode_prefix = 0F3h + x86.store_instruction <0Fh,ext>,@src,postbyte + end if + else + err 'invalid operand' + end if + end macro + +end iterate + +iterate , saveprevssp,0EAh, setssbsy,0E8h + + macro instr? + db 0F3h,0Fh,01h,modrm + end macro + +end iterate + +iterate , wrssd,0,0F6h, wrussd,66h,0F5h + + macro instr? dest*,src* + x86.parse_operand @dest,dest + x86.parse_operand @src,src + local size + if @src.size <> 4 + err 'invalid operand size' + else if @dest.size and not @src.size + err 'operand sizes do not match' + end if + if @src.type = 'reg' & ( @dest.type = 'reg' | @dest.type = 'mem' ) + @dest.opcode_prefix = prefix + x86.store_instruction <0Fh,38h,ext>,@dest,@src.rm + else + err 'invalid combination of operands' + end if + end macro + +end iterate + +iterate , wrssq,0,0F6h, wrussq,66h,0F5h + + macro instr? dest*,src* + x86.parse_operand @dest,dest + x86.parse_operand @src,src + local size + if @src.size <> 8 + err 'invalid operand size' + else if @dest.size and not @src.size + err 'operand sizes do not match' + end if + if @src.type = 'reg' & ( @dest.type = 'reg' | @dest.type = 'mem' ) + x86.select_operand_prefix @dest,8 + @dest.opcode_prefix = prefix + x86.store_instruction <0Fh,38h,ext>,@dest,@src.rm + else + err 'invalid combination of operands' + end if + end macro + +end iterate diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/f16c.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/f16c.inc new file mode 100644 index 0000000..ef708d5 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/f16c.inc @@ -0,0 +1,29 @@ + +include 'avx.inc' + +macro vcvtph2ps? dest*,src* + AVX.parse_operand @dest,dest + AVX.parse_operand @src,src + if @dest.type = 'mmreg' & (@src.type = 'mmreg' | @src.type = 'mem') + if (@src.type = 'mmreg' & @src.size <> 16) | (@src.type = 'mem' & @src.size*2 <> @dest.size) + err 'invalid operand size' + end if + AVX.store_instruction @dest.size,VEX_66_0F38_W0,13h,@src,@dest.rm + else + err 'invalid combination of operands' + end if +end macro + +macro vcvtps2ph? dest*,src*,round* + AVX.parse_operand @dest,dest + AVX.parse_operand @src,src + x86.parse_operand @aux,round + if (@dest.type = 'mmreg' | @dest.type = 'mem') & @src.type = 'mmreg' & @aux.type = 'imm' + if (@dest.type = 'mmreg' & @dest.size <> 16) | (@dest.type = 'mem' & @dest.size*2 <> @src.size) | @aux.size and not 1 + err 'invalid operand size' + end if + AVX.store_instruction @src.size,VEX_66_0F3A_W0,1Dh,@dest,@src.rm,,1,@aux.imm + else + err 'invalid combination of operands' + end if +end macro diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/fma.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/fma.inc new file mode 100644 index 0000000..6632031 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/fma.inc @@ -0,0 +1,30 @@ + +include 'avx.inc' + +iterate , vfmaddsub,6, vfmsubadd,7, vfmadd,8, vfmsub,0Ah, vfnmadd,0Ch, vfnmsub,0Eh + + iterate , 132,90h, 213,0A0h, 231,0B0h + + macro instr#order#pd? dest*,src*,src2* + AVX.basic_instruction VEX_66_0F38_W1,hcode+lcode,0,dest,src,src2 + end macro + + macro instr#order#ps? dest*,src*,src2* + AVX.basic_instruction VEX_66_0F38_W0,hcode+lcode,0,dest,src,src2 + end macro + + if lcode > 7 + + macro instr#order#sd? dest*,src*,src2* + AVX.basic_instruction VEX_66_0F38_W1,hcode+lcode+1,8,dest,src,src2 + end macro + + macro instr#order#ss? dest*,src*,src2* + AVX.basic_instruction VEX_66_0F38_W0,hcode+lcode+1,4,dest,src,src2 + end macro + + end if + + end iterate + +end iterate diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/fsgsbase.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/fsgsbase.inc new file mode 100644 index 0000000..56489dc --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/fsgsbase.inc @@ -0,0 +1,24 @@ + +iterate , rdfsbase,0, rdgsbase,1, wrfsbase,2, wrgsbase,3 + + macro instr? dest* + if x86.mode = 64 + x86.parse_operand @dest,dest + if @dest.type = 'reg' + if @dest.size >= 4 + @dest.opcode_prefix = 0F3h + x86.select_operand_prefix @dest,@dest.size + x86.store_instruction <0Fh,0AEh>,@dest,postbyte + else + err 'invalid operand size' + end if + else + err 'invalid operand' + end if + else + err 'instruction requires long mode' + end if + end macro + +end iterate + diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/gfni.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/gfni.inc new file mode 100644 index 0000000..7382615 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/gfni.inc @@ -0,0 +1,53 @@ + +include 'sse.inc' + +iterate , gf2p8mulb,0CFh + macro instr? dest*,src* + SSE.basic_instruction 66h,<38h,supp>,16,dest,src + end macro +end iterate + +iterate , gf2p8affineinvqb,0CFh, gf2p8affineqb,0CEh + macro instr? dest*,src*,imm* + SSE.basic_instruction_imm8 66h,<3Ah,supp>,16,dest,src,imm + end macro +end iterate + +if defined AVX_512 + + iterate , gf2p8mulb,0CFh + + macro v#instr? dest*,src*,src2*& + AVX_512.basic_instruction VEX_66_0F38_W0,EVEX_AS_VEX+EVEX_VL,opcode,0,dest,src,src2 + end macro + + end iterate + + iterate , gf2p8affineinvqb,0CFh, gf2p8affineqb,0CEh + + macro v#instr? dest*,src*,src2*,imm*& + AVX_512.basic_instruction_bcst_imm8 VEX_66_0F3A_W1,EVEX_W1+EVEX_VL,opcode,8,dest,src,src2,imm + end macro + + end iterate + +else if defined AVX + + iterate , gf2p8mulb,0CFh + + macro v#instr? dest*,src*,src2* + AVX.basic_instruction VEX_66_0F38_W0,opcode,0,dest,src,src2 + end macro + + end iterate + + iterate , gf2p8affineinvqb,0CFh, gf2p8affineqb,0CEh + + macro v#instr? dest*,src*,src2*,imm* + AVX.basic_instruction_imm8 VEX_66_0F3A_W1,opcode,0,dest,src,src2,imm + end macro + + end iterate + +end if + diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/hle.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/hle.inc new file mode 100644 index 0000000..cff03a0 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/hle.inc @@ -0,0 +1,16 @@ + +macro xacquire? instr& + db 0F2h + instr +end macro + +macro xrelease? instr& + db 0F3h + instr +end macro + +macro xtest? + db 0Fh,1,0D6h +end macro + + diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/invpcid.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/invpcid.inc new file mode 100644 index 0000000..cdc5864 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/invpcid.inc @@ -0,0 +1,15 @@ + +macro invpcid? dest*,src* + x86.parse_operand @dest,dest + x86.parse_operand @src,src + if @dest.type = 'reg' & @src.type = 'mem' + if (x86.mode < 64 & @dest.size <> 4) | (x86.mode = 64 & @dest.size <> 8) | @src.size and not 16 + err 'invalid operand size' + end if + @src.opcode_prefix = 66h + x86.store_instruction <0Fh,38h,82h>,@src,@dest.rm + else + err 'invalid combination of operands' + end if +end macro + diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/mmx.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/mmx.inc new file mode 100644 index 0000000..296dc3e --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/mmx.inc @@ -0,0 +1,160 @@ + +if ~ defined MMX + + restore MMX ; this ensures that symbol cannot be forward-referenced + MMX = 1 + + element MMX.reg + + repeat 8, i:0 + element mm#i? : MMX.reg + i + end repeat + + calminstruction MMX.parse_operand namespace, operand + + local size, type, mod, rm, imm + local i, sym, cmd + + asmcmd =x86.=parse_operand namespace, operand + + arrange type, namespace.=type + arrange size, namespace.=size + arrange imm, namespace.=imm + + check type = 'imm' & size = 0 + jno done + check imm eq 1 elementof imm & 1 metadataof imm relativeto MMX.reg + jno done + + compute type, 'mmreg' + compute mod, 11b + compute rm, 1 metadataof imm - MMX.reg + compute size, 8 + + arrange sym, namespace.=mod + publish sym, mod + + arrange sym, namespace.=rm + publish sym, rm + + arrange sym, namespace.=type + publish sym, type + + arrange sym, namespace.=size + publish sym, size + + done: + + end calminstruction + + calminstruction MMX.basic_instruction ext,dest,src + asmcmd =MMX.=parse_operand =@dest,dest + asmcmd =MMX.=parse_operand =@src,src + check (@src.size or @dest.size) and not 8 + jno size_ok + asmcmd =err 'invalid operand size' + size_ok: + check @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') + jno invalid_combination_of_operands + asmcmd =x86.=store_instruction <0Fh,ext>,=@src,=@dest.=rm + exit + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' + end calminstruction + + iterate , punpcklbw,60h, punpcklwd,61h, punpckldq,62h, packsswb,63h, pcmpgtb,64h, pcmpgtw,65h, pcmpgtd,66h, packuswb,67h, punpckhbw,68h, \ + punpckhwd,69h, punpckhdq,6Ah, packssdw,6Bh, pcmpeqb,74h, pcmpeqw,75h, pcmpeqd,76h, pmullw,0D5h, psubusb,0D8h, psubusw,0D9h, \ + pand,0DBh, paddusb,0DCh, paddusw,0DDh, pandn,0DFh, pmulhw,0E5h, psubsb,0E8h, psubsw,0E9h, por,0EBh, paddsb,0ECh, paddsw,0EDh, \ + pxor,0EFh, pmaddwd,0F5h, psubb,0F8h, psubw,0F9h, psubd,0FAh, paddb,0FCh, paddw,0FDh, paddd,0FEh + + macro instr? dest*,src* + MMX.basic_instruction opcode,dest,src + end macro + + end iterate + + calminstruction movq? dest*,src* + asmcmd =MMX.=parse_operand =@dest,dest + asmcmd =MMX.=parse_operand =@src,src + check (@src.size or @dest.size) and not 8 + jno size_ok + asmcmd =err 'invalid operand size' + size_ok: + check @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') + jyes mmreg_mem + check @dest.type = 'mem' & @src.type = 'mmreg' + jyes mem_mmreg + asmcmd =err 'invalid combination of operands' + exit + mmreg_mem: + asmcmd =x86.=store_instruction <0Fh,6Fh>,=@src,=@dest.=rm + exit + mem_mmreg: + asmcmd =x86.=store_instruction <0Fh,7Fh>,=@dest,=@src.=rm + exit + end calminstruction + + calminstruction movd? dest*,src* + asmcmd =MMX.=parse_operand =@dest,dest + asmcmd =MMX.=parse_operand =@src,src + check @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'reg') + jyes mmreg_rm + check (@dest.type = 'mem' | @dest.type = 'reg') & @src.type = 'mmreg' + jyes rm_mmreg + asmcmd =err 'invalid combination of operands' + exit + mmreg_rm: + check @src.size and not 4 + jno mmreg_rm_ok + asmcmd =err 'invalid operand size' + mmreg_rm_ok: + asmcmd =x86.=store_instruction <0Fh,6Eh>,=@src,=@dest.=rm + exit + rm_mmreg: + check @dest.size and not 4 + jno rm_mmreg_ok + asmcmd =err 'invalid operand size' + rm_mmreg_ok: + asmcmd =x86.=store_instruction <0Fh,7Eh>,=@dest,=@src.=rm + end calminstruction + + calminstruction MMX.bit_shift_instruction ext,dest,src + asmcmd =MMX.=parse_operand =@dest,dest + asmcmd =MMX.=parse_operand =@src,src + check @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') + jyes mmreg_rm + check @dest.type = 'mmreg' & @src.type = 'imm' + jyes mmreg_imm + asmcmd =err 'invalid combination of operands' + exit + mmreg_rm: + check @src.size and not 8 + jno mmreg_rm_ok + asmcmd =err 'invalid operand size' + mmreg_rm_ok: + asmcmd =x86.=store_instruction <0Fh,ext>,=@src,=@dest.=rm + exit + mmreg_imm: + check @src.size and not 1 + jno rm_mmreg_ok + asmcmd =err 'invalid operand size' + rm_mmreg_ok: + local iext, irm + compute iext, 70h+(ext and 0Fh) + compute irm, ((ext shr 4)-0Ch) shl 1 + asmcmd =x86.=store_instruction <0Fh,iext>,=@dest,irm,1,=@src.=imm + end calminstruction + + iterate , psrlw,0D1h, psrld,0D2h, psrlq,0D3h, psrad,0E2h, psraw,0E1h, psllw,0F1h, pslld,0F2h, psllq,0F3h + + macro instr? dest*,src* + MMX.bit_shift_instruction opcode,dest,src + end macro + + end iterate + + macro emms? + db 0Fh,77h + end macro + +end if diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/movbe.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/movbe.inc new file mode 100644 index 0000000..02d4548 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/movbe.inc @@ -0,0 +1,44 @@ +; CPUID Fn0000_0001_ECX[MOVBE] = 1 +calminstruction movbe? dest*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + + local size + + check @dest.size <> 0 & @src.size <> 0 & @dest.size <> @src.size + jyes operand_sizes_do_not_match + + compute size, @dest.size or @src.size + check size > 1 + jno invalid_operand_size + + main: + check @dest.type = 'reg' & @src.type = 'mem' + jyes movbe_reg_mem + check @dest.type = 'mem' & @src.type = 'reg' + jyes movbe_mem_reg + + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' + exit + + movbe_reg_mem: + asmcmd =x86.=select_operand_prefix =@src,size + asmcmd =x86.=store_instruction <0Fh,38h,0F0h>,=@src,=@dest.=rm + exit + + movbe_mem_reg: + asmcmd =x86.=select_operand_prefix =@dest,size + asmcmd =x86.=store_instruction <0Fh,38h,0F1h>,=@dest,=@src.=rm + exit + + invalid_operand_size: + asmcmd err 'invalid operand size' + compute size, 0 + jump main + + operand_sizes_do_not_match: + asmcmd err 'operand sizes do not match' + compute size, 0 + jump main +end calminstruction diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/movdir64b.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/movdir64b.inc new file mode 100644 index 0000000..431f3ef --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/movdir64b.inc @@ -0,0 +1,23 @@ + +if ~ defined AVX512 + define x86.dqqword? :64 + define x86.zword? :64 +end if + +macro movdir64b? dest*,src* + x86.parse_operand @dest,dest + x86.parse_operand @src,src + if @dest.type = 'reg' & @src.type = 'mem' + if @src.size and not 64 + err 'invalid operand size' + end if + if (@src.mode = 16 & @dest.size <> 2) | (@src.mode = 32 & @dest.size <> 4) | (@src.mode = 64 & @dest.size <> 8) + err 'invalid operand size' + end if + @src.opcode_prefix = 66h + x86.store_instruction <0Fh,38h,0F8h>,@src,@dest.rm + else + err 'invalid combination of operands' + end if +end macro + diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/movdiri.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/movdiri.inc new file mode 100644 index 0000000..39f1ff3 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/movdiri.inc @@ -0,0 +1,19 @@ + +macro movdiri? dest*,src* + x86.parse_operand @dest,dest + x86.parse_operand @src,src + if @src.type = 'reg' & @dest.type = 'mem' + if @dest.size <> 0 & @src.size <> @dest.size + err 'operand sizes do not match' + end if + if @src.size = 8 & x86.mode = 64 + @dest.prefix = 48h + else if @src.size <> 4 + err 'invalid operand size' + end if + x86.store_instruction <0Fh,38h,0F9h>,@dest,@src.rm + else + err 'invalid combination of operands' + end if +end macro + diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/mpx.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/mpx.inc new file mode 100644 index 0000000..debc0ac --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/mpx.inc @@ -0,0 +1,196 @@ + +if ~ defined MPX + + restore MPX ; this ensures that symbol cannot be forward-referenced + MPX = 1 + + element MPX.bnd + + repeat 4, i:0 + element bnd#i? : MPX.bnd + i + end repeat + + macro MPX.parse_operand ns,op + x86.parse_operand ns,op + if ns.type = 'imm' & ns.size = 0 & ns.imm eq 1 elementof ns.imm & 1 metadataof ns.imm relativeto MPX.bnd + ns.type = 'bnd' + ns.mod = 11b + ns.rm = 1 metadataof ns.imm - MPX.bnd + ns.size = 0 + end if + end macro + + macro MPX.parse_sib_operand ns,op& + match [b=,si], op + ns.split = 1 + ns.segment_prefix = 0 + ns.prefix = 0 + ns.opcode_prefix = 0 + ns.rex_prefix = 0 + ns.size = 0 + ns.type = 'mem' + ns.base_part = +b + ns.index_part = +si + if x86.mode = 64 + ns.mode = 64 + ns.address_registers_type = x86.r64 + else + ns.mode = 32 + ns.address_registers_type = x86.r32 + end if + ns.base_registers = 0 + ns.index_registers = 0 + repeat elementsof ns.base_part + if % metadataof ns.base_part relativeto x86.r16 | % metadataof ns.base_part relativeto x86.r32 | % metadataof ns.base_part relativeto x86.r64 | % metadataof ns.address relativeto x86.ip + ns.base_registers = ns.base_registers + % elementof ns.base_part * % scaleof ns.base_part + end if + end repeat + repeat elementsof ns.index_part + if % metadataof ns.index_part relativeto x86.r16 | % metadataof ns.index_part relativeto x86.r32 | % metadataof ns.index_part relativeto x86.r64 | % metadataof ns.address relativeto x86.ip + ns.index_registers = ns.index_registers + % elementof ns.index_part * % scaleof ns.index_part + end if + end repeat + ns.displacement = ns.base_part - ns.base_registers + ns.index_part - ns.index_registers + if ns.index_registers eq 0 + ns.index = 4 + ns.scale = 1 + else if elementsof ns.index_registers = 1 & 1 metadataof ns.index_registers relativeto ns.address_registers_type & 1 metadataof ns.index_registers - ns.address_registers_type <> 4 + ns.index = 1 metadataof ns.index_registers - ns.address_registers_type + ns.scale = 1 scaleof ns.index_registers + else + err 'invalid address' + end if + if ns.base_registers eq 0 + ns.rm = 4 + ns.base = 5 + ns.index_only = 1 + else if ns.base_registers eq 1 elementof ns.base_registers & 1 metadataof ns.base_registers relativeto ns.address_registers_type + ns.base = 1 metadataof ns.base_registers - ns.address_registers_type + if ns.index = 4 & ns.base <> 4 + ns.rm = ns.base + else + ns.rm = 4 + end if + ns.index_only = 0 + else + err 'invalid address' + end if + ns.auto_relative = 0 + ns.displacement_size = 4 + ns.mod = 2 + if ns.index_only + ns.mod = 0 + else if ns.displacement relativeto 0 + if ns.displacement = 0 & ns.rm and 111b <> 5 & (ns.rm <> 4 | ns.base and 111b <> 5) + ns.displacement_size = 0 + ns.mod = 0 + else if ns.displacement < 80h & ns.displacement >= -80h + ns.displacement_size = 1 + ns.mod = 1 + else if ns.displacement - 1 shl ns.mode >= -80h & ns.displacement < 1 shl ns.mode + ns.displacement = ns.displacement - 1 shl ns.mode + ns.displacement_size = 1 + ns.mod = 1 + end if + end if + else + ns.split = 0 + x86.parse_operand ns,op + end match + end macro + + macro bndmk? dest*,src*& + MPX.parse_operand @dest,dest + MPX.parse_sib_operand @src,src + if @dest.type = 'bnd' & @src.type = 'mem' + if @src.split & ~ 0 scaleof @src.base_part eq 0 + err 'invalid base address' + end if + if (x86.mode = 64 & @src.size and not 8) | (x86.mode < 64 & @src.size and not 4) + err 'invalid operand size' + end if + @src.opcode_prefix = 0F3h + x86.store_instruction <0Fh,1Bh>,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + macro bndmov? dest*,src* + MPX.parse_operand @dest,dest + MPX.parse_operand @src,src + if @dest.type = 'bnd' & (@src.type = 'bnd' | @src.type = 'mem') + if (x86.mode = 64 & @src.size and not 16) | (x86.mode < 64 & @src.size and not 8) + err 'invalid operand size' + end if + if @src.type = 'mem' & @src.mode <> x86.mode + err 'invalid address' + end if + @src.opcode_prefix = 66h + x86.store_instruction <0Fh,1Ah>,@src,@dest.rm + else if @dest.type = 'mem' & @src.type = 'bnd' + if (x86.mode = 64 & @dest.size and not 16) | (x86.mode < 64 & @dest.size and not 8) + err 'invalid operand size' + end if + if @dest.type = 'mem' & @dest.mode <> x86.mode + err 'invalid address' + end if + @dest.opcode_prefix = 66h + x86.store_instruction <0Fh,1Bh>,@dest,@src.rm + else + err 'invalid combination of operands' + end if + end macro + + iterate , bndcl,0F3h,1Ah, bndcu,0F2h,1Ah, bndcn,0F2h,1Bh + + macro instr? dest*,src* + MPX.parse_operand @dest,dest + x86.parse_operand @src,src + if @dest.type = 'bnd' & (@src.type = 'reg' | @src.type = 'mem') + if (x86.mode = 64 & @src.size and not 8) | (x86.mode < 64 & @src.size and not 4) + err 'invalid operand size' + end if + if @src.type = 'mem' & @src.mode <> x86.mode + err 'invalid address' + end if + @src.opcode_prefix = prefix + x86.store_instruction <0Fh,ext>,@src,@dest.rm + else + err 'invalid operand' + end if + end macro + + end iterate + + macro bndldx? dest*,src*& + MPX.parse_operand @dest,dest + MPX.parse_sib_operand @src,src + if @dest.type = 'bnd' & @src.type = 'mem' + if @src.scale > 1 | ( @src.split & ~ 0 scaleof @src.index_part eq 0 ) + err 'invalid index' + end if + x86.store_instruction <0Fh,1Ah>,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + macro bndstx? operands*& + match [dest] =, src, operands + MPX.parse_sib_operand @dest,[dest] + MPX.parse_operand @src,src + if @dest.type = 'mem' & @src.type = 'bnd' + if @dest.scale > 1 | ( @dest.split & ~ 0 scaleof @dest.index_part eq 0 ) + err 'invalid index' + end if + x86.store_instruction <0Fh,1Bh>,@dest,@src.rm + else + err 'invalid combination of operands' + end if + else + err 'invalid combination of operands' + end match + end macro + +end if diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/pclmulqdq.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/pclmulqdq.inc new file mode 100644 index 0000000..235e46c --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/pclmulqdq.inc @@ -0,0 +1,14 @@ + +include 'sse.inc' + +macro pclmulqdq? dest*,src*,imm* + SSE.basic_instruction_imm8 66h,<3Ah,44h>,16,dest,src,imm +end macro + +if defined AVX + + macro vpclmulqdq? dest*,src*,src2*,imm* + AVX.basic_instruction_imm8 VEX_66_0F3A_W0,44h,16,dest,src,src2,imm + end macro + +end if diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/ptwrite.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/ptwrite.inc new file mode 100644 index 0000000..de1514a --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/ptwrite.inc @@ -0,0 +1,18 @@ + +macro ptwrite? src* + x86.parse_operand @src,src + if @src.size = 0 + err 'operand size not specified' + else if @src.size <> 4 & (@src.size <> 8 | x86.mode <> 64) + err 'invalid operand size' + end if + if @src.type = 'reg' | @src.type = 'mem' + if @src.size = 8 + x86.select_operand_prefix @src,8 + end if + @src.opcode_prefix = 0F3h + x86.store_instruction <0Fh,0AEh>,@src,4 + else + err 'invalid operand' + end if +end macro diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/rdrand.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/rdrand.inc new file mode 100644 index 0000000..76c9668 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/rdrand.inc @@ -0,0 +1,10 @@ + +macro rdrand? dest* + x86.parse_operand @dest,dest + if @dest.type = 'reg' + x86.select_operand_prefix @dest,@dest.size + x86.store_instruction <0Fh,0C7h>,@dest,6 + else + err 'invalid operand' + end if +end macro diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/rdseed.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/rdseed.inc new file mode 100644 index 0000000..d026881 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/rdseed.inc @@ -0,0 +1,10 @@ + +macro rdseed? dest* + x86.parse_operand @dest,dest + if @dest.type = 'reg' + x86.select_operand_prefix @dest,@dest.size + x86.store_instruction <0Fh,0C7h>,@dest,7 + else + err 'invalid operand' + end if +end macro diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/rdtscp.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/rdtscp.inc new file mode 100644 index 0000000..86fd1b2 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/rdtscp.inc @@ -0,0 +1,4 @@ + +macro rdtscp? + db 0Fh,1,0F9h +end macro diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/rtm.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/rtm.inc new file mode 100644 index 0000000..3942e09 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/rtm.inc @@ -0,0 +1,39 @@ + +macro xbegin? dest* + x86.parse_jump_operand @dest,dest + if @dest.type = 'imm' & ~ @dest.jump_type + if x86.mode shr 3 <> @dest.size + err 'invalid operand size' + end if + if x86.mode = 16 + db 0C7h,0F8h + dw @dest.imm-($+2) + else + if ~ $ relativeto 0 & @dest.imm relativeto 0 + @dest.imm = @dest.imm + $ - 0 scaleof $ + err 'invalid address' + end if + if @dest.unresolved | ( @dest.imm relativeto $ & @dest.imm-($+5) < 8000h & @dest.imm-($+5) >= -8000h ) + db 66h,0C7h,0F8h + dw @dest.imm-($+2) + else + db 0C7h,0F8h + dd @dest.imm-($+4) + end if + end if + else + err 'invalid operand' + end if +end macro + +macro xabort? imm* + db 0C6h,0F8h,imm +end macro + +macro xend? + db 0Fh,1,0D5h +end macro + +macro xtest? + db 0Fh,1,0D6h +end macro diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/smx.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/smx.inc new file mode 100644 index 0000000..93919b8 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/smx.inc @@ -0,0 +1,4 @@ + +macro getsec? + db 0Fh,37h +end macro diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/sse.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/sse.inc new file mode 100644 index 0000000..f8609d1 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/sse.inc @@ -0,0 +1,457 @@ + +if ~ defined SSE + + restore SSE ; this ensures that symbol cannot be forward-referenced + SSE = 1 + + include 'mmx.inc' + + element SSE.reg + + repeat 8, i:0 + element xmm#i? : SSE.reg + i + end repeat + + define x86.dqword? :16 + define x86.xword? :16 + + calminstruction SSE.parse_operand namespace, operand + + local size, type, mod, rm, imm + local i, sym, cmd + + asmcmd =x86.=parse_operand namespace, operand + + arrange type, namespace.=type + arrange size, namespace.=size + arrange imm, namespace.=imm + + check type = 'imm' & size = 0 + jno done + check imm eq 1 elementof imm & 1 metadataof imm relativeto MMX.reg + jyes mm_register + check imm eq 1 elementof imm & 1 metadataof imm relativeto SSE.reg + jyes xmm_register + exit + + mm_register: + + compute rm, 1 metadataof imm - MMX.reg + compute size, 8 + + jump export_mmreg + + xmm_register: + + compute rm, 1 metadataof imm - SSE.reg + compute size, 16 + + export_mmreg: + + compute type, 'mmreg' + compute mod, 11b + + arrange sym, namespace.=mod + publish sym, mod + + arrange sym, namespace.=rm + publish sym, rm + + arrange sym, namespace.=type + publish sym, type + + arrange sym, namespace.=size + publish sym, size + + done: + + end calminstruction + + calminstruction SSE.basic_instruction pre,ext,msize,dest,src + asmcmd =SSE.=parse_operand =@dest,dest + asmcmd =SSE.=parse_operand =@src,src + check @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') + jno invalid_combination_of_operands + check @dest.size <> 16 | (@src.type = 'mem' & @src.size and not msize) | (@src.type = 'mmreg' & @src.size <> 16) + jno size_ok + asmcmd =err 'invalid operand size' + size_ok: + compute @src.opcode_prefix, pre + asmcmd =x86.=store_instruction <0Fh,ext>,=@src,=@dest.=rm + exit + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' + end calminstruction + + calminstruction SSE.basic_instruction_imm8 pre,ext,msize,dest,src,aux + asmcmd =SSE.=parse_operand =@dest,dest + asmcmd =SSE.=parse_operand =@src,src + asmcmd =x86.=parse_operand =@aux,aux + check @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') & @aux.type = 'imm' + jno invalid_combination_of_operands + check @dest.size <> 16 | (@src.type = 'mem' & @src.size and not msize) | (@src.type = 'mmreg' & @src.size <> 16) | @aux.size and not 1 + jno size_ok + asmcmd =err 'invalid operand size' + size_ok: + compute @src.opcode_prefix, pre + asmcmd =x86.=store_instruction <0Fh,ext>,=@src,=@dest.=rm,1,=@aux.=imm + exit + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' + end calminstruction + + iterate , sqrt,51h, rsqrt,52h, rcp,53h, add,58h, mul,59h, sub,5Ch, min,5Dh, div,5Eh, max,5Fh + macro instr#ps? dest*,src* + SSE.basic_instruction 0,ext,16,dest,src + end macro + macro instr#ss? dest*,src* + SSE.basic_instruction 0F3h,ext,4,dest,src + end macro + end iterate + + iterate , and,54h, andn,55h, or,56h, xor,57h, unpckl,14h, unpckh,15h + macro instr#ps? dest*,src* + SSE.basic_instruction 0,ext,16,dest,src + end macro + end iterate + + macro cmpps? dest*,src*,code* + SSE.basic_instruction_imm8 0,0C2h,16,dest,src,code + end macro + + macro cmpss? dest*,src*,code* + SSE.basic_instruction_imm8 0F3h,0C2h,4,dest,src,code + end macro + + iterate , eq,0, lt,1, le,2, unord,3, neq,4, nlt,5, nle,6, ord,7 + macro cmp#cond#ps? dest*,src* + cmpps dest,src,code + end macro + macro cmp#cond#ss? dest*,src* + cmpss dest,src,code + end macro + end iterate + + macro shufps? dest*,src*,imm* + SSE.basic_instruction_imm8 0,0C6h,16,dest,src,imm + end macro + + iterate , movaps,28h, movups,10h + macro instr? dest*,src* + SSE.parse_operand @dest,dest + SSE.parse_operand @src,src + if (@dest.size or @src.size) and not 16 + err 'invalid operand size' + end if + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') + x86.store_instruction <0Fh,ext>,@src,@dest.rm + else if @dest.type = 'mem' & @src.type = 'mmreg' + x86.store_instruction <0Fh,ext+1>,@dest,@src.rm + else + err 'invalid combination of operands' + end if + end macro + end iterate + + iterate , movlps,12h, movhps,16h + macro instr? dest*,src* + SSE.parse_operand @dest,dest + SSE.parse_operand @src,src + if @dest.type = 'mmreg' & @src.type = 'mem' + if @dest.size <> 16 | @src.size and not 8 + err 'invalid operand size' + end if + x86.store_instruction <0Fh,ext>,@src,@dest.rm + else if @dest.type = 'mem' & @src.type = 'mmreg' + if @dest.size and not 8 | @src.size <> 16 + err 'invalid operand size' + end if + x86.store_instruction <0Fh,ext+1>,@dest,@src.rm + else + err 'invalid combination of operands' + end if + end macro + end iterate + + iterate , movhlps,12h, movlhps,16h + macro instr? dest*,src* + SSE.parse_operand @dest,dest + SSE.parse_operand @src,src + if (@dest.size or @src.size) and not 16 + err 'invalid operand size' + end if + if @dest.type = 'mmreg' & @src.type = 'mmreg' + x86.store_instruction <0Fh,ext>,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + end iterate + + macro movss? dest*,src* + SSE.parse_operand @dest,dest + SSE.parse_operand @src,src + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') + if @dest.size <> 16 | (@src.type = 'mem' & @src.size and not 4) | (@src.type = 'mmreg' & @src.size <> 16) + err 'invalid operand size' + end if + @src.opcode_prefix = 0F3h + x86.store_instruction <0Fh,10h>,@src,@dest.rm + else if @dest.type = 'mem' & @src.type = 'mmreg' + if @dest.size and not 4 | @src.size <> 16 + err 'invalid operand size' + end if + @dest.opcode_prefix = 0F3h + x86.store_instruction <0Fh,11h>,@dest,@src.rm + else + err 'invalid combination of operands' + end if + end macro + + macro movntps? dest*,src* + SSE.parse_operand @dest,dest + SSE.parse_operand @src,src + if @dest.type = 'mem' & @src.type = 'mmreg' + if (@dest.size or @src.size) and not 16 + err 'invalid operand size' + end if + x86.store_instruction <0Fh,2Bh>,@dest,@src.rm + else + err 'invalid combination of operands' + end if + end macro + + macro movmskps? dest*,src* + x86.parse_operand @dest,dest + SSE.parse_operand @src,src + if @dest.type = 'reg' & @src.type = 'mmreg' + if (@dest.size <> 4 & (x86.mode < 64 | @dest.size <> 8)) | @src.size <> 16 + err 'invalid operand size' + end if + x86.store_instruction <0Fh,50h>,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + iterate , ucomiss,2Eh, comiss,2Fh + macro instr? dest*,src* + SSE.parse_operand @dest,dest + SSE.parse_operand @src,src + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') + if @dest.size <> 16 | (@src.type = 'mem' & @src.size and not 4) | (@src.type = 'mmreg' & @src.size <> 16) + err 'invalid operand size' + end if + x86.store_instruction <0Fh,ext>,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + end iterate + + macro cvtpi2ps? dest*,src* + SSE.parse_operand @dest,dest + SSE.parse_operand @src,src + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') + if @dest.size <> 16 | @src.size and not 8 + err 'invalid operand size' + end if + x86.store_instruction <0Fh,2Ah>,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + macro cvtsi2ss? dest*,src* + SSE.parse_operand @dest,dest + x86.parse_operand @src,src + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'reg') + if @src.size = 0 + err 'operand size not specified' + else if @dest.size <> 16 | @src.size < 4 + err 'invalid operand size' + end if + x86.select_operand_prefix @src,@src.size + @src.opcode_prefix = 0F3h + x86.store_instruction <0Fh,2Ah>,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + iterate , cvttps2pi,2Ch, cvtps2pi,2Dh + macro instr? dest*,src* + SSE.parse_operand @dest,dest + SSE.parse_operand @src,src + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') + if @dest.size <> 8 | (@src.size = 'mem' & @src.size and not 8) | (@src.size = 'mmreg' & @src.size <> 16) + err 'invalid operand size' + end if + x86.store_instruction <0Fh,ext>,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + end iterate + + iterate , cvttss2si,2Ch, cvtss2si,2Dh + macro instr? dest*,src* + SSE.parse_operand @dest,dest + SSE.parse_operand @src,src + if @dest.type = 'reg' & (@src.type = 'mem' | @src.type = 'mmreg') + if @dest.size < 4 | (@src.size = 'mem' & @src.size and not 4) | (@src.size = 'mmreg' & @src.size <> 16) + err 'invalid operand size' + end if + x86.select_operand_prefix @src,@dest.size + @src.opcode_prefix = 0F3h + x86.store_instruction <0Fh,ext>,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + end iterate + + iterate , pminub,0DAh, pmaxub,0DEh, pavgb,0E0h, pavgw,0E3h, pmulhuw,0E4h, pminsw,0EAh, pmaxsw,0EEh, psadbw,0F6h + macro instr? dest*,src* + MMX.basic_instruction ext,dest,src + end macro + end iterate + + macro pinsrw? dest*,src*,sel* + MMX.parse_operand @dest,dest + x86.parse_operand @src,src + x86.parse_operand @aux,sel + if @dest.type = 'mmreg' & (@src.type = 'reg' | @src.type = 'mem') & @aux.type = 'imm' + if (@src.type = 'reg' & @src.size <> 4) | (@src.type = 'mem' & @src.size and not 2) | @aux.size and not 1 + err 'invalid operand size' + end if + x86.store_instruction <0Fh,0C4h>,@src,@dest.rm,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + + macro pextrw? dest*,src*,sel* + x86.parse_operand @dest,dest + MMX.parse_operand @src,src + x86.parse_operand @aux,sel + if @dest.type = 'reg' & @src.type = 'mmreg' & @aux.type = 'imm' + if @dest.size <> 4 | @aux.size and not 1 + err 'invalid operand size' + end if + x86.store_instruction <0Fh,0C5h>,@src,@dest.rm,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + + macro pshufw? dest*,src*,sel* + MMX.parse_operand @dest,dest + MMX.parse_operand @src,src + x86.parse_operand @aux,sel + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') & @aux.type = 'imm' + if @src.size and not 8 | @aux.size and not 1 + err 'invalid operand size' + end if + x86.store_instruction <0Fh,70h>,@src,@dest.rm,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + + macro pmovmskb? dest*,src* + x86.parse_operand @dest,dest + MMX.parse_operand @src,src + if @dest.type = 'reg' & @src.type = 'mmreg' + if @dest.size <> 4 + err 'invalid operand size' + end if + x86.store_instruction <0Fh,0D7h>,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + macro movntq? dest*,src* + MMX.parse_operand @dest,dest + MMX.parse_operand @src,src + if @dest.type = 'mem' & @src.type = 'mmreg' + if @dest.size and not 8 + err 'invalid operand size' + end if + x86.store_instruction <0Fh,0E7h>,@dest,@src.rm + else + err 'invalid combination of operands' + end if + end macro + + macro maskmovq? src*,sel* + MMX.parse_operand @src,src + MMX.parse_operand @aux,sel + if @src.type = 'mmreg' & @aux.type = 'mmreg' + x86.store_instruction <0Fh,0F7h>,@aux,@src.rm + else + err 'invalid combination of operands' + end if + end macro + + iterate , prefetchnta,0, prefetcht0,1, prefetcht1,2, prefetcht2,3 + macro instr? src* + x86.parse_operand @src,src + if @src.type = 'mem' + if @src.size and not 1 + err 'invalid operand size' + end if + x86.store_instruction <0Fh,18h>,@src,postbyte + else + err 'invalid operand' + end if + end macro + end iterate + +;HUETNOHUTNOEHUTNOEUHNOETUHTNOEUHTNOEUHNOETU -- MISSING -- START + macro prefetchw? src* + x86.parse_operand @src,src + if @src.type = 'mem' + if @src.size and not 1 + err 'invalid operand size' + end if + x86.store_instruction <0Fh,0Dh>,@src,1 + else + err 'invalid operand' + end if + end macro +;HUETNOHUTNOEHUTNOEUHNOETUHTNOEUHTNOEUHNOETU -- MISSING -- END + macro sfence? + db 0Fh,0AEh,0F8h + end macro + + iterate , fxsave,0, fxrstor,1 + macro instr? src* + x86.parse_operand @src,src + if @src.type = 'mem' + if @src.size and not 512 + err 'invalid operand size' + end if + x86.store_instruction <0Fh,0AEh>,@src,postbyte + else + err 'invalid operand' + end if + end macro + end iterate + + iterate , ldmxcsr,2, stmxcsr,3 + macro instr? src* + x86.parse_operand @src,src + if @src.type = 'mem' + if @src.size and not 4 + err 'invalid operand size' + end if + x86.store_instruction <0Fh,0AEh>,@src,postbyte + else + err 'invalid operand' + end if + end macro + end iterate + +end if diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/sse2.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/sse2.inc new file mode 100644 index 0000000..340c141 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/sse2.inc @@ -0,0 +1,603 @@ + +if ~ defined SSE2 + + restore SSE2 ; this ensures that symbol cannot be forward-referenced + SSE2 = 1 + + include 'sse.inc' + + iterate , sqrt,51h, rsqrt,52h, rcp,53h, add,58h, mul,59h, sub,5Ch, min,5Dh, div,5Eh, max,5Fh + macro instr#pd? dest*,src* + SSE.basic_instruction 66h,ext,16,dest,src + end macro + macro instr#sd? dest*,src* + SSE.basic_instruction 0F2h,ext,8,dest,src + end macro + end iterate + + iterate , and,54h, andn,55h, or,56h, xor,57h, unpckl,14h, unpckh,15h + macro instr#pd? dest*,src* + SSE.basic_instruction 66h,ext,16,dest,src + end macro + end iterate + + macro cmppd? dest*,src*,code* + SSE.basic_instruction_imm8 66h,0C2h,16,dest,src,code + end macro + + macro SSE.cmpsd? dest*,src*,code* + SSE.basic_instruction_imm8 0F2h,0C2h,8,dest,src,code + end macro + + calminstruction cmpsd? args& + match , args + jno sse + assemble x86.o32 + assemble x86.cmpsw + exit + sse: + arrange args, =SSE.=cmpsd args + assemble args + end calminstruction + + iterate , eq,0, lt,1, le,2, unord,3, neq,4, nlt,5, nle,6, ord,7 + macro cmp#cond#pd? dest*,src* + cmppd dest,src,code + end macro + macro cmp#cond#sd? dest*,src* + cmpsd dest,src,code + end macro + end iterate + + macro shufpd? dest*,src*,imm* + SSE.basic_instruction_imm8 66h,0C6h,16,dest,src,imm + end macro + + iterate , movapd,28h, movupd,10h + macro instr? dest*,src* + SSE.parse_operand @dest,dest + SSE.parse_operand @src,src + if (@dest.size or @src.size) and not 16 + err 'invalid operand size' + end if + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') + @src.opcode_prefix = 66h + x86.store_instruction <0Fh,ext>,@src,@dest.rm + else if @dest.type = 'mem' & @src.type = 'mmreg' + @dest.opcode_prefix = 66h + x86.store_instruction <0Fh,ext+1>,@dest,@src.rm + else + err 'invalid combination of operands' + end if + end macro + end iterate + + iterate , movlpd,12h, movhpd,16h + macro instr? dest*,src* + SSE.parse_operand @dest,dest + SSE.parse_operand @src,src + if @dest.type = 'mmreg' & @src.type = 'mem' + if @dest.size <> 16 | @src.size and not 8 + err 'invalid operand size' + end if + @src.opcode_prefix = 66h + x86.store_instruction <0Fh,ext>,@src,@dest.rm + else if @dest.type = 'mem' & @src.type = 'mmreg' + if @dest.size and not 8 | @src.size <> 16 + err 'invalid operand size' + end if + @dest.opcode_prefix = 66h + x86.store_instruction <0Fh,ext+1>,@dest,@src.rm + else + err 'invalid combination of operands' + end if + end macro + end iterate + + macro SSE.movsd? dest*,src* + SSE.parse_operand @dest,dest + SSE.parse_operand @src,src + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') + if @dest.size <> 16 | (@src.type = 'mem' & @src.size and not 8) | (@src.type = 'mmreg' & @src.size <> 16) + err 'invalid operand size' + end if + @src.opcode_prefix = 0F2h + x86.store_instruction <0Fh,10h>,@src,@dest.rm + else if @dest.type = 'mem' & @src.type = 'mmreg' + if @dest.size and not 8 | @src.size <> 16 + err 'invalid operand size' + end if + @dest.opcode_prefix = 0F2h + x86.store_instruction <0Fh,11h>,@dest,@src.rm + else + err 'invalid combination of operands' + end if + end macro + + calminstruction movsd? args& + match , args + jno sse + assemble x86.o32 + assemble x86.movsw + exit + sse: + arrange args, =SSE.=movsd args + assemble args + end calminstruction + + iterate , movdqa,66h, movdqu,0F3h + macro instr? dest*,src* + SSE.parse_operand @dest,dest + SSE.parse_operand @src,src + if (@dest.size or @src.size) and not 16 + err 'invalid operand size' + end if + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') + @src.opcode_prefix = pre + x86.store_instruction <0Fh,6Fh>,@src,@dest.rm + else if @dest.type = 'mem' & @src.type = 'mmreg' + @dest.opcode_prefix = pre + x86.store_instruction <0Fh,7Fh>,@dest,@src.rm + else + err 'invalid combination of operands' + end if + end macro + end iterate + + iterate , movntpd,2Bh, movntdq,0E7h + macro instr? dest*,src* + SSE.parse_operand @dest,dest + SSE.parse_operand @src,src + if @dest.type = 'mem' & @src.type = 'mmreg' + if (@dest.size or @src.size) and not 16 + err 'invalid operand size' + end if + @dest.opcode_prefix = 66h + x86.store_instruction <0Fh,ext>,@dest,@src.rm + else + err 'invalid combination of operands' + end if + end macro + end iterate + + macro movmskpd? dest*,src* + SSE.parse_operand @dest,dest + SSE.parse_operand @src,src + if @dest.type = 'reg' & @src.type = 'mmreg' + if (@dest.size <> 4 & (x86.mode < 64 | @dest.size <> 8)) | @src.size <> 16 + err 'invalid operand size' + end if + @src.opcode_prefix = 66h + x86.store_instruction <0Fh,50h>,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + macro maskmovdqu? dest*,src* + SSE.parse_operand @dest,dest + SSE.parse_operand @src,src + if @dest.type = 'mmreg' & @src.type = 'mmreg' + if (@dest.size or @src.size) <> 16 + err 'invalid operand size' + end if + @src.opcode_prefix = 66h + x86.store_instruction <0Fh,0F7h>,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + iterate , ucomisd,2Eh, comisd,2Fh + macro instr? dest*,src* + SSE.parse_operand @dest,dest + SSE.parse_operand @src,src + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') + if @dest.size <> 16 | (@src.type = 'mem' & @src.size and not 8) | (@src.type = 'mmreg' & @src.size <> 16) + err 'invalid operand size' + end if + @src.opcode_prefix = 66h + x86.store_instruction <0Fh,ext>,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + end iterate + + macro cvtps2pd? dest*,src* + SSE.basic_instruction 0,5Ah,8,dest,src + end macro + + macro cvtpd2ps? dest*,src* + SSE.basic_instruction 66h,5Ah,16,dest,src + end macro + + macro cvtsd2ss? dest*,src* + SSE.basic_instruction 0F2h,5Ah,8,dest,src + end macro + + macro cvtss2sd? dest*,src* + SSE.basic_instruction 0F3h,5Ah,4,dest,src + end macro + + macro cvtdq2ps? dest*,src* + SSE.basic_instruction 0,5Bh,16,dest,src + end macro + + macro cvtps2dq? dest*,src* + SSE.basic_instruction 66h,5Bh,16,dest,src + end macro + + macro cvttps2dq? dest*,src* + SSE.basic_instruction 0F3h,5Bh,16,dest,src + end macro + + macro cvttpd2dq? dest*,src* + SSE.basic_instruction 66h,0E6h,16,dest,src + end macro + + macro cvtpd2dq? dest*,src* + SSE.basic_instruction 0F2h,0E6h,16,dest,src + end macro + + macro cvtdq2pd? dest*,src* + SSE.basic_instruction 0F3h,0E6h,8,dest,src + end macro + + macro movdq2q? dest*,src* + SSE.parse_operand @dest,dest + SSE.parse_operand @src,src + if @dest.type = 'mmreg' & @src.type = 'mmreg' + if @dest.size <> 8 | @src.size <> 16 + err 'invalid operand size' + end if + @src.opcode_prefix = 0F2h + x86.store_instruction <0Fh,0D6h>,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + macro movq2dq? dest*,src* + SSE.parse_operand @dest,dest + SSE.parse_operand @src,src + if @dest.type = 'mmreg' & @src.type = 'mmreg' + if @dest.size <> 16 | @src.size <> 8 + err 'invalid operand size' + end if + @src.opcode_prefix = 0F3h + x86.store_instruction <0Fh,0D6h>,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + macro cvtpi2pd? dest*,src* + SSE.parse_operand @dest,dest + SSE.parse_operand @src,src + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') + if @dest.size <> 16 | @src.size and not 8 + err 'invalid operand size' + end if + @src.opcode_prefix = 66h + x86.store_instruction <0Fh,2Ah>,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + macro cvtsi2sd? dest*,src* + SSE.parse_operand @dest,dest + x86.parse_operand @src,src + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'reg') + if @src.size = 0 + err 'operand size not specified' + else if @dest.size <> 16 | @src.size < 4 + err 'invalid operand size' + end if + x86.select_operand_prefix @src,@src.size + @src.opcode_prefix = 0F2h + x86.store_instruction <0Fh,2Ah>,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + iterate , cvttpd2pi,2Ch, cvtpd2pi,2Dh + macro instr? dest*,src* + SSE.parse_operand @dest,dest + SSE.parse_operand @src,src + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') + if @dest.size <> 8 | @src.size and not 16 + err 'invalid operand size' + end if + @src.opcode_prefix = 66h + x86.store_instruction <0Fh,ext>,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + end iterate + + iterate , cvttsd2si,2Ch, cvtsd2si,2Dh + macro instr? dest*,src* + x86.parse_operand @dest,dest + SSE.parse_operand @src,src + if @dest.type = 'reg' & (@src.type = 'mem' | @src.type = 'mmreg') + if @dest.size < 4 | (@src.type = 'mem' & @src.size and not 8) | (@src.type = 'mmreg' & @src.size <>16) + err 'invalid operand size' + end if + x86.select_operand_prefix @src,@dest.size + @src.opcode_prefix = 0F2h + x86.store_instruction <0Fh,ext>,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + end iterate + + calminstruction MMX.select_operand_prefix rm_operand*,size* + local sym, prefix + check size = 16 + jno no_prefix + compute prefix, 66h + arrange sym, rm_operand.=prefix + publish sym, prefix + exit + no_prefix: + check size <> 8 + jno done + asmcmd =err 'invalid operand size' + done: + end calminstruction + + calminstruction MMX.basic_instruction ext,dest,src + asmcmd =SSE.=parse_operand =@dest,dest + asmcmd =SSE.=parse_operand =@src,src + check @src.size and not @dest.size + jno size_ok + asmcmd =err 'operand sizes do not match' + size_ok: + check @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') + jno invalid_combination_of_operands + asmcmd =MMX.=select_operand_prefix =@src,=@dest.=size + asmcmd =x86.=store_instruction <0Fh,ext>,=@src,=@dest.=rm + exit + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' + end calminstruction + + calminstruction MMX.bit_shift_instruction ext,dest,src + asmcmd =SSE.=parse_operand =@dest,dest + asmcmd =SSE.=parse_operand =@src,src + check @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') + jyes mmreg_rm + check @dest.type = 'mmreg' & @src.type = 'imm' + jyes mmreg_imm + asmcmd =err 'invalid combination of operands' + exit + mmreg_rm: + check @src.size and not @dest.size + jno mmreg_rm_ok + asmcmd =err 'operand sizes do not match' + mmreg_rm_ok: + asmcmd =MMX.=select_operand_prefix =@src,=@dest.=size + asmcmd =x86.=store_instruction <0Fh,ext>,=@src,=@dest.=rm + exit + mmreg_imm: + check @src.size and not 1 + jno rm_mmreg_ok + asmcmd =err 'invalid operand size' + rm_mmreg_ok: + local iext, irm + compute iext, 70h+(ext and 0Fh) + compute irm, ((ext shr 4)-0Ch) shl 1 + asmcmd =MMX.=select_operand_prefix =@dest,=@dest.=size + asmcmd =x86.=store_instruction <0Fh,iext>,=@dest,irm,1,=@src.=imm + end calminstruction + + iterate , paddq,0D4h, pmuludq,0F4h, psubq,0FBh + macro instr? dest*,src* + MMX.basic_instruction ext,dest,src + end macro + end iterate + + macro movq? dest*,src* + SSE.parse_operand @dest,dest + SSE.parse_operand @src,src + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') + if (@src.type = 'mem' & @src.size and not 8) | (@src.type = 'mmreg' & @src.size <> @dest.size) + err 'invalid operand size' + end if + if @dest.size = 8 + x86.store_instruction <0Fh,6Fh>,@src,@dest.rm + else + @src.opcode_prefix = 0F3h + x86.store_instruction <0Fh,7Eh>,@src,@dest.rm + end if + else if @dest.type = 'mem' & @src.type = 'mmreg' + if @dest.size and not 8 + err 'invalid operand size' + end if + if @src.size = 8 + x86.store_instruction <0Fh,7Fh>,@dest,@src.rm + else + @dest.opcode_prefix = 66h + x86.store_instruction <0Fh,0D6h>,@dest,@src.rm + end if + else if @dest.type = 'reg' & @src.type = 'mmreg' + if @dest.size <> 8 + err 'invalid operand size' + end if + if @src.size = 16 + @dest.opcode_prefix = 66h + end if + @dest.prefix = 48h + x86.store_instruction <0Fh,7Eh>,@dest,@src.rm + else if @dest.type = 'mmreg' & @src.type = 'reg' + if @src.size <> 8 + err 'invalid operand size' + end if + if @dest.size = 16 + @src.opcode_prefix = 66h + end if + @src.prefix = 48h + x86.store_instruction <0Fh,6Eh>,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + macro movd? dest*,src* + SSE.parse_operand @dest,dest + SSE.parse_operand @src,src + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'reg') + if @src.size and not 4 + err 'invalid operand size' + end if + MMX.select_operand_prefix @src,@dest.size + x86.store_instruction <0Fh,6Eh>,@src,@dest.rm + else if (@dest.type = 'mem' | @dest.type = 'reg') & @src.type = 'mmreg' + if @dest.size and not 4 + err 'invalid operand size' + end if + MMX.select_operand_prefix @dest,@src.size + x86.store_instruction <0Fh,7Eh>,@dest,@src.rm + else + err 'invalid combination of operands' + end if + end macro + + macro pinsrw? dest*,src*,sel* + SSE.parse_operand @dest,dest + x86.parse_operand @src,src + x86.parse_operand @aux,sel + if @dest.type = 'mmreg' & (@src.type = 'reg' | @src.type = 'mem') & @aux.type = 'imm' + if (@src.type = 'reg' & @src.size <> 4) | (@src.type = 'mem' & @src.size and not 2) | @aux.size and not 1 + err 'invalid operand size' + end if + MMX.select_operand_prefix @src,@dest.size + x86.store_instruction <0Fh,0C4h>,@src,@dest.rm,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + + macro pextrw? dest*,src*,sel* + x86.parse_operand @dest,dest + SSE.parse_operand @src,src + x86.parse_operand @aux,sel + if @dest.type = 'reg' & @src.type = 'mmreg' & @aux.type = 'imm' + if x86.mode = 64 & @dest.size = 8 + @dest.size = 4 + end if + if @dest.size <> 4 | @aux.size and not 1 + err 'invalid operand size' + end if + MMX.select_operand_prefix @src,@src.size + x86.store_instruction <0Fh,0C5h>,@src,@dest.rm,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + + iterate , pshufd,66h, pshuflw,0F2h, pshufhw,0F3h + macro instr? dest*,src*,sel* + SSE.parse_operand @dest,dest + SSE.parse_operand @src,src + x86.parse_operand @aux,sel + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') & @aux.type = 'imm' + if @dest.size <> 16 | @src.size and not 16 | @aux.size and not 1 + err 'invalid operand size' + end if + @src.opcode_prefix = pre + x86.store_instruction <0Fh,70h>,@src,@dest.rm,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + end iterate + + macro pmovmskb? dest*,src* + x86.parse_operand @dest,dest + SSE.parse_operand @src,src + if @dest.type = 'reg' & @src.type = 'mmreg' + if @dest.size <> 4 & (x86.mode < 64 | @dest.size <> 8) + err 'invalid operand size' + end if + MMX.select_operand_prefix @src,@src.size + x86.store_instruction <0Fh,0D7h>,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + + iterate , psrldq,3, pslldq,7 + macro instr? dest*,cnt* + SSE.parse_operand @dest,dest + x86.parse_operand @aux,cnt + if @dest.type = 'mmreg' & @aux.type = 'imm' + if @dest.size <> 16 | @aux.size and not 1 + err 'invalid operand size' + end if + @dest.opcode_prefix = 66h + x86.store_instruction <0Fh,73h>,@dest,postbyte,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + end iterate + + iterate , punpcklqdq,6Ch, punpckhqdq,6Dh + macro instr? dest*,src* + SSE.parse_operand @dest,dest + SSE.parse_operand @src,src + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') + if (@dest.size or @src.size) and not 16 + err 'invalid operand size' + end if + @src.opcode_prefix = 66h + x86.store_instruction <0Fh,ext>,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro + end iterate + + macro movnti? dest*,src* + x86.parse_operand @dest,dest + x86.parse_operand @src,src + if @dest.type = 'mem' & @src.type = 'reg' + if @dest.size and not @src.size + err 'operand sizes do not match' + else if @src.size <> 4 & @src.size <> 8 + err 'invalid operand size' + end if + x86.select_operand_prefix @dest,@src.size + x86.store_instruction <0Fh,0C3h>,@dest,@src.rm + else + err 'invalid combination of operands' + end if + end macro + + macro clflush? src* + x86.parse_operand @src,src + if @src.type = 'mem' + if @src.size and not 1 + err 'invalid operand size' + end if + x86.store_instruction <0Fh,0AEh>,@src,7 + else + err 'invalid operand' + end if + end macro + + macro lfence? + db 0Fh,0AEh,0E8h + end macro + + macro mfence? + db 0Fh,0AEh,0F0h + end macro + +end if \ No newline at end of file diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/sse3.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/sse3.inc new file mode 100644 index 0000000..40bc4b5 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/sse3.inc @@ -0,0 +1,72 @@ + +include 'sse2.inc' + +macro fisttp? src* + x86.parse_operand @src,src + if @src.type = 'mem' + if @src.size = 2 + x86.store_instruction 0DFh,@src,1 + else if @src.size = 4 + x86.store_instruction 0DBh,@src,1 + else if @src.size = 8 + x86.store_instruction 0DDh,@src,1 + else if @src.size + err 'invalid operand size' + else + err 'operand size not specified' + end if + else + err 'invalid operand' + end if +end macro + +iterate , addsub,0D0h, hadd,7Ch, hsub,7Dh + macro instr#pd? dest*,src* + SSE.basic_instruction 66h,ext,16,dest,src + end macro + macro instr#ps? dest*,src* + SSE.basic_instruction 0F2h,ext,16,dest,src + end macro +end iterate + +iterate , movsldup,12h, movshdup,16h + macro instr? dest*,src* + SSE.basic_instruction 0F3h,ext,16,dest,src + end macro +end iterate + +macro movddup? dest*,src* + SSE.basic_instruction 0F2h,12h,8,dest,src +end macro + +macro lddqu? dest*,src* + SSE.parse_operand @dest,dest + SSE.parse_operand @src,src + if (@dest.size or @src.size) and not 16 + err 'invalid operand size' + end if + if @dest.type = 'mmreg' & @src.type = 'mem' + @src.opcode_prefix = 0F2h + x86.store_instruction <0Fh,0F0h>,@src,@dest.rm + else + err 'invalid combination of operands' + end if +end macro + +macro monitor? arg1,arg2,arg3 + match any, arg1 arg2 arg3 + if ~ arg1 eq eax | ~ arg2 eq ecx | ~ arg3 eq edx + err 'invalid combination of operands' + end if + end match + db 0Fh,01h,0C8h +end macro + +macro mwait? arg1,arg2 + match any, arg1 arg2 + if ~ arg1 eq eax | ~ arg2 eq ecx + err 'invalid combination of operands' + end if + end match + db 0Fh,01h,0C9h +end macro diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/sse4.1.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/sse4.1.inc new file mode 100644 index 0000000..3f181f9 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/sse4.1.inc @@ -0,0 +1,204 @@ + +include 'ssse3.inc' + +iterate , ptest,17h, pmuldq,28h, pcmpeqq,29h, packusdw,2Bh, pminsb,38h, pminsd,39h, pminuw,3Ah, pminud,3Bh, pmaxsb,3Ch, pmaxsd,3Dh, pmaxuw,3Eh, pmaxud,3Fh, pmulld,40h, phminposuw,41h + macro instr? dest*,src* + SSE.basic_instruction 66h,<38h,supp>,16,dest,src + end macro +end iterate + +iterate , roundps,08h, roundpd,09h, roundss,0Ah, roundsd,0Bh, blendps,0Ch, blendpd,0Dh, pblendw,0Eh, dpps,40h, dppd,41h, mpsadbw,42h + macro instr? dest*,src*,imm* + SSE.basic_instruction_imm8 66h,<3Ah,supp>,16,dest,src,imm + end macro +end iterate + +iterate , pblendvb,10h, blendvps,14h, blendvpd,15h + macro instr? dest*,src*,sel* + SSE.parse_operand @dest,dest + SSE.parse_operand @src,src + SSE.parse_operand @aux,sel + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') & @aux.type = 'mmreg' & @aux.size = 16 & @aux.rm = 0 + if @dest.size or @src.size and not 16 + err 'invalid operand size' + end if + @src.opcode_prefix = 66h + x86.store_instruction <0Fh,38h,supp>,@src,@dest.rm + else + err 'invalid combination of operands' + end if + end macro +end iterate + +iterate , bw,0,8, bd,1,4, bq,2,2, wd,3,8, wq,4,4, dq,5,8 + macro pmovsx#conv? dest*,src* + SSE.basic_instruction 66h,<38h,20h+code>,msize,dest,src + end macro + macro pmovzx#conv? dest*,src* + SSE.basic_instruction 66h,<38h,30h+code>,msize,dest,src + end macro +end iterate + +macro insertps? dest*,src*,sel* + SSE.basic_instruction_imm8 66h,<3Ah,21h>,4,dest,src,sel +end macro + +macro extractps? dest*,src*,sel* + x86.parse_operand @dest,dest + SSE.parse_operand @src,src + x86.parse_operand @aux,sel + if @dest.type = 'reg' & @src.type = 'mmreg' & @aux.type = 'imm' + if x86.mode = 64 & @dest.size = 8 + @dest.size = 4 + end if + if @dest.size <> 4 | @src.size and not 16 | @aux.size and not 1 + err 'invalid operand size' + end if + @dest.opcode_prefix = 66h + x86.store_instruction <0Fh,3Ah,17h>,@dest,@src.rm,1,@aux.imm + else + err 'invalid combination of operands' + end if +end macro + +macro pinsrb? dest*,src*,sel* + SSE.parse_operand @dest,dest + x86.parse_operand @src,src + x86.parse_operand @aux,sel + if (@dest.type = 'mmreg' & @dest.size = 16) & (@src.type = 'reg' | @src.type = 'mem') & @aux.type = 'imm' + if (@src.type = 'reg' & @src.size <> 4) | (@src.type = 'mem' & @src.size and not 1) | @aux.size and not 1 + err 'invalid operand size' + end if + @src.opcode_prefix = 66h + x86.store_instruction <0Fh,3Ah,20h>,@src,@dest.rm,1,@aux.imm + else + err 'invalid combination of operands' + end if +end macro + +macro pinsrd? dest*,src*,sel* + SSE.parse_operand @dest,dest + x86.parse_operand @src,src + x86.parse_operand @aux,sel + if (@dest.type = 'mmreg' & @dest.size = 16) & (@src.type = 'reg' | @src.type = 'mem') & @aux.type = 'imm' + if @src.size and not 4 | @aux.size and not 1 + err 'invalid operand size' + end if + @src.opcode_prefix = 66h + x86.store_instruction <0Fh,3Ah,22h>,@src,@dest.rm,1,@aux.imm + else + err 'invalid combination of operands' + end if +end macro + +macro pinsrq? dest*,src*,sel* + SSE.parse_operand @dest,dest + x86.parse_operand @src,src + x86.parse_operand @aux,sel + if (@dest.type = 'mmreg' & @dest.size = 16) & (@src.type = 'reg' | @src.type = 'mem') & @aux.type = 'imm' + if @src.size and not 8 | @aux.size and not 1 + err 'invalid operand size' + end if + if x86.mode < 64 + err 'instruction requires long mode' + end if + @src.opcode_prefix = 66h + @src.rex_prefix = 48h + x86.store_instruction <0Fh,3Ah,22h>,@src,@dest.rm,1,@aux.imm + else + err 'invalid combination of operands' + end if +end macro + +macro pextrb? dest*,src*,sel* + x86.parse_operand @dest,dest + SSE.parse_operand @src,src + x86.parse_operand @aux,sel + if (@dest.type = 'reg' | @dest.type = 'mem') & (@src.type = 'mmreg' & @src.size = 16) & @aux.type = 'imm' + if x86.mode = 64 & @dest.type = 'reg' & @dest.size = 8 + @dest.size = 4 + end if + if (@dest.type = 'reg' & @dest.size <> 4) | (@dest.size = 'mem' & @dest.size and not 1) | @aux.size and not 1 + err 'invalid operand size' + end if + @dest.opcode_prefix = 66h + x86.store_instruction <0Fh,3Ah,14h>,@dest,@src.rm,1,@aux.imm + else + err 'invalid combination of operands' + end if +end macro + +macro pextrw? dest*,src*,sel* + x86.parse_operand @dest,dest + SSE.parse_operand @src,src + x86.parse_operand @aux,sel + if @dest.type = 'reg' & @src.type = 'mmreg' & @aux.type = 'imm' + if x86.mode = 64 & @dest.size = 8 + @dest.size = 4 + end if + if @dest.size <> 4 | @aux.size and not 1 + err 'invalid operand size' + end if + MMX.select_operand_prefix @src,@src.size + x86.store_instruction <0Fh,0C5h>,@src,@dest.rm,1,@aux.imm + else if @dest.type = 'mem' & (@src.type = 'mmreg' & @src.size = 16) & @aux.type = 'imm' + if @dest.size and not 2 | @aux.size and not 1 + err 'invalid operand size' + end if + @dest.opcode_prefix = 66h + x86.store_instruction <0Fh,3Ah,15h>,@dest,@src.rm,1,@aux.imm + else + err 'invalid combination of operands' + end if +end macro + +macro pextrd? dest*,src*,sel* + x86.parse_operand @dest,dest + SSE.parse_operand @src,src + x86.parse_operand @aux,sel + if (@dest.type = 'reg' | @dest.type = 'mem') & (@src.type = 'mmreg' & @src.size = 16) & @aux.type = 'imm' + if x86.mode = 64 & @dest.type = 'reg' & @dest.size = 8 + @dest.size = 4 + end if + if @dest.size and not 4 | @aux.size and not 1 + err 'invalid operand size' + end if + @dest.opcode_prefix = 66h + x86.store_instruction <0Fh,3Ah,16h>,@dest,@src.rm,1,@aux.imm + else + err 'invalid combination of operands' + end if +end macro + +macro pextrq? dest*,src*,sel* + x86.parse_operand @dest,dest + SSE.parse_operand @src,src + x86.parse_operand @aux,sel + if (@dest.type = 'reg' | @dest.type = 'mem') & (@src.type = 'mmreg' & @src.size = 16) & @aux.type = 'imm' + if @dest.size and not 8 | @aux.size and not 1 + err 'invalid operand size' + end if + if x86.mode < 64 + err 'instruction requires long mode' + end if + @dest.opcode_prefix = 66h + @dest.rex_prefix = 48h + x86.store_instruction <0Fh,3Ah,16h>,@dest,@src.rm,1,@aux.imm + else + err 'invalid combination of operands' + end if +end macro + +macro movntdqa? dest*,src* + SSE.parse_operand @dest,dest + SSE.parse_operand @src,src + if @dest.type = 'mmreg' & @src.type = 'mem' + if (@dest.size or @src.size) and not 16 + err 'invalid operand size' + end if + @src.opcode_prefix = 66h + x86.store_instruction <0Fh,38h,2Ah>,@src,@dest.rm + else + err 'invalid combination of operands' + end if +end macro diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/sse4.2.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/sse4.2.inc new file mode 100644 index 0000000..182ee10 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/sse4.2.inc @@ -0,0 +1,54 @@ + +include 'sse4.1.inc' + +iterate , pcmpgtq,37h + macro instr? dest*,src* + SSE.basic_instruction 66h,<38h,supp>,16,dest,src + end macro +end iterate + +iterate , pcmpestrm,60h, pcmpestri,61h, pcmpistrm,62h, pcmpistri,63h + macro instr? dest*,src*,imm* + SSE.basic_instruction_imm8 66h,<3Ah,supp>,16,dest,src,imm + end macro +end iterate + +macro crc32? dest*,src* + x86.parse_operand @dest,dest + x86.parse_operand @src,src + if @dest.type = 'reg' & ( @src.type = 'reg' | @src.type = 'mem' ) + if @dest.size <> 4 & ( @dest.size <> 8 | x86.mode <> 64 ) + err 'invalid operand size' + end if + @src.opcode_prefix = 0F2h + if @src.size > 1 + x86.select_operand_prefix @src,@src.size + x86.store_instruction <0Fh,38h,0F1h>,@src,@dest.rm + else if @src.size > 0 + x86.store_instruction <0Fh,38h,0F0h>,@src,@dest.rm + else + err 'operand size not specified' + end if + else + err 'invalid combination of operands' + end if +end macro + +macro popcnt? dest*,src* + x86.parse_operand @dest,dest + x86.parse_operand @src,src + if @dest.type = 'reg' & ( @src.type = 'reg' | @src.type = 'mem' ) + if @src.size and not @dest.size + err 'operand sizes do not match' + end if + @src.opcode_prefix = 0F3h + if @dest.size > 1 + x86.select_operand_prefix @src,@dest.size + x86.store_instruction <0Fh,0B8h>,@src,@dest.rm + else + err 'invalid operand size' + end if + else + err 'invalid combination of operands' + end if +end macro diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/ssse3.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/ssse3.inc new file mode 100644 index 0000000..cc35b26 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/ssse3.inc @@ -0,0 +1,26 @@ + +include 'sse3.inc' + +iterate , pshufb,0, phaddw,1, phaddd,2, phaddsw,3, pmaddubsw,4, phsubw,5, phsubd,6, phsubsw,7, psignb,8, psignw,9, psignd,0Ah, pmulhrsw,0Bh, pabsb,1Ch, pabsw,1Dh, pabsd,1Eh + macro instr? dest*,src* + MMX.basic_instruction <38h,supp>,dest,src + end macro +end iterate + +macro palignr? dest*,src*,aux* + SSE.parse_operand @dest,dest + SSE.parse_operand @src,src + x86.parse_operand @aux,aux + if @dest.type = 'mmreg' & (@src.type = 'mem' | @src.type = 'mmreg') & @aux.type = 'imm' + if @src.size and not @dest.size + err 'operand sizes do not match' + end if + if @aux.size and not 1 + err 'invalid operand size' + end if + MMX.select_operand_prefix @src,@dest.size + x86.store_instruction <0Fh,3Ah,0Fh>,@src,@dest.rm,1,@aux.imm + else + err 'invalid combination of operands' + end if +end macro diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/vaes.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/vaes.inc new file mode 100644 index 0000000..00e83db --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/vaes.inc @@ -0,0 +1,37 @@ + +include 'aes.inc' + +if defined AVX_512 + + iterate , aesenc,0DCh, aesenclast,0DDh, aesdec,0DEh, aesdeclast,0DFh + + macro v#instr? dest*,src*,src2* + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + AVX_512.parse_operand @src2,src2 + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') + if @dest.size <> @src.size | @src2.size and not @dest.size + err 'operand sizes do not match' + end if + @src2.memsize = 0 + AVX_512.store_instruction @dest.size,VEX_66_0F38_W0,EVEX_AS_VEX+EVEX_VL,opcode,@src2,0,@dest.rm,@src.rm + else + err 'invalid combination of operands' + end if + end macro + + end iterate + +else + + include 'avx.inc' + + iterate , aesenc,0DCh, aesenclast,0DDh, aesdec,0DEh, aesdeclast,0DFh + + macro v#instr? dest*,src*,src2* + AVX.basic_instruction VEX_66_0F38_W0,opcode,0,dest,src,src2 + end macro + + end iterate + +end if diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/vmx.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/vmx.inc new file mode 100644 index 0000000..408857d --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/vmx.inc @@ -0,0 +1,65 @@ + +iterate , vmxon,0F3h,0C7h,6, vmclear,66h,0C7h,6, \ + vmptrld,0,0C7h,6, vmptrst,0,0C7h,7 + + macro instr? src* + x86.parse_operand @src,src + if @src.type = 'mem' + if @src.size and not 8 + err 'invalid operand size' + else + @src.opcode_prefix = prefix + x86.store_instruction <0Fh,ext>,@src,postbyte + end if + else + err 'invalid operand' + end if + end macro + +end iterate + +macro vmxoff? + db 0Fh,1,0C4h +end macro + +macro vmcall? + db 0Fh,1,0C1h +end macro + +macro vmlaunch? + db 0Fh,1,0C2h +end macro + +macro vmresume? + db 0Fh,1,0C3h +end macro + +macro vmread? dest*,src* + x86.parse_operand @dest,dest + x86.parse_operand @src,src + if @src.type = 'reg' & (@dest.type = 'mem' | @dest.type = 'reg') + if (x86.mode < 64 & @src.size <> 4) | (x86.mode = 64 & @src.size <> 8) + err 'invalid operand size' + else if @dest.size and not @src.size + err 'operand sizes do not match' + end if + x86.store_instruction <0Fh,78h>,@dest,@src.rm + else + err 'invalid combination of operands' + end if +end macro + +macro vmwrite? dest*,src* + x86.parse_operand @dest,dest + x86.parse_operand @src,src + if @dest.type = 'reg' & (@src.type = 'mem' | @src.type = 'reg') + if (x86.mode < 64 & @dest.size <> 4) | (x86.mode = 64 & @dest.size <> 8) + err 'invalid operand size' + else if @src.size and not @dest.size + err 'operand sizes do not match' + end if + x86.store_instruction <0Fh,79h>,@src,@dest.rm + else + err 'invalid combination of operands' + end if +end macro diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/vpclmulqdq.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/vpclmulqdq.inc new file mode 100644 index 0000000..3afdcb7 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/vpclmulqdq.inc @@ -0,0 +1,32 @@ + +include 'pclmulqdq.inc' + +if defined AVX_512 + + macro vpclmulqdq? dest*,src*,src2*,aux* + AVX_512.parse_operand @dest,dest + AVX_512.parse_operand @src,src + AVX_512.parse_operand @src2,src2 + x86.parse_operand @aux,aux + if @dest.type = 'mmreg' & @src.type = 'mmreg' & (@src2.type = 'mem' | @src2.type = 'mmreg') & @aux.type = 'imm' + if @aux.size and not 1 + err 'invalid operand size' + else if @dest.size <> @src.size | @src2.size and not @dest.size + err 'operand sizes do not match' + end if + @src2.memsize = 0 + AVX_512.store_instruction @dest.size,VEX_66_0F3A_W0,EVEX_AS_VEX+EVEX_VL,44h,@src2,@dest.mask,@dest.rm,@src.rm,1,@aux.imm + else + err 'invalid combination of operands' + end if + end macro + +else + + include 'avx.inc' + + macro vpclmulqdq? dest*,src*,src2*,imm* + AVX.basic_instruction_imm8 VEX_66_0F3A_W0,44h,0,dest,src,src2,imm + end macro + +end if diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/ext/xsave.inc b/x86_64_sse2_x87/fasm/examples/x86/include/ext/xsave.inc new file mode 100644 index 0000000..945fc12 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/ext/xsave.inc @@ -0,0 +1,35 @@ + +iterate , xsave,4, xrstor,5 + + macro instr? src* + x86.parse_operand @src,src + if @src.type = 'mem' + x86.store_instruction <0Fh,0AEh>,@src,postbyte + else + err 'invalid operand' + end if + end macro + + macro instr#64? src* + if x86.mode = 64 + x86.parse_operand @src,src + if @src.type = 'mem' + x86.select_operand_prefix @src,8 + x86.store_instruction <0Fh,0AEh>,@src,postbyte + else + err 'invalid operand' + end if + else + err 'instruction requires long mode' + end if + end macro + +end iterate + +macro xgetbv? + db 0Fh,1,0D0h +end macro + +macro xsetbv? + db 0Fh,1,0D1h +end macro diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/format/coff.inc b/x86_64_sse2_x87/fasm/examples/x86/include/format/coff.inc new file mode 100644 index 0000000..e8bdde7 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/format/coff.inc @@ -0,0 +1,485 @@ + +macro struct? name + macro end?.struct?! + end namespace + esc end struc + virtual at 0 + name name + sizeof.name = $ + end virtual + purge end?.struct? + end macro + esc struc name + label . : sizeof.name + namespace . +end macro + +struct SCNHDR + s_name db 8 dup ? + s_paddr dd ? + s_vaddr dd ? + s_size dd ? + s_scnptr dd ? + s_relptr dd ? + s_lnnoptr dd ? + s_nreloc dw ? + s_nlnno dw ? + s_flags dd ? +end struct + +SCNHSZ = sizeof SCNHDR + +struct RELOC + r_vaddr dd ? + r_symndx dd ? + r_type dw ? +end struct + +RELSZ = sizeof RELOC + +struct SYMENT + e_name db 8 dup ? + virtual at e_name + e_zeroes dd ? + e_offset dd ? + end virtual + e_value dd ? + e_scnum dw ? + e_type dw ? + e_sclass db ? + e_numaux db ? +end struct + +SYMESZ = sizeof SYMENT + +I386MAGIC = 0x14c + +F_RELFLG = 0x0001 +F_EXEC = 0x0002 +F_LNNO = 0x0004 +F_LSYMS = 0x0008 +F_AR32WR = 0x0100 + +STYP_TEXT = 0x0020 +STYP_DATA = 0x0040 +STYP_BSS = 0x0080 + +N_UNDEF = 0 +N_ABS = -1 +N_DEBUG = -2 + +T_NULL = 0000b +T_VOID = 0001b +T_CHAR = 0010b +T_SHORT = 0011b +T_INT = 0100b +T_LONG = 0101b +T_FLOAT = 0110b +T_DOUBLE = 0111b +T_STRUCT = 1000b +T_UNION = 1001b +T_ENUM = 1010b +T_MOE = 1011b +T_UCHAR = 1100b +T_USHORT = 1101b +T_UINT = 1110b +T_ULONG = 1111b +T_LNGDBL = 01_0000b + +DT_NON = 00b +DT_PTR = 01b +DT_FCN = 10b +DT_ARY = 11b + +C_NULL = 0 +C_AUTO = 1 +C_EXT = 2 +C_STAT = 3 +C_REG = 4 +C_EXTDEF = 5 +C_LABEL = 6 +C_ULABEL = 7 +C_MOS = 8 +C_ARG = 9 +C_STRTAG = 10 +C_MOU = 11 +C_UNTAG = 12 +C_TPDEF = 13 +C_USTATIC = 14 +C_ENTAG = 15 +C_MOE = 16 +C_REGPARM = 17 +C_FIELD = 18 +C_AUTOARG = 19 +C_LASTENT = 20 +C_BLOCK = 100 +C_FCN = 101 +C_EOS = 102 +C_FILE = 103 +C_LINE = 104 +C_ALIAS = 105 +C_HIDDEN = 106 +C_EFCN = 255 + +RELOC_ADDR32 = 6 +RELOC_REL32 = 20 + +COFF:: + +namespace COFF + + Header: + + f_magic dw I386MAGIC + f_nscns dw NUMBER_OF_SECTIONS + f_timdat dd __TIME__ + f_symptr dd SYMBOL_TABLE_OFFSET + f_nsyms dd NUMBER_OF_SYMBOLS + f_opthdr dw 0 + f_flags dw F_AR32WR + F_LNNO + + Sections: db NUMBER_OF_SECTIONS * SCNHSZ dup 0 + + virtual at 0 + symbol_table:: rb NUMBER_OF_SYMBOLS * SYMESZ + end virtual + + virtual at 0 + string_table:: dd STRING_TABLE_SIZE + STRING_POSITION = $ + rb STRING_TABLE_SIZE - $ + end virtual + + virtual at 0 + relocations:: rb NUMBER_OF_RELOCATIONS * RELSZ + end virtual + + element relocatable? + + macro section_start + local sym + element sym : relocatable * (1+SECTION_INDEX) + SYMBOL_INDEX + SECTION_BASE = sym + org sym + if DEFINED_SECTION | DEFAULT_SECTION_PRESENT + store SECTION_NAME : 8 at symbol_table : SYMBOL_INDEX * SYMESZ + SYMENT.e_name + store C_STAT at symbol_table : SYMBOL_INDEX * SYMESZ + SYMENT.e_sclass + store 1+SECTION_INDEX at symbol_table : SYMBOL_INDEX * SYMESZ + SYMENT.e_scnum + SYMBOL_INDEX = SYMBOL_INDEX + 1 + end if + end macro + + RELOCATION_INDEX = 0 + SECTION_INDEX = 0 + SECTION_RELOCATION_INDEX = RELOCATION_INDEX + SYMBOL_INDEX = 0 + + SECTION_OFFSET = $% + SECTION_ALIGN = 4 + SECTION_NAME = '.flat' + SECTION_FLAGS = STYP_TEXT + STYP_DATA + DEFINED_SECTION = 0 + DEFAULT_SECTION_PRESENT = 0 + section_start + +end namespace + +macro section? + namespace COFF + + SECTION_SIZE = $% - SECTION_OFFSET + + if DEFINED_SECTION | SECTION_SIZE > 0 + + if ~ DEFINED_SECTION + DEFAULT_SECTION_PRESENT = 1 + end if + + if $%% = SECTION_OFFSET + SECTION_FLAGS = SECTION_FLAGS or STYP_BSS + SECTION_OFFSET = 0 + section $ + else + UNINITIALIZED_LENGTH = $% - $%% + section $ + db UNINITIALIZED_LENGTH dup 0 + end if + + store SECTION_NAME : 8 at COFF:Sections + SECTION_INDEX * SCNHSZ + SCNHDR.s_name + store SECTION_OFFSET at COFF:Sections + SECTION_INDEX * SCNHSZ + SCNHDR.s_scnptr + store SECTION_SIZE at COFF:Sections + SECTION_INDEX * SCNHSZ + SCNHDR.s_size + store SECTION_FLAGS at COFF:Sections + SECTION_INDEX * SCNHSZ + SCNHDR.s_flags + + if RELOCATION_INDEX > SECTION_RELOCATION_INDEX + store RELOCATION_INDEX - SECTION_RELOCATION_INDEX at COFF:Sections + SECTION_INDEX * SCNHSZ + SCNHDR.s_nreloc + store RELOCATIONS_OFFSET + SECTION_RELOCATION_INDEX * RELSZ at COFF:Sections + SECTION_INDEX * SCNHSZ + SCNHDR.s_relptr + end if + + SECTION_INDEX = SECTION_INDEX + 1 + + end if + + end namespace +end macro + +macro section? declaration* + namespace COFF + + section + + DEFINED_SECTION = 1 + SECTION_FLAGS = 0 + SECTION_OFFSET = $% + SECTION_ALIGN = 4 + + match name attributes, declaration + + SECTION_NAME = name + + local seq,list + define seq attributes + while 1 + match car cdr, seq + define list car + define seq cdr + else + match any, seq + define list any + end match + break + end match + end while + irpv attribute, list + match =code?, attribute + SECTION_FLAGS = SECTION_FLAGS or STYP_TEXT + else match =data?, attribute + SECTION_FLAGS = SECTION_FLAGS or STYP_DATA + else + err 'unknown attribute "',`attribute,'"' + end match + end irpv + + else + + SECTION_NAME = declaration + + end match + + section_start + + SECTION_RELOCATION_INDEX = RELOCATION_INDEX + + end namespace +end macro + +calminstruction align? boundary,value:? + check COFF.SECTION_ALIGN mod (boundary) = 0 + jyes allowed + arrange value, =err 'section not aligned enough' + assemble value + exit + allowed: + compute boundary, (boundary-1)-($-COFF.SECTION_BASE+boundary-1) mod boundary + arrange value, =db boundary =dup value + assemble value +end calminstruction + +macro public? declaration* + namespace COFF + match =static? value =as? str, declaration + SYMBOL_VALUE = value + SYMBOL_NAME = string str + SYMBOL_CLASS = C_STAT + else match value =as? str, declaration + SYMBOL_VALUE = value + SYMBOL_NAME = string str + SYMBOL_CLASS = C_EXT + else match =static? value, declaration + SYMBOL_VALUE = value + SYMBOL_NAME = `value + SYMBOL_CLASS = C_STAT + else + SYMBOL_VALUE = declaration + SYMBOL_NAME = `declaration + SYMBOL_CLASS = C_EXT + end match + if SYMBOL_VALUE relativeto 1 elementof SYMBOL_VALUE & 1 elementof (1 metadataof SYMBOL_VALUE) relativeto relocatable & 1 scaleof (1 metadataof SYMBOL_VALUE) > 0 + SYMBOL_SECTION_INDEX = 1 scaleof (1 metadataof SYMBOL_VALUE) + SYMBOL_VALUE = SYMBOL_VALUE - 1 elementof SYMBOL_VALUE + else + SYMBOL_SECTION_INDEX = N_ABS + end if + if lengthof SYMBOL_NAME > 8 + store 0 at symbol_table : SYMBOL_INDEX * SYMESZ + SYMENT.e_zeroes + store STRING_POSITION at symbol_table : SYMBOL_INDEX * SYMESZ + SYMENT.e_offset + store SYMBOL_NAME : lengthof SYMBOL_NAME at string_table:STRING_POSITION + STRING_POSITION = STRING_POSITION + lengthof SYMBOL_NAME + 1 + else + store SYMBOL_NAME : 8 at symbol_table : SYMBOL_INDEX * SYMESZ + SYMENT.e_name + end if + store SYMBOL_VALUE at symbol_table : SYMBOL_INDEX * SYMESZ + SYMENT.e_value + store SYMBOL_SECTION_INDEX at symbol_table : SYMBOL_INDEX * SYMESZ + SYMENT.e_scnum + store SYMBOL_CLASS at symbol_table : SYMBOL_INDEX * SYMESZ + SYMENT.e_sclass + SYMBOL_INDEX = SYMBOL_INDEX + 1 + end namespace +end macro + +macro extrn? declaration* + namespace COFF + local sym,psym + element sym : relocatable * (-1) + SYMBOL_INDEX + match str =as? name:size, declaration + label name:size at sym + SYMBOL_NAME = string str + else match name:size, declaration + label name:size at sym + SYMBOL_NAME = `name + else match str =as? name, declaration + label name at sym + SYMBOL_NAME = string str + else + label declaration at sym + SYMBOL_NAME = `declaration + end match + if lengthof SYMBOL_NAME > 8 + store 0 at symbol_table : SYMBOL_INDEX * SYMESZ + SYMENT.e_zeroes + store STRING_POSITION at symbol_table : SYMBOL_INDEX * SYMESZ + SYMENT.e_offset + store SYMBOL_NAME : lengthof SYMBOL_NAME at string_table:STRING_POSITION + STRING_POSITION = STRING_POSITION + lengthof SYMBOL_NAME + 1 + else + store SYMBOL_NAME : 8 at symbol_table : SYMBOL_INDEX * SYMESZ + SYMENT.e_name + end if + store C_EXT at symbol_table : SYMBOL_INDEX * SYMESZ + SYMENT.e_sclass + SYMBOL_INDEX = SYMBOL_INDEX + 1 + end namespace +end macro + +calminstruction calminstruction?.init? var*, val:0 + compute val, val + publish var, val +end calminstruction + +calminstruction calminstruction?.initsym? var*, val& + publish var, val +end calminstruction + +calminstruction calminstruction?.unique? name + local counter, buffer + init counter + compute counter, counter + 1 + arrange buffer, name#counter + publish name, buffer +end calminstruction + +calminstruction calminstruction?.asm? line& + local tmp, ln, buffer + initsym tmp, unique ln + assemble tmp + publish ln, line + arrange buffer, =assemble ln + assemble buffer +end calminstruction + +calminstruction dword? value + compute value, value + check ~ value relativeto 0 & value relativeto 1 elementof value & 1 elementof (1 metadataof value) relativeto COFF.relocatable + jyes addr32 + check ~ value relativeto 0 & (value + COFF.SECTION_BASE) relativeto 1 elementof (value + COFF.SECTION_BASE) + jno plain + check 1 elementof (1 metadataof (value + COFF.SECTION_BASE)) relativeto COFF.relocatable + jyes rel32 + plain: + asm emit 4: value + exit + local offset, symndx, type + addr32: + compute symndx, 0 scaleof (1 metadataof value) + compute type, RELOC_ADDR32 + jump add_relocation + rel32: + compute value, value + COFF.SECTION_BASE + compute symndx, 0 scaleof (1 metadataof value) + compute type, RELOC_REL32 + jump add_relocation + add_relocation: + compute offset, $% + asm emit 4: 0 scaleof value + check $% > offset + jno done + compute offset, offset - COFF.SECTION_OFFSET + local reloc + compute reloc, COFF.RELOCATION_INDEX * RELSZ + asm store offset at COFF.relocations : reloc + RELOC.r_vaddr + asm store symndx at COFF.relocations : reloc + RELOC.r_symndx + asm store type at COFF.relocations : reloc + RELOC.r_type + compute COFF.RELOCATION_INDEX, COFF.RELOCATION_INDEX + 1 + done: +end calminstruction + +calminstruction dd? definitions& + local value, n + start: + match value=,definitions, definitions, () + jyes recognize + match value, definitions + arrange definitions, + recognize: + match n =dup? value, value, () + jyes duplicate + match ?, value + jyes reserve + arrange value, =dword value + assemble value + next: + match , definitions + jno start + take , definitions + take definitions, definitions + jyes next + exit + reserve: + arrange value, =dd ? + assemble value + jump next + duplicate: + match (value), value + stack: + check n + jno next + take definitions, value + arrange value, definitions + compute n, n - 1 + jump stack +end calminstruction + +calminstruction (label) dd? definitions& + local cmd + arrange cmd, =label label : =dword + assemble cmd + arrange cmd, =dd definitions + assemble cmd +end calminstruction + +postpone + purge section? + section + namespace COFF + + NUMBER_OF_SECTIONS := SECTION_INDEX + STRING_TABLE_SIZE := STRING_POSITION + NUMBER_OF_SYMBOLS := SYMBOL_INDEX + NUMBER_OF_RELOCATIONS := RELOCATION_INDEX + + RELOCATIONS_OFFSET = $% + load byte_sequence : NUMBER_OF_RELOCATIONS * RELSZ from relocations:0 + db byte_sequence + + SYMBOL_TABLE_OFFSET = $% + load byte_sequence : NUMBER_OF_SYMBOLS * SYMESZ from symbol_table:0 + db byte_sequence + + load byte_sequence : STRING_TABLE_SIZE from string_table:0 + db byte_sequence + + end namespace +end postpone diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/format/coffms.inc b/x86_64_sse2_x87/fasm/examples/x86/include/format/coffms.inc new file mode 100644 index 0000000..150e483 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/format/coffms.inc @@ -0,0 +1,769 @@ + +macro struct? name + macro end?.struct?! + end namespace + esc end struc + virtual at 0 + name name + sizeof.name = $ + end virtual + purge end?.struct? + end macro + esc struc name + label . : sizeof.name + namespace . +end macro + +struct IMAGE_SECTION_HEADER + Name db 8 dup ? + VirtualSize dd ? + VirtualAddress dd ? + SizeOfRawData dd ? + PointerToRawData dd ? + PointerToRelocations dd ? + PointerToLinenumbers dd ? + NumberOfRelocations dw ? + NumberOfLinenumbers dw ? + Characteristics dd ? +end struct + +struct IMAGE_RELOCATION + VirtualAddress dd ? + SymbolTableIndex dd ? + Type dw ? +end struct + +struct IMAGE_SYMBOL + ShortName db 8 dup ? + virtual at ShortName + Zeroes dd ? + Offset dd ? + end virtual + Value dd ? + SectionNumber dw ? + Type dw ? + StorageClass db ? + NumberOfAuxSymbols db ? +end struct + +IMAGE_FILE_MACHINE_UNKNOWN = 0x0 +IMAGE_FILE_MACHINE_AM33 = 0x1D3 +IMAGE_FILE_MACHINE_AMD64 = 0x8664 +IMAGE_FILE_MACHINE_ARM = 0x1C0 +IMAGE_FILE_MACHINE_ARMNT = 0x1C4 +IMAGE_FILE_MACHINE_ARM64 = 0xAA64 +IMAGE_FILE_MACHINE_EBC = 0xEBC +IMAGE_FILE_MACHINE_I386 = 0x14C +IMAGE_FILE_MACHINE_IA64 = 0x200 +IMAGE_FILE_MACHINE_M32R = 0x9041 +IMAGE_FILE_MACHINE_MIPS16 = 0x266 +IMAGE_FILE_MACHINE_MIPSFPU = 0x366 +IMAGE_FILE_MACHINE_MIPSFPU16 = 0x466 +IMAGE_FILE_MACHINE_POWERPC = 0x1F0 +IMAGE_FILE_MACHINE_POWERPCFP = 0x1F1 +IMAGE_FILE_MACHINE_R4000 = 0x166 +IMAGE_FILE_MACHINE_SH3 = 0x1A2 +IMAGE_FILE_MACHINE_SH3DSP = 0x1A3 +IMAGE_FILE_MACHINE_SH4 = 0x1A6 +IMAGE_FILE_MACHINE_SH5 = 0x1A8 +IMAGE_FILE_MACHINE_THUMB = 0x1C2 +IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x169 + +IMAGE_FILE_RELOCS_STRIPPED = 0x0001 +IMAGE_FILE_EXECUTABLE_IMAGE = 0x0002 +IMAGE_FILE_LINE_NUMS_STRIPPED = 0x0004 +IMAGE_FILE_LOCAL_SYMS_STRIPPED = 0x0008 +IMAGE_FILE_AGGRESSIVE_WS_TRIM = 0x0010 +IMAGE_FILE_LARGE_ADDRESS_AWARE = 0x0020 +IMAGE_FILE_BYTES_REVERSED_LO = 0x0080 +IMAGE_FILE_32BIT_MACHINE = 0x0100 +IMAGE_FILE_DEBUG_STRIPPED = 0x0200 +IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP = 0x0400 +IMAGE_FILE_NET_RUN_FROM_SWAP = 0x0800 +IMAGE_FILE_SYSTEM = 0x1000 +IMAGE_FILE_DLL = 0x2000 +IMAGE_FILE_UP_SYSTEM_ONLY = 0x4000 +IMAGE_FILE_BYTES_REVERSED_HI = 0x8000 + +IMAGE_SCN_TYPE_NO_PAD = 0x00000008 +IMAGE_SCN_CNT_CODE = 0x00000020 +IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040 +IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080 +IMAGE_SCN_LNK_OTHER = 0x00000100 +IMAGE_SCN_LNK_INFO = 0x00000200 +IMAGE_SCN_LNK_REMOVE = 0x00000800 +IMAGE_SCN_LNK_COMDAT = 0x00001000 +IMAGE_SCN_GPREL = 0x00008000 +IMAGE_SCN_MEM_PURGEABLE = 0x00020000 +IMAGE_SCN_MEM_16BIT = 0x00020000 +IMAGE_SCN_MEM_LOCKED = 0x00040000 +IMAGE_SCN_MEM_PRELOAD = 0x00080000 +IMAGE_SCN_ALIGN_1BYTES = 0x00100000 +IMAGE_SCN_ALIGN_2BYTES = 0x00200000 +IMAGE_SCN_ALIGN_4BYTES = 0x00300000 +IMAGE_SCN_ALIGN_8BYTES = 0x00400000 +IMAGE_SCN_ALIGN_16BYTES = 0x00500000 +IMAGE_SCN_ALIGN_32BYTES = 0x00600000 +IMAGE_SCN_ALIGN_64BYTES = 0x00700000 +IMAGE_SCN_ALIGN_128BYTES = 0x00800000 +IMAGE_SCN_ALIGN_256BYTES = 0x00900000 +IMAGE_SCN_ALIGN_512BYTES = 0x00A00000 +IMAGE_SCN_ALIGN_1024BYTES = 0x00B00000 +IMAGE_SCN_ALIGN_2048BYTES = 0x00C00000 +IMAGE_SCN_ALIGN_4096BYTES = 0x00D00000 +IMAGE_SCN_ALIGN_8192BYTES = 0x00E00000 +IMAGE_SCN_LNK_NRELOC_OVFL = 0x01000000 +IMAGE_SCN_MEM_DISCARDABLE = 0x02000000 +IMAGE_SCN_MEM_NOT_CACHED = 0x04000000 +IMAGE_SCN_MEM_NOT_PAGED = 0x08000000 +IMAGE_SCN_MEM_SHARED = 0x10000000 +IMAGE_SCN_MEM_EXECUTE = 0x20000000 +IMAGE_SCN_MEM_READ = 0x40000000 +IMAGE_SCN_MEM_WRITE = 0x80000000 + +IMAGE_REL_AMD64_ABSOLUTE = 0x0000 +IMAGE_REL_AMD64_ADDR64 = 0x0001 +IMAGE_REL_AMD64_ADDR32 = 0x0002 +IMAGE_REL_AMD64_ADDR32NB = 0x0003 +IMAGE_REL_AMD64_REL32 = 0x0004 +IMAGE_REL_AMD64_REL32_1 = 0x0005 +IMAGE_REL_AMD64_REL32_2 = 0x0006 +IMAGE_REL_AMD64_REL32_3 = 0x0007 +IMAGE_REL_AMD64_REL32_4 = 0x0008 +IMAGE_REL_AMD64_REL32_5 = 0x0009 +IMAGE_REL_AMD64_SECTION = 0x000A +IMAGE_REL_AMD64_SECREL = 0x000B +IMAGE_REL_AMD64_SECREL7 = 0x000C +IMAGE_REL_AMD64_TOKEN = 0x000D +IMAGE_REL_AMD64_SREL32 = 0x000E +IMAGE_REL_AMD64_PAIR = 0x000F +IMAGE_REL_AMD64_SSPAN32 = 0x0010 + +IMAGE_REL_I386_ABSOLUTE = 0x0000 +IMAGE_REL_I386_DIR16 = 0x0001 +IMAGE_REL_I386_REL16 = 0x0002 +IMAGE_REL_I386_DIR32 = 0x0006 +IMAGE_REL_I386_DIR32NB = 0x0007 +IMAGE_REL_I386_SEG12 = 0x0009 +IMAGE_REL_I386_SECTION = 0x000A +IMAGE_REL_I386_SECREL = 0x000B +IMAGE_REL_I386_TOKEN = 0x000C +IMAGE_REL_I386_SECREL7 = 0x000D +IMAGE_REL_I386_REL32 = 0x0014 + +IMAGE_SYM_UNDEFINED = 0 +IMAGE_SYM_ABSOLUTE = -1 +IMAGE_SYM_DEBUG = -2 + +IMAGE_SYM_TYPE_NULL = 0 +IMAGE_SYM_TYPE_VOID = 1 +IMAGE_SYM_TYPE_CHAR = 2 +IMAGE_SYM_TYPE_SHORT = 3 +IMAGE_SYM_TYPE_INT = 4 +IMAGE_SYM_TYPE_LONG = 5 +IMAGE_SYM_TYPE_FLOAT = 6 +IMAGE_SYM_TYPE_DOUBLE = 7 +IMAGE_SYM_TYPE_STRUCT = 8 +IMAGE_SYM_TYPE_UNION = 9 +IMAGE_SYM_TYPE_ENUM = 10 +IMAGE_SYM_TYPE_MOE = 11 +IMAGE_SYM_TYPE_BYTE = 12 +IMAGE_SYM_TYPE_WORD = 13 +IMAGE_SYM_TYPE_UINT = 14 +IMAGE_SYM_TYPE_DWORD = 15 + +IMAGE_SYM_DTYPE_NULL = 0 +IMAGE_SYM_DTYPE_POINTER = 1 +IMAGE_SYM_DTYPE_FUNCTION = 2 +IMAGE_SYM_DTYPE_ARRAY = 3 + +IMAGE_SYM_CLASS_END_OF_FUNCTION = -1 +IMAGE_SYM_CLASS_NULL = 0 +IMAGE_SYM_CLASS_AUTOMATIC = 1 +IMAGE_SYM_CLASS_EXTERNAL = 2 +IMAGE_SYM_CLASS_STATIC = 3 +IMAGE_SYM_CLASS_REGISTER = 4 +IMAGE_SYM_CLASS_EXTERNAL_DEF = 5 +IMAGE_SYM_CLASS_LABEL = 6 +IMAGE_SYM_CLASS_UNDEFINED_LABEL = 7 +IMAGE_SYM_CLASS_MEMBER_OF_STRUCT = 8 +IMAGE_SYM_CLASS_ARGUMENT = 9 +IMAGE_SYM_CLASS_STRUCT_TAG = 10 +IMAGE_SYM_CLASS_MEMBER_OF_UNION = 11 +IMAGE_SYM_CLASS_UNION_TAG = 12 +IMAGE_SYM_CLASS_TYPE_DEFINITION = 13 +IMAGE_SYM_CLASS_UNDEFINED_STATIC = 14 +IMAGE_SYM_CLASS_ENUM_TAG = 15 +IMAGE_SYM_CLASS_MEMBER_OF_ENUM = 16 +IMAGE_SYM_CLASS_REGISTER_PARAM = 17 +IMAGE_SYM_CLASS_BIT_FIELD = 18 +IMAGE_SYM_CLASS_BLOCK = 100 +IMAGE_SYM_CLASS_FUNCTION = 101 +IMAGE_SYM_CLASS_END_OF_STRUCT = 102 +IMAGE_SYM_CLASS_FILE = 103 +IMAGE_SYM_CLASS_SECTION = 104 +IMAGE_SYM_CLASS_WEAK_EXTERNAL = 105 +IMAGE_SYM_CLASS_CLR_TOKEN = 107 + +IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY = 1 +IMAGE_WEAK_EXTERN_SEARCH_LIBRARY = 2 +IMAGE_WEAK_EXTERN_SEARCH_ALIAS = 3 + +COFF:: + +namespace COFF + + if defined Settings.Machine + MACHINE := Settings.Machine + else + MACHINE := IMAGE_FILE_MACHINE_I386 + end if + + if defined Settings.Characteristics + CHARACTERISTICS = Settings.Characteristics + else + CHARACTERISTICS = IMAGE_FILE_32BIT_MACHINE + end if + + Header: + + Machine dw MACHINE + NumberOfSections dw NUMBER_OF_SECTIONS + TimeDateStamp dd __TIME__ + PointerToSymbolTable dd SYMBOL_TABLE_OFFSET + NumberOfSymbols dd NUMBER_OF_SYMBOLS + SizeOfOptionalHeader dw 0 + Characteristics dw CHARACTERISTICS + + Sections: db NUMBER_OF_SECTIONS * sizeof IMAGE_SECTION_HEADER dup 0 + + virtual at 0 + symbol_table:: rb NUMBER_OF_SYMBOLS * sizeof IMAGE_SYMBOL + end virtual + + virtual at 0 + string_table:: dd STRING_TABLE_SIZE + STRING_POSITION = $ + rb STRING_TABLE_SIZE - $ + end virtual + + virtual at 0 + relocations:: rb NUMBER_OF_RELOCATIONS * sizeof IMAGE_RELOCATION + end virtual + + element relocatable? + + macro section_start + local sym + element sym : relocatable * (1+SECTION_INDEX) + SYMBOL_INDEX + SECTION_BASE = sym + org sym + if DEFINED_SECTION | DEFAULT_SECTION_PRESENT + store SECTION_NAME : 8 at symbol_table : SYMBOL_INDEX * sizeof IMAGE_SYMBOL + IMAGE_SYMBOL.ShortName + store IMAGE_SYM_CLASS_STATIC at symbol_table : SYMBOL_INDEX * sizeof IMAGE_SYMBOL + IMAGE_SYMBOL.StorageClass + store 1+SECTION_INDEX at symbol_table : SYMBOL_INDEX * sizeof IMAGE_SYMBOL + IMAGE_SYMBOL.SectionNumber + SYMBOL_INDEX = SYMBOL_INDEX + 1 + end if + end macro + + RELOCATION_INDEX = 0 + SECTION_INDEX = 0 + SECTION_RELOCATION_INDEX = RELOCATION_INDEX + SYMBOL_INDEX = 0 + + SECTION_OFFSET = $% + SECTION_ALIGN = 4 + SECTION_NAME = '.flat' + SECTION_FLAGS = IMAGE_SCN_CNT_CODE + IMAGE_SCN_CNT_INITIALIZED_DATA + DEFINED_SECTION = 0 + DEFAULT_SECTION_PRESENT = 0 + section_start + +end namespace + +macro section? + namespace COFF + + SECTION_SIZE = $% - SECTION_OFFSET + + if DEFINED_SECTION | SECTION_SIZE > 0 + + if ~ DEFINED_SECTION + DEFAULT_SECTION_PRESENT = 1 + end if + + if $%% = SECTION_OFFSET + SECTION_FLAGS = SECTION_FLAGS or IMAGE_SCN_CNT_UNINITIALIZED_DATA + SECTION_OFFSET = 0 + section $ + else + UNINITIALIZED_LENGTH = $% - $%% + section $ + db UNINITIALIZED_LENGTH dup 0 + end if + + if RELOCATION_INDEX > SECTION_RELOCATION_INDEX + store RELOCATIONS_OFFSET + SECTION_RELOCATION_INDEX * sizeof IMAGE_RELOCATION at COFF:Sections + SECTION_INDEX * sizeof IMAGE_SECTION_HEADER + IMAGE_SECTION_HEADER.PointerToRelocations + if RELOCATION_INDEX - SECTION_RELOCATION_INDEX > 65535 + SECTION_FLAGS = SECTION_FLAGS or IMAGE_SCN_LNK_NRELOC_OVFL + store 0FFFFh at COFF:Sections + SECTION_INDEX * sizeof IMAGE_SECTION_HEADER + IMAGE_SECTION_HEADER.NumberOfRelocations + load RELOCATIONS : (RELOCATION_INDEX - SECTION_RELOCATION_INDEX) * sizeof IMAGE_RELOCATION from relocations : SECTION_RELOCATION_INDEX * sizeof IMAGE_RELOCATION + store RELOCATIONS : (RELOCATION_INDEX - SECTION_RELOCATION_INDEX) * sizeof IMAGE_RELOCATION at relocations : (SECTION_RELOCATION_INDEX+1) * sizeof IMAGE_RELOCATION + RELOCATION_INDEX = RELOCATION_INDEX + 1 + store RELOCATION_INDEX - SECTION_RELOCATION_INDEX at relocations : SECTION_RELOCATION_INDEX * sizeof IMAGE_RELOCATION + IMAGE_RELOCATION.VirtualAddress + store 0 at relocations : SECTION_RELOCATION_INDEX * sizeof IMAGE_RELOCATION + IMAGE_RELOCATION.SymbolTableIndex + store 0 at relocations : SECTION_RELOCATION_INDEX * sizeof IMAGE_RELOCATION + IMAGE_RELOCATION.Type + else + store RELOCATION_INDEX - SECTION_RELOCATION_INDEX at COFF:Sections + SECTION_INDEX * sizeof IMAGE_SECTION_HEADER + IMAGE_SECTION_HEADER.NumberOfRelocations + end if + end if + + store SECTION_NAME : 8 at COFF:Sections + SECTION_INDEX * sizeof IMAGE_SECTION_HEADER + IMAGE_SECTION_HEADER.Name + store SECTION_OFFSET at COFF:Sections + SECTION_INDEX * sizeof IMAGE_SECTION_HEADER + IMAGE_SECTION_HEADER.PointerToRawData + store SECTION_SIZE at COFF:Sections + SECTION_INDEX * sizeof IMAGE_SECTION_HEADER + IMAGE_SECTION_HEADER.SizeOfRawData + store SECTION_FLAGS at COFF:Sections + SECTION_INDEX * sizeof IMAGE_SECTION_HEADER + IMAGE_SECTION_HEADER.Characteristics + + SECTION_INDEX = SECTION_INDEX + 1 + + end if + + end namespace +end macro + +macro section? declaration* + namespace COFF + + section + + DEFINED_SECTION = 1 + SECTION_FLAGS = 0 + SECTION_OFFSET = $% + SECTION_ALIGN = 4 + + match name attributes, declaration + + SECTION_NAME = name + + local seq,list + match flags =align? boundary, attributes + SECTION_ALIGN = boundary + define seq flags + else match =align? boundary, attributes + SECTION_ALIGN = boundary + define seq + else + define seq attributes + end match + while 1 + match car cdr, seq + define list car + define seq cdr + else + match any, seq + define list any + end match + break + end match + end while + irpv attribute, list + match =code?, attribute + SECTION_FLAGS = SECTION_FLAGS or IMAGE_SCN_CNT_CODE + else match =data?, attribute + SECTION_FLAGS = SECTION_FLAGS or IMAGE_SCN_CNT_INITIALIZED_DATA + else match =readable?, attribute + SECTION_FLAGS = SECTION_FLAGS or IMAGE_SCN_MEM_READ + else match =writeable?, attribute + SECTION_FLAGS = SECTION_FLAGS or IMAGE_SCN_MEM_WRITE + else match =executable?, attribute + SECTION_FLAGS = SECTION_FLAGS or IMAGE_SCN_MEM_EXECUTE + else match =shareable?, attribute + SECTION_FLAGS = SECTION_FLAGS or IMAGE_SCN_MEM_SHARED + else match =discardable?, attribute + SECTION_FLAGS = SECTION_FLAGS or IMAGE_SCN_MEM_DISCARDABLE + else match =notpageable?, attribute + SECTION_FLAGS = SECTION_FLAGS or IMAGE_SCN_MEM_NOT_PAGED + else match =linkremove?, attribute + SECTION_FLAGS = SECTION_FLAGS or IMAGE_SCN_LNK_REMOVE + else match =linkinfo?, attribute + SECTION_FLAGS = SECTION_FLAGS or IMAGE_SCN_LNK_INFO + else + err 'unknown attribute "',`attribute,'"' + end match + end irpv + + else + + SECTION_NAME = declaration + + end match + + repeat 1, i:SECTION_ALIGN + if defined IMAGE_SCN_ALIGN_#i#BYTES + SECTION_FLAGS = SECTION_FLAGS or IMAGE_SCN_ALIGN_#i#BYTES + else + err 'invalid section alignment' + end if + end repeat + + section_start + + SECTION_RELOCATION_INDEX = RELOCATION_INDEX + + end namespace +end macro + +calminstruction align? boundary,value:? + check COFF.SECTION_ALIGN mod (boundary) = 0 + jyes allowed + arrange value, =err 'section not aligned enough' + assemble value + exit + allowed: + compute boundary, (boundary-1)-($-COFF.SECTION_BASE+boundary-1) mod boundary + arrange value, =db boundary =dup value + assemble value +end calminstruction + +macro public? declaration* + namespace COFF + match =static? value =as? str, declaration + SYMBOL_VALUE = value + SYMBOL_NAME = string str + SYMBOL_CLASS = IMAGE_SYM_CLASS_STATIC + else match value =as? str, declaration + SYMBOL_VALUE = value + SYMBOL_NAME = string str + SYMBOL_CLASS = IMAGE_SYM_CLASS_EXTERNAL + else match =static? value, declaration + SYMBOL_VALUE = value + SYMBOL_NAME = `value + SYMBOL_CLASS = IMAGE_SYM_CLASS_STATIC + else + SYMBOL_VALUE = declaration + SYMBOL_NAME = `declaration + SYMBOL_CLASS = IMAGE_SYM_CLASS_EXTERNAL + end match + if SYMBOL_VALUE relativeto 1 elementof SYMBOL_VALUE & 1 elementof (1 metadataof SYMBOL_VALUE) relativeto relocatable & SYMBOL_CLASS = IMAGE_SYM_CLASS_EXTERNAL + if 1 scaleof (1 metadataof SYMBOL_VALUE) > 0 + SYMBOL_SECTION_INDEX = 1 scaleof (1 metadataof SYMBOL_VALUE) + SYMBOL_VALUE = SYMBOL_VALUE - 1 elementof SYMBOL_VALUE + else + SYMBOL_SECTION_INDEX = IMAGE_SYM_UNDEFINED + SYMBOL_VALUE = 0 + SYMBOL_CLASS = IMAGE_SYM_CLASS_WEAK_EXTERNAL + SYMBOL_TAG = 0 scaleof (1 metadataof SYMBOL_VALUE) + end if + else + SYMBOL_SECTION_INDEX = IMAGE_SYM_ABSOLUTE + end if + if lengthof SYMBOL_NAME > 8 + store 0 at symbol_table : SYMBOL_INDEX * sizeof IMAGE_SYMBOL + IMAGE_SYMBOL.Zeroes + store STRING_POSITION at symbol_table : SYMBOL_INDEX * sizeof IMAGE_SYMBOL + IMAGE_SYMBOL.Offset + store SYMBOL_NAME : lengthof SYMBOL_NAME at string_table:STRING_POSITION + STRING_POSITION = STRING_POSITION + lengthof SYMBOL_NAME + 1 + else + store SYMBOL_NAME : 8 at symbol_table : SYMBOL_INDEX * sizeof IMAGE_SYMBOL + IMAGE_SYMBOL.ShortName + end if + store SYMBOL_VALUE at symbol_table : SYMBOL_INDEX * sizeof IMAGE_SYMBOL + IMAGE_SYMBOL.Value + store SYMBOL_SECTION_INDEX at symbol_table : SYMBOL_INDEX * sizeof IMAGE_SYMBOL + IMAGE_SYMBOL.SectionNumber + store SYMBOL_CLASS at symbol_table : SYMBOL_INDEX * sizeof IMAGE_SYMBOL + IMAGE_SYMBOL.StorageClass + if SYMBOL_CLASS = IMAGE_SYM_CLASS_WEAK_EXTERNAL + store 1 at symbol_table : SYMBOL_INDEX * sizeof IMAGE_SYMBOL + IMAGE_SYMBOL.NumberOfAuxSymbols + SYMBOL_INDEX = SYMBOL_INDEX + 1 + store SYMBOL_TAG : 4 at symbol_table : SYMBOL_INDEX * sizeof IMAGE_SYMBOL + store IMAGE_WEAK_EXTERN_SEARCH_ALIAS : 4 at symbol_table : SYMBOL_INDEX * sizeof IMAGE_SYMBOL + 4 + end if + SYMBOL_INDEX = SYMBOL_INDEX + 1 + end namespace +end macro + +macro extrn? declaration* + namespace COFF + local sym,psym + element sym : relocatable * (-1) + SYMBOL_INDEX + match str =as? name:size, declaration + label name:size at sym + SYMBOL_NAME = string str + else match name:size, declaration + label name:size at sym + SYMBOL_NAME = `name + else match str =as? name, declaration + label name at sym + SYMBOL_NAME = string str + else + label declaration at sym + SYMBOL_NAME = `declaration + end match + if lengthof SYMBOL_NAME > 8 + store 0 at symbol_table : SYMBOL_INDEX * sizeof IMAGE_SYMBOL + IMAGE_SYMBOL.Zeroes + store STRING_POSITION at symbol_table : SYMBOL_INDEX * sizeof IMAGE_SYMBOL + IMAGE_SYMBOL.Offset + store SYMBOL_NAME : lengthof SYMBOL_NAME at string_table:STRING_POSITION + STRING_POSITION = STRING_POSITION + lengthof SYMBOL_NAME + 1 + else + store SYMBOL_NAME : 8 at symbol_table : SYMBOL_INDEX * sizeof IMAGE_SYMBOL + IMAGE_SYMBOL.ShortName + end if + store IMAGE_SYM_CLASS_EXTERNAL at symbol_table : SYMBOL_INDEX * sizeof IMAGE_SYMBOL + IMAGE_SYMBOL.StorageClass + SYMBOL_INDEX = SYMBOL_INDEX + 1 + end namespace +end macro + +element COFF.IMAGE_BASE +RVA? equ -COFF.IMAGE_BASE+ + +calminstruction calminstruction?.init? var*, val:0 + compute val, val + publish var, val +end calminstruction + +calminstruction calminstruction?.initsym? var*, val& + publish var, val +end calminstruction + +calminstruction calminstruction?.unique? name + local counter, buffer + init counter + compute counter, counter + 1 + arrange buffer, name#counter + publish name, buffer +end calminstruction + +calminstruction calminstruction?.asm? line& + local tmp, ln, buffer + initsym tmp, unique ln + assemble tmp + publish ln, line + arrange buffer, =assemble ln + assemble buffer +end calminstruction + +if COFF.MACHINE = IMAGE_FILE_MACHINE_I386 + + calminstruction dword? value + compute value, value + check ~ value relativeto 0 & value relativeto 1 elementof value & 1 elementof (1 metadataof value) relativeto COFF.relocatable + jyes dir32 + check ~ value relativeto 0 & (value + COFF.IMAGE_BASE) relativeto 1 elementof (value + COFF.IMAGE_BASE) & 1 elementof (1 metadataof (value + COFF.IMAGE_BASE)) relativeto COFF.relocatable + jyes dir32nb + check ~ value relativeto 0 & (value + COFF.SECTION_BASE) relativeto 1 elementof (value + COFF.SECTION_BASE) + jno plain + check 1 elementof (1 metadataof (value + COFF.SECTION_BASE)) relativeto COFF.relocatable + jyes rel32 + plain: + asm emit 4: value + exit + local offset, symndx, type + dir32: + compute symndx, 0 scaleof (1 metadataof value) + compute type, IMAGE_REL_I386_DIR32 + jump add_relocation + dir32nb: + compute value, value + COFF.IMAGE_BASE + compute symndx, 0 scaleof (1 metadataof value) + compute type, IMAGE_REL_I386_DIR32NB + jump add_relocation + rel32: + compute value, value + (COFF.SECTION_BASE + $% + 4 - COFF.SECTION_OFFSET) + compute symndx, 0 scaleof (1 metadataof value) + compute type, IMAGE_REL_I386_REL32 + jump add_relocation + add_relocation: + compute offset, $% + asm emit 4: 0 scaleof value + check $% > offset + jno done + compute offset, offset - COFF.SECTION_OFFSET + local relocation + compute relocation, COFF.RELOCATION_INDEX * sizeof IMAGE_RELOCATION + asm store offset at COFF.relocations : relocation + IMAGE_RELOCATION.VirtualAddress + asm store symndx at COFF.relocations : relocation + IMAGE_RELOCATION.SymbolTableIndex + asm store type at COFF.relocations : relocation + IMAGE_RELOCATION.Type + compute COFF.RELOCATION_INDEX, COFF.RELOCATION_INDEX + 1 + done: + end calminstruction + + calminstruction qword? value + compute value, value + check ~ value relativeto 0 & value relativeto 1 elementof value & 1 elementof (1 metadataof value) relativeto COFF.relocatable + jyes dir32 + check ~ value relativeto 0 & (value + COFF.IMAGE_BASE) relativeto 1 elementof (value + COFF.IMAGE_BASE) & 1 elementof (1 metadataof (value + COFF.IMAGE_BASE)) relativeto COFF.relocatable + jyes dir32nb + plain: + asm emit 8: value + exit + local offset, symndx, type + dir32: + compute symndx, 0 scaleof (1 metadataof value) + compute type, IMAGE_REL_I386_DIR32 + jump add_relocation + dir32nb: + compute value, value + COFF.IMAGE_BASE + compute symndx, 0 scaleof (1 metadataof value) + compute type, IMAGE_REL_I386_DIR32NB + jump add_relocation + add_relocation: + compute offset, $% + asm emit 8: 0 scaleof value + check $% > offset + jno done + compute offset, offset - COFF.SECTION_OFFSET + local relocation + compute relocation, COFF.RELOCATION_INDEX * sizeof IMAGE_RELOCATION + asm store offset at COFF.relocations : relocation + IMAGE_RELOCATION.VirtualAddress + asm store symndx at COFF.relocations : relocation + IMAGE_RELOCATION.SymbolTableIndex + asm store type at COFF.relocations : relocation + IMAGE_RELOCATION.Type + compute COFF.RELOCATION_INDEX, COFF.RELOCATION_INDEX + 1 + done: + end calminstruction + +else if COFF.MACHINE = IMAGE_FILE_MACHINE_AMD64 + + calminstruction dword? value + compute value, value + check ~ value relativeto 0 & value relativeto 1 elementof value & 1 elementof (1 metadataof value) relativeto COFF.relocatable + jyes addr32 + check ~ value relativeto 0 & (value + COFF.IMAGE_BASE) relativeto 1 elementof (value + COFF.IMAGE_BASE) & 1 elementof (1 metadataof (value + COFF.IMAGE_BASE)) relativeto COFF.relocatable + jyes addr32nb + check ~ value relativeto 0 & (value + COFF.SECTION_BASE) relativeto 1 elementof (value + COFF.SECTION_BASE) + jno plain + check 1 elementof (1 metadataof (value + COFF.SECTION_BASE)) relativeto COFF.relocatable + jyes rel32 + plain: + asm emit 4: value + exit + local offset, symndx, type + addr32: + compute symndx, 0 scaleof (1 metadataof value) + compute type, IMAGE_REL_AMD64_ADDR32 + jump add_relocation + addr32nb: + compute value, value + COFF.IMAGE_BASE + compute symndx, 0 scaleof (1 metadataof value) + compute type, IMAGE_REL_AMD64_ADDR32NB + jump add_relocation + rel32: + compute value, value + (COFF.SECTION_BASE + $% + 4 - COFF.SECTION_OFFSET) + compute symndx, 0 scaleof (1 metadataof value) + compute type, IMAGE_REL_AMD64_REL32 + jump add_relocation + add_relocation: + compute offset, $% + asm emit 4: 0 scaleof value + check $% > offset + jno done + compute offset, offset - COFF.SECTION_OFFSET + local relocation + compute relocation, COFF.RELOCATION_INDEX * sizeof IMAGE_RELOCATION + asm store offset at COFF.relocations : relocation + IMAGE_RELOCATION.VirtualAddress + asm store symndx at COFF.relocations : relocation + IMAGE_RELOCATION.SymbolTableIndex + asm store type at COFF.relocations : relocation + IMAGE_RELOCATION.Type + compute COFF.RELOCATION_INDEX, COFF.RELOCATION_INDEX + 1 + done: + end calminstruction + + calminstruction qword? value + compute value, value + check ~ value relativeto 0 & value relativeto 1 elementof value & 1 elementof (1 metadataof value) relativeto COFF.relocatable + jyes addr64 + check ~ value relativeto 0 & (value + COFF.IMAGE_BASE) relativeto 1 elementof (value + COFF.IMAGE_BASE) & 1 elementof (1 metadataof (value + COFF.IMAGE_BASE)) relativeto COFF.relocatable + jyes addr64nb + plain: + asm emit 8: value + exit + local offset, symndx, type + addr64: + compute symndx, 0 scaleof (1 metadataof value) + compute type, IMAGE_REL_AMD64_ADDR64 + jump add_relocation + addr64nb: + compute value, value + COFF.IMAGE_BASE + compute symndx, 0 scaleof (1 metadataof value) + compute type, IMAGE_REL_AMD64_ADDR64NB + jump add_relocation + add_relocation: + compute offset, $% + asm emit 8: 0 scaleof value + check $% > offset + jno done + compute offset, offset - COFF.SECTION_OFFSET + local relocation + compute relocation, COFF.RELOCATION_INDEX * sizeof IMAGE_RELOCATION + asm store offset at COFF.relocations : relocation + IMAGE_RELOCATION.VirtualAddress + asm store symndx at COFF.relocations : relocation + IMAGE_RELOCATION.SymbolTableIndex + asm store type at COFF.relocations : relocation + IMAGE_RELOCATION.Type + compute COFF.RELOCATION_INDEX, COFF.RELOCATION_INDEX + 1 + done: + end calminstruction + +end if + +iterate , dd,dword, dq,qword + + calminstruction dd? definitions& + local value, n + start: + match value=,definitions, definitions, () + jyes recognize + match value, definitions + arrange definitions, + recognize: + match n =dup? value, value, () + jyes duplicate + match ?, value + jyes reserve + arrange value, =dword value + assemble value + next: + match , definitions + jno start + take , definitions + take definitions, definitions + jyes next + exit + reserve: + arrange value, =dd ? + assemble value + jump next + duplicate: + match (value), value + stack: + check n + jno next + take definitions, value + arrange value, definitions + compute n, n - 1 + jump stack + end calminstruction + + calminstruction (label) dd? definitions& + local cmd + arrange cmd, =label label : =dword + assemble cmd + arrange cmd, =dd definitions + assemble cmd + end calminstruction + +end iterate + +postpone + purge section? + section + namespace COFF + + NUMBER_OF_SECTIONS := SECTION_INDEX + STRING_TABLE_SIZE := STRING_POSITION + NUMBER_OF_SYMBOLS := SYMBOL_INDEX + NUMBER_OF_RELOCATIONS := RELOCATION_INDEX + + RELOCATIONS_OFFSET = $% + load byte_sequence : NUMBER_OF_RELOCATIONS * sizeof IMAGE_RELOCATION from relocations:0 + db byte_sequence + + SYMBOL_TABLE_OFFSET = $% + load byte_sequence : NUMBER_OF_SYMBOLS * sizeof IMAGE_SYMBOL from symbol_table:0 + db byte_sequence + + load byte_sequence : STRING_TABLE_SIZE from string_table:0 + db byte_sequence + + end namespace +end postpone diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/format/elf32.inc b/x86_64_sse2_x87/fasm/examples/x86/include/format/elf32.inc new file mode 100644 index 0000000..c509a52 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/format/elf32.inc @@ -0,0 +1,654 @@ + +macro struct? name + macro end?.struct?! + end namespace + esc end struc + virtual at 0 + name name + sizeof.name = $ + end virtual + purge end?.struct? + end macro + esc struc name + label . : sizeof.name + namespace . +end macro + +struct Elf32_Shdr + sh_name dd ? + sh_type dd ? + sh_flags dd ? + sh_addr dd ? + sh_offset dd ? + sh_size dd ? + sh_link dd ? + sh_info dd ? + sh_addralign dd ? + sh_entsize dd ? +end struct + +struct Elf32_Sym + st_name dd ? + st_value dd ? + st_size dd ? + st_info db ? + st_other db ? + st_shndx dw ? +end struct + +struct Elf32_Rel + r_offset dd ? + r_info dd ? +end struct + +struct Elf32_Rela + r_offset dd ? + r_info dd ? + r_addend dd ? +end struct + +struct Elf32_Phdr + p_type dd ? + p_offset dd ? + p_vaddr dd ? + p_paddr dd ? + p_filesz dd ? + p_memsz dd ? + p_flags dd ? + p_align dd ? +end struct + +purge struct? + +ELFCLASSNONE = 0 +ELFCLASS32 = 1 +ELFCLASS64 = 2 + +ELFDATANONE = 0 +ELFDATA2LSB = 1 +ELFDATA2MSB = 2 + +ELFOSABI_NONE = 0 +ELFOSABI_HPUX = 1 +ELFOSABI_NETBSD = 2 +ELFOSABI_GNU = 3 +ELFOSABI_LINUX = 3 +ELFOSABI_SOLARIS = 6 +ELFOSABI_AIX = 7 +ELFOSABI_IRIX = 8 +ELFOSABI_FREEBSD = 9 +ELFOSABI_TRU64 = 10 +ELFOSABI_MODESTO = 11 +ELFOSABI_OPENBSD = 12 +ELFOSABI_OPENVMS = 13 +ELFOSABI_NSK = 14 +ELFOSABI_AROS = 15 +ELFOSABI_FENIXOS = 16 +ELFOSABI_CLOUDABI = 17 +ELFOSABI_OPENVOS = 18 + +ET_NONE = 0 +ET_REL = 1 +ET_EXEC = 2 +ET_DYN = 3 +ET_CORE = 4 +ET_LOPROC = 0xff00 +ET_HIPROC = 0xffff + +EM_NONE = 0 +EM_M32 = 1 +EM_SPARC = 2 +EM_386 = 3 +EM_68K = 4 +EM_88K = 5 +EM_860 = 7 +EM_MIPS = 8 +EM_X86_64 = 62 + +EV_NONE = 0 +EV_CURRENT = 1 + +SHN_UNDEF = 0 +SHN_LORESERVE = 0xff00 +SHN_LOPROC = 0xff00 +SHN_HIPROC = 0xff1f +SHN_ABS = 0xfff1 +SHN_COMMON = 0xfff2 +SHN_HIRESERVE = 0xffff + +SHT_NULL = 0 +SHT_PROGBITS = 1 +SHT_SYMTAB = 2 +SHT_STRTAB = 3 +SHT_RELA = 4 +SHT_HASH = 5 +SHT_DYNAMIC = 6 +SHT_NOTE = 7 +SHT_NOBITS = 8 +SHT_REL = 9 +SHT_SHLIB = 10 +SHT_DYNSYM = 11 +SHT_LOPROC = 0x70000000 +SHT_HIPROC = 0x7fffffff +SHT_LOUSER = 0x80000000 +SHT_HIUSER = 0xffffffff + +SHF_WRITE = 0x1 +SHF_ALLOC = 0x2 +SHF_EXECINSTR = 0x4 +SHF_MASKPROC = 0xf0000000 + +STT_NOTYPE = 0 +STT_OBJECT = 1 +STT_FUNC = 2 +STT_SECTION = 3 +STT_FILE = 4 +STT_LOPROC = 13 +STT_HIPROC = 15 + +STB_LOCAL = 0 +STB_GLOBAL = 1 +STB_WEAK = 2 +STB_LOPROC = 13 +STB_HIPROC = 15 + +R_386_NONE = 0 +R_386_32 = 1 +R_386_PC32 = 2 +R_386_GOT32 = 3 +R_386_PLT32 = 4 +R_386_COPY = 5 +R_386_GLOB_DAT = 6 +R_386_JMP_SLOT = 7 +R_386_RELATIVE = 8 +R_386_GOTOFF = 9 +R_386_GOTPC = 10 + +R_X86_64_NONE = 0 +R_X86_64_64 = 1 +R_X86_64_PC32 = 2 +R_X86_64_GOT32 = 3 +R_X86_64_PLT32 = 4 +R_X86_64_COPY = 5 +R_X86_64_GLOB_DAT = 6 +R_X86_64_JUMP_SLOT = 7 +R_X86_64_RELATIVE = 8 +R_X86_64_GOTPCREL = 9 +R_X86_64_32 = 10 +R_X86_64_32S = 11 +R_X86_64_16 = 12 +R_X86_64_PC16 = 13 +R_X86_64_8 = 14 +R_X86_64_PC8 = 15 +R_X86_64_DPTMOD64 = 16 +R_X86_64_DTPOFF64 = 17 +R_X86_64_TPOFF64 = 18 +R_X86_64_TLSGD = 19 +R_X86_64_TLSLD = 20 +R_X86_64_DTPOFF32 = 21 +R_X86_64_GOTTPOFF = 22 +R_X86_64_TPOFF32 = 23 +R_X86_64_PC64 = 24 +R_X86_64_GOTOFF64 = 25 +R_X86_64_GOTPC32 = 26 + +ELF:: + +namespace ELF + + if defined Settings.Machine + MACHINE := Settings.Machine + else + MACHINE := EM_386 + end if + + if defined Settings.ABI + ABI := Settings.ABI + else + ABI := ELFOSABI_NONE + end if + + if MACHINE = EM_386 + R_32 = R_386_32 + R_PC32 = R_386_PC32 + R_GOTOFF = R_386_GOTOFF + R_PLT32 = R_386_PLT32 + else if MACHINE = EM_X86_64 + R_32 = R_X86_64_32 + R_PC32 = R_X86_64_PC32 + R_PLT32 = R_X86_64_PLT32 + end if + + Header: + + e_ident db 0x7F,'ELF',ELFCLASS32,ELFDATA2LSB,EV_CURRENT,ABI,(16-$) dup 0 + e_type dw ET_REL + e_machine dw MACHINE + e_version dd EV_CURRENT + e_entry dd 0 + e_phoff dd 0 + e_shoff dd SECTION_TABLE_OFFSET + e_flags dd 0 + e_ehsize dw Content + e_phentsize dw 0 + e_phnum dw 0 + e_shentsize dw sizeof Elf32_Shdr + e_shnum dw NUMBER_OF_SECTIONS + e_shstrndx dw STRING_TABLE_SECTION_INDEX + + Content: + + virtual at 0 + section_table:: rb NUMBER_OF_SECTIONS * sizeof Elf32_Shdr + end virtual + + virtual at 0 + symbol_table:: rb NUMBER_OF_SYMBOLS * sizeof Elf32_Sym + end virtual + + virtual at 0 + string_table:: + _null db 0 + _symtab db '.symtab',0 + _strtab db '.strtab',0 + SECTION_NAME_POSITION = $ + rb SECTION_NAME_TABLE_SIZE - $ + STRING_POSITION = $ + rb STRING_TABLE_SIZE - $ + end virtual + + virtual at 0 + relocations:: rb NUMBER_OF_RELOCATIONS * sizeof Elf32_Rel + end virtual + + element relocatable? + + macro section_org + local sym + element sym : relocatable * SECTION_INDEX + SECTION_SYMBOL_INDEX + SECTION_BASE = sym + org sym + end macro + + RELOCATION_INDEX = 0 + SECTION_INDEX = 1 + SECTION_SYMBOL_INDEX = SECTION_INDEX + SECTION_RELOCATION_INDEX = RELOCATION_INDEX + SYMBOL_INDEX = NUMBER_OF_SECTION_SYMBOLS + + SECTION_OFFSET = $% + SECTION_ALIGN = 4 + SECTION_NAME = '.flat' + SECTION_FLAGS = SHF_ALLOC + SHF_WRITE + SHF_EXECINSTR + DEFINED_SECTION = 0 + section_org + +end namespace + +macro section? + namespace ELF + + SECTION_SIZE = $% - SECTION_OFFSET + + if DEFINED_SECTION | SECTION_SIZE > 0 + + store SECTION_OFFSET at section_table : Elf32_Shdr.sh_offset + SECTION_INDEX * sizeof Elf32_Shdr + store SECTION_SIZE at section_table : Elf32_Shdr.sh_size + SECTION_INDEX * sizeof Elf32_Shdr + store SECTION_ALIGN at section_table : Elf32_Shdr.sh_addralign + SECTION_INDEX * sizeof Elf32_Shdr + store SECTION_FLAGS at section_table : Elf32_Shdr.sh_flags + SECTION_INDEX * sizeof Elf32_Shdr + + if $%% = SECTION_OFFSET + store SHT_NOBITS at section_table : Elf32_Shdr.sh_type + SECTION_INDEX * sizeof Elf32_Shdr + section $ + else + store SHT_PROGBITS at section_table : Elf32_Shdr.sh_type + SECTION_INDEX * sizeof Elf32_Shdr + UNINITIALIZED_LENGTH = $% - $%% + section $ + db UNINITIALIZED_LENGTH dup 0 + end if + + store SECTION_INDEX at symbol_table : Elf32_Sym.st_shndx + SECTION_SYMBOL_INDEX * sizeof Elf32_Sym + store STT_SECTION + STB_LOCAL shl 4 at symbol_table : Elf32_Sym.st_info + SECTION_SYMBOL_INDEX * sizeof Elf32_Sym + + if RELOCATION_INDEX > SECTION_RELOCATION_INDEX + + store RELOCATIONS_OFFSET + SECTION_RELOCATION_INDEX * sizeof Elf32_Rel at section_table : Elf32_Shdr.sh_offset + (SECTION_INDEX+1) * sizeof Elf32_Shdr + store (RELOCATION_INDEX - SECTION_RELOCATION_INDEX) * sizeof Elf32_Rel at section_table : Elf32_Shdr.sh_size + (SECTION_INDEX+1) * sizeof Elf32_Shdr + store SHT_REL at section_table : Elf32_Shdr.sh_type + (SECTION_INDEX+1) * sizeof Elf32_Shdr + store SYMBOL_TABLE_SECTION_INDEX at section_table : Elf32_Shdr.sh_link + (SECTION_INDEX+1) * sizeof Elf32_Shdr + store SECTION_INDEX at section_table : Elf32_Shdr.sh_info + (SECTION_INDEX+1) * sizeof Elf32_Shdr + store sizeof Elf32_Rel at section_table : Elf32_Shdr.sh_entsize + (SECTION_INDEX+1) * sizeof Elf32_Shdr + store 4 at section_table : Elf32_Shdr.sh_addralign + (SECTION_INDEX+1) * sizeof Elf32_Shdr + + store SECTION_NAME_POSITION at section_table : Elf32_Shdr.sh_name + (SECTION_INDEX+1) * sizeof Elf32_Shdr + store SECTION_NAME_POSITION + 4 at section_table : Elf32_Shdr.sh_name + SECTION_INDEX * sizeof Elf32_Shdr + store SECTION_NAME_POSITION + 4 at symbol_table : Elf32_Sym.st_name + SECTION_SYMBOL_INDEX * sizeof Elf32_Sym + store '.rel' + SECTION_NAME shl (4*8) : 4 + lengthof (string SECTION_NAME) at string_table:SECTION_NAME_POSITION + SECTION_NAME_POSITION = SECTION_NAME_POSITION + 4 + lengthof (string SECTION_NAME) + 1 + + SECTION_INDEX = SECTION_INDEX + 2 + SECTION_SYMBOL_INDEX = SECTION_SYMBOL_INDEX + 1 + + else + store SECTION_NAME_POSITION at section_table : Elf32_Shdr.sh_name + SECTION_INDEX * sizeof Elf32_Shdr + store SECTION_NAME_POSITION at symbol_table : Elf32_Sym.st_name + SECTION_SYMBOL_INDEX * sizeof Elf32_Sym + store SECTION_NAME : lengthof (string SECTION_NAME) at string_table:SECTION_NAME_POSITION + SECTION_NAME_POSITION = SECTION_NAME_POSITION + lengthof (string SECTION_NAME) + 1 + + SECTION_INDEX = SECTION_INDEX + 1 + SECTION_SYMBOL_INDEX = SECTION_SYMBOL_INDEX + 1 + + end if + + end if + + end namespace +end macro + +macro section? declaration* + namespace ELF + + section + + DEFINED_SECTION = 1 + SECTION_FLAGS = SHF_ALLOC + SECTION_OFFSET = $% + SECTION_ALIGN = 4 + + match name attributes, declaration + + SECTION_NAME = name + + local seq,list + match flags =align? boundary, attributes + SECTION_ALIGN = boundary + define seq flags + else match =align? boundary, attributes + SECTION_ALIGN = boundary + define seq + else + define seq attributes + end match + while 1 + match car cdr, seq + define list car + define seq cdr + else + match any, seq + define list any + end match + break + end match + end while + irpv attribute, list + match =writeable?, attribute + SECTION_FLAGS = SECTION_FLAGS or SHF_WRITE + else match =executable?, attribute + SECTION_FLAGS = SECTION_FLAGS or SHF_EXECINSTR + else + err 'unknown attribute "',`attribute,'"' + end match + end irpv + + else + + SECTION_NAME = declaration + + end match + + section_org + + SECTION_RELOCATION_INDEX = RELOCATION_INDEX + + end namespace +end macro + +calminstruction align? boundary,value:? + check ELF.SECTION_ALIGN mod (boundary) = 0 + jyes allowed + arrange value, =err 'section not aligned enough' + assemble value + exit + allowed: + compute boundary, (boundary-1)-($-ELF.SECTION_BASE+boundary-1) mod boundary + arrange value, =db boundary =dup value + assemble value +end calminstruction + +macro public? declaration* + namespace ELF + match value =as? str, declaration + SYMBOL_VALUE = value + SYMBOL_SIZE = sizeof value + SYMBOL_NAME = string str + else + SYMBOL_VALUE = declaration + SYMBOL_SIZE = sizeof declaration + SYMBOL_NAME = `declaration + end match + if SYMBOL_VALUE relativeto 1 elementof SYMBOL_VALUE & 1 elementof (1 metadataof SYMBOL_VALUE) relativeto relocatable & 1 scaleof (1 metadataof SYMBOL_VALUE) > 0 + SYMBOL_SECTION_INDEX = 1 scaleof (1 metadataof SYMBOL_VALUE) + SYMBOL_VALUE = SYMBOL_VALUE - 1 elementof SYMBOL_VALUE + else + SYMBOL_SECTION_INDEX = SHN_ABS + end if + store STRING_POSITION at symbol_table : Elf32_Sym.st_name + SYMBOL_INDEX * sizeof Elf32_Sym + store SYMBOL_NAME : lengthof SYMBOL_NAME at string_table:STRING_POSITION + STRING_POSITION = STRING_POSITION + lengthof SYMBOL_NAME + 1 + store SYMBOL_VALUE at symbol_table : Elf32_Sym.st_value + SYMBOL_INDEX * sizeof Elf32_Sym + store SYMBOL_SIZE at symbol_table : Elf32_Sym.st_size + SYMBOL_INDEX * sizeof Elf32_Sym + store SYMBOL_SECTION_INDEX at symbol_table : Elf32_Sym.st_shndx + SYMBOL_INDEX * sizeof Elf32_Sym + if SYMBOL_SIZE + store STT_OBJECT + STB_GLOBAL shl 4 at symbol_table : Elf32_Sym.st_info + SYMBOL_INDEX * sizeof Elf32_Sym + else + store STT_FUNC + STB_GLOBAL shl 4 at symbol_table : Elf32_Sym.st_info + SYMBOL_INDEX * sizeof Elf32_Sym + end if + SYMBOL_INDEX = SYMBOL_INDEX + 1 + end namespace +end macro + +macro extrn? declaration* + namespace ELF + local sym,psym + element sym : relocatable * (-1) + SYMBOL_INDEX + element psym : PLT + SYMBOL_INDEX + match str =as? name:size, declaration + label name:size at sym + label PLT.name at psym + SYMBOL_NAME = string str + SYMBOL_SIZE = size + else match name:size, declaration + label name:size at sym + label PLT.name at psym + SYMBOL_NAME = `name + SYMBOL_SIZE = size + else match str =as? name, declaration + label name at sym + label PLT.name at psym + SYMBOL_NAME = string str + SYMBOL_SIZE = 0 + else + label declaration at sym + label PLT.declaration at psym + SYMBOL_NAME = `declaration + SYMBOL_SIZE = 0 + end match + store STRING_POSITION at symbol_table : Elf32_Sym.st_name + SYMBOL_INDEX * sizeof Elf32_Sym + store SYMBOL_NAME : lengthof SYMBOL_NAME at string_table:STRING_POSITION + STRING_POSITION = STRING_POSITION + lengthof SYMBOL_NAME + 1 + store SYMBOL_SIZE at symbol_table : Elf32_Sym.st_size + SYMBOL_INDEX * sizeof Elf32_Sym + store STT_NOTYPE + STB_GLOBAL shl 4 at symbol_table : Elf32_Sym.st_info + SYMBOL_INDEX * sizeof Elf32_Sym + SYMBOL_INDEX = SYMBOL_INDEX + 1 + end namespace +end macro + +element _GLOBAL_OFFSET_TABLE_ +RVA? equ -_GLOBAL_OFFSET_TABLE_+ +element PLT? + +calminstruction calminstruction?.init? var*, val:0 + compute val, val + publish var, val +end calminstruction + +calminstruction calminstruction?.initsym? var*, val& + publish var, val +end calminstruction + +calminstruction calminstruction?.unique? name + local counter, buffer + init counter + compute counter, counter + 1 + arrange buffer, name#counter + publish name, buffer +end calminstruction + +calminstruction calminstruction?.asm? line& + local tmp, ln, buffer + initsym tmp, unique ln + assemble tmp + publish ln, line + arrange buffer, =assemble ln + assemble buffer +end calminstruction + +calminstruction dword? value + compute value, value + check ~ value relativeto 0 & value relativeto 1 elementof value & 1 elementof (1 metadataof value) relativeto ELF.relocatable + jyes r_32 + check ~ value relativeto 0 & (value + _GLOBAL_OFFSET_TABLE_) relativeto 1 elementof (value + _GLOBAL_OFFSET_TABLE_) & 1 elementof (1 metadataof (value + _GLOBAL_OFFSET_TABLE_)) relativeto ELF.relocatable + jyes r_gotoff + check ~ value relativeto 0 & (value + ELF.SECTION_BASE) relativeto 1 elementof (value + ELF.SECTION_BASE) + jno plain + check 1 elementof (1 metadataof (value + ELF.SECTION_BASE)) relativeto ELF.relocatable + jyes r_pc32 + check 1 elementof (1 metadataof (value + ELF.SECTION_BASE)) relativeto PLT + jyes r_plt32 + plain: + asm emit 4: value + exit + local offset, info + r_32: + compute info, ELF.R_32 + (0 scaleof (1 metadataof value)) shl 8 + jump add_relocation + r_gotoff: + compute value, value + _GLOBAL_OFFSET_TABLE_ + compute info, ELF.R_GOTOFF + (0 scaleof (1 metadataof value)) shl 8 + jump add_relocation + r_pc32: + compute value, value + (ELF.SECTION_BASE + $% - ELF.SECTION_OFFSET) + compute info, ELF.R_PC32 + (0 scaleof (1 metadataof value)) shl 8 + jump add_relocation + r_plt32: + compute value, value + (ELF.SECTION_BASE + $% - ELF.SECTION_OFFSET) + compute info, ELF.R_PLT32 + (0 scaleof (1 metadataof value)) shl 8 + jump add_relocation + add_relocation: + compute offset, $% + asm emit 4: 0 scaleof value + check $% > offset + jno done + compute offset, offset - ELF.SECTION_OFFSET + local Rel + compute Rel, ELF.RELOCATION_INDEX * sizeof Elf32_Rel + asm store offset at ELF.relocations : Rel + Elf32_Rel.r_offset + asm store info at ELF.relocations : Rel + Elf32_Rel.r_info + compute ELF.RELOCATION_INDEX, ELF.RELOCATION_INDEX + 1 + done: +end calminstruction + +calminstruction dd? definitions& + local value, n + start: + match value=,definitions, definitions, () + jyes recognize + match value, definitions + arrange definitions, + recognize: + match n =dup? value, value, () + jyes duplicate + match ?, value + jyes reserve + arrange value, =dword value + assemble value + next: + match , definitions + jno start + take , definitions + take definitions, definitions + jyes next + exit + reserve: + arrange value, =dd ? + assemble value + jump next + duplicate: + match (value), value + stack: + check n + jno next + take definitions, value + arrange value, definitions + compute n, n - 1 + jump stack +end calminstruction + +calminstruction (label) dd? definitions& + local cmd + arrange cmd, =label label : =dword + assemble cmd + arrange cmd, =dd definitions + assemble cmd +end calminstruction + +postpone + purge section? + section + namespace ELF + + SECTION_NAME_TABLE_SIZE := SECTION_NAME_POSITION + STRING_TABLE_SIZE := STRING_POSITION + + NUMBER_OF_SECTION_SYMBOLS := SECTION_SYMBOL_INDEX + NUMBER_OF_SYMBOLS := SYMBOL_INDEX + SYMBOL_TABLE_SIZE := NUMBER_OF_SYMBOLS * sizeof Elf32_Sym + + NUMBER_OF_RELOCATIONS := RELOCATION_INDEX + rb (-$%) and 11b + RELOCATIONS_OFFSET = $% + load byte_sequence : NUMBER_OF_RELOCATIONS * sizeof Elf32_Rel from relocations:0 + db byte_sequence + + store _symtab at section_table : Elf32_Shdr.sh_name + SECTION_INDEX * sizeof Elf32_Shdr + store $% at section_table : Elf32_Shdr.sh_offset + SECTION_INDEX * sizeof Elf32_Shdr + store SYMBOL_TABLE_SIZE at section_table : Elf32_Shdr.sh_size + SECTION_INDEX * sizeof Elf32_Shdr + store sizeof Elf32_Sym at section_table : Elf32_Shdr.sh_entsize + SECTION_INDEX * sizeof Elf32_Shdr + store 4 at section_table : Elf32_Shdr.sh_addralign + SECTION_INDEX * sizeof Elf32_Shdr + store SHT_SYMTAB at section_table : Elf32_Shdr.sh_type + SECTION_INDEX * sizeof Elf32_Shdr + store STRING_TABLE_SECTION_INDEX at section_table : Elf32_Shdr.sh_link + SECTION_INDEX * sizeof Elf32_Shdr + store NUMBER_OF_SECTION_SYMBOLS at section_table : Elf32_Shdr.sh_info + SECTION_INDEX * sizeof Elf32_Shdr + SYMBOL_TABLE_SECTION_INDEX := SECTION_INDEX + load byte_sequence : SYMBOL_TABLE_SIZE from symbol_table:0 + db byte_sequence + SECTION_INDEX = SECTION_INDEX + 1 + + store _strtab at section_table : Elf32_Shdr.sh_name + SECTION_INDEX * sizeof Elf32_Shdr + store $% at section_table : Elf32_Shdr.sh_offset + SECTION_INDEX * sizeof Elf32_Shdr + store STRING_TABLE_SIZE at section_table : Elf32_Shdr.sh_size + SECTION_INDEX * sizeof Elf32_Shdr + store 1 at section_table : Elf32_Shdr.sh_addralign + SECTION_INDEX * sizeof Elf32_Shdr + store SHT_STRTAB at section_table : Elf32_Shdr.sh_type + SECTION_INDEX * sizeof Elf32_Shdr + STRING_TABLE_SECTION_INDEX := SECTION_INDEX + load byte_sequence : STRING_TABLE_SIZE from string_table:0 + db byte_sequence + SECTION_INDEX = SECTION_INDEX + 1 + + assert SECTION_INDEX <= SHN_LORESERVE + + NUMBER_OF_SECTIONS := SECTION_INDEX + rb (-$%) and 11b + SECTION_TABLE_OFFSET := $% + load byte_sequence : NUMBER_OF_SECTIONS * sizeof Elf32_Shdr from section_table:0 + db byte_sequence + + end namespace +end postpone diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/format/elf64.inc b/x86_64_sse2_x87/fasm/examples/x86/include/format/elf64.inc new file mode 100644 index 0000000..42481f3 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/format/elf64.inc @@ -0,0 +1,666 @@ + +macro struct? name + macro end?.struct?! + end namespace + esc end struc + virtual at 0 + name name + sizeof.name = $ + end virtual + purge end?.struct? + end macro + esc struc name + label . : sizeof.name + namespace . +end macro + +struct Elf64_Shdr + sh_name dd ? + sh_type dd ? + sh_flags dq ? + sh_addr dq ? + sh_offset dq ? + sh_size dq ? + sh_link dd ? + sh_info dd ? + sh_addralign dq ? + sh_entsize dq ? +end struct + +struct Elf64_Sym + st_name dd ? + st_info db ? + st_other db ? + st_shndx dw ? + st_value dq ? + st_size dq ? +end struct + +struct Elf64_Rel + r_offset dq ? + r_info dq ? +end struct + +struct Elf64_Rela + r_offset dq ? + r_info dq ? + r_addend dq ? +end struct + +struct Elf64_Phdr + p_type dd ? + p_flags dd ? + p_offset dq ? + p_vaddr dq ? + p_paddr dq ? + p_filesz dq ? + p_memsz dq ? + p_align dq ? +end struct + +purge struct? + +ELFCLASSNONE = 0 +ELFCLASS32 = 1 +ELFCLASS64 = 2 + +ELFDATANONE = 0 +ELFDATA2LSB = 1 +ELFDATA2MSB = 2 + +ELFOSABI_NONE = 0 +ELFOSABI_HPUX = 1 +ELFOSABI_NETBSD = 2 +ELFOSABI_GNU = 3 +ELFOSABI_LINUX = 3 +ELFOSABI_SOLARIS = 6 +ELFOSABI_AIX = 7 +ELFOSABI_IRIX = 8 +ELFOSABI_FREEBSD = 9 +ELFOSABI_TRU64 = 10 +ELFOSABI_MODESTO = 11 +ELFOSABI_OPENBSD = 12 +ELFOSABI_OPENVMS = 13 +ELFOSABI_NSK = 14 +ELFOSABI_AROS = 15 +ELFOSABI_FENIXOS = 16 +ELFOSABI_CLOUDABI = 17 +ELFOSABI_OPENVOS = 18 + +ET_NONE = 0 +ET_REL = 1 +ET_EXEC = 2 +ET_DYN = 3 +ET_CORE = 4 +ET_LOPROC = 0xff00 +ET_HIPROC = 0xffff + +EM_NONE = 0 +EM_IA_64 = 50 +EM_X86_64 = 62 + +EV_NONE = 0 +EV_CURRENT = 1 + +SHN_UNDEF = 0 +SHN_LORESERVE = 0xff00 +SHN_LOPROC = 0xff00 +SHN_HIPROC = 0xff1f +SHN_ABS = 0xfff1 +SHN_COMMON = 0xfff2 +SHN_HIRESERVE = 0xffff + +SHT_NULL = 0 +SHT_PROGBITS = 1 +SHT_SYMTAB = 2 +SHT_STRTAB = 3 +SHT_RELA = 4 +SHT_HASH = 5 +SHT_DYNAMIC = 6 +SHT_NOTE = 7 +SHT_NOBITS = 8 +SHT_REL = 9 +SHT_SHLIB = 10 +SHT_DYNSYM = 11 +SHT_LOPROC = 0x70000000 +SHT_HIPROC = 0x7fffffff +SHT_LOUSER = 0x80000000 +SHT_HIUSER = 0xffffffff + +SHF_WRITE = 0x1 +SHF_ALLOC = 0x2 +SHF_EXECINSTR = 0x4 +SHF_MASKPROC = 0xf0000000 + +STT_NOTYPE = 0 +STT_OBJECT = 1 +STT_FUNC = 2 +STT_SECTION = 3 +STT_FILE = 4 +STT_LOPROC = 13 +STT_HIPROC = 15 + +STB_LOCAL = 0 +STB_GLOBAL = 1 +STB_WEAK = 2 +STB_LOPROC = 13 +STB_HIPROC = 15 + +R_X86_64_NONE = 0 +R_X86_64_64 = 1 +R_X86_64_PC32 = 2 +R_X86_64_GOT32 = 3 +R_X86_64_PLT32 = 4 +R_X86_64_COPY = 5 +R_X86_64_GLOB_DAT = 6 +R_X86_64_JUMP_SLOT = 7 +R_X86_64_RELATIVE = 8 +R_X86_64_GOTPCREL = 9 +R_X86_64_32 = 10 +R_X86_64_32S = 11 +R_X86_64_16 = 12 +R_X86_64_PC16 = 13 +R_X86_64_8 = 14 +R_X86_64_PC8 = 15 +R_X86_64_DPTMOD64 = 16 +R_X86_64_DTPOFF64 = 17 +R_X86_64_TPOFF64 = 18 +R_X86_64_TLSGD = 19 +R_X86_64_TLSLD = 20 +R_X86_64_DTPOFF32 = 21 +R_X86_64_GOTTPOFF = 22 +R_X86_64_TPOFF32 = 23 +R_X86_64_PC64 = 24 +R_X86_64_GOTOFF64 = 25 +R_X86_64_GOTPC32 = 26 + +ELF:: + +namespace ELF + + if defined Settings.Machine + MACHINE := Settings.Machine + else + MACHINE := EM_X86_64 + end if + + if defined Settings.ABI + ABI := Settings.ABI + else + ABI := ELFOSABI_NONE + end if + + if MACHINE = EM_X86_64 + R_64 = R_X86_64_64 + R_32 = R_X86_64_32S + R_PC32 = R_X86_64_PC32 + R_PLT32 = R_X86_64_PLT32 + end if + + Header: + + e_ident db 0x7F,'ELF',ELFCLASS64,ELFDATA2LSB,EV_CURRENT,ABI,(16-$) dup 0 + e_type dw ET_REL + e_machine dw MACHINE + e_version dd EV_CURRENT + e_entry dq 0 + e_phoff dq 0 + e_shoff dq SECTION_TABLE_OFFSET + e_flags dd 0 + e_ehsize dw Content + e_phentsize dw 0 + e_phnum dw 0 + e_shentsize dw sizeof Elf64_Shdr + e_shnum dw NUMBER_OF_SECTIONS + e_shstrndx dw STRING_TABLE_SECTION_INDEX + + Content: + + virtual at 0 + section_table:: rb NUMBER_OF_SECTIONS * sizeof Elf64_Shdr + end virtual + + virtual at 0 + symbol_table:: rb NUMBER_OF_SYMBOLS * sizeof Elf64_Sym + end virtual + + virtual at 0 + string_table:: + _null db 0 + _symtab db '.symtab',0 + _strtab db '.strtab',0 + SECTION_NAME_POSITION = $ + rb SECTION_NAME_TABLE_SIZE - $ + STRING_POSITION = $ + rb STRING_TABLE_SIZE - $ + end virtual + + virtual at 0 + relocations:: rb NUMBER_OF_RELOCATIONS * sizeof Elf64_Rela + end virtual + + element relocatable? + + macro section_org + local sym + element sym : relocatable * SECTION_INDEX + SECTION_SYMBOL_INDEX + SECTION_BASE = sym + org sym + end macro + + RELOCATION_INDEX = 0 + SECTION_INDEX = 1 + SECTION_SYMBOL_INDEX = SECTION_INDEX + SECTION_RELOCATION_INDEX = RELOCATION_INDEX + SYMBOL_INDEX = NUMBER_OF_SECTION_SYMBOLS + + SECTION_OFFSET = $% + SECTION_ALIGN = 8 + SECTION_NAME = '.flat' + SECTION_FLAGS = SHF_ALLOC + SHF_WRITE + SHF_EXECINSTR + DEFINED_SECTION = 0 + section_org + +end namespace + +macro section? + namespace ELF + + SECTION_SIZE = $% - SECTION_OFFSET + + if DEFINED_SECTION | SECTION_SIZE > 0 + + store SECTION_OFFSET at section_table : Elf64_Shdr.sh_offset + SECTION_INDEX * sizeof Elf64_Shdr + store SECTION_SIZE at section_table : Elf64_Shdr.sh_size + SECTION_INDEX * sizeof Elf64_Shdr + store SECTION_ALIGN at section_table : Elf64_Shdr.sh_addralign + SECTION_INDEX * sizeof Elf64_Shdr + store SECTION_FLAGS at section_table : Elf64_Shdr.sh_flags + SECTION_INDEX * sizeof Elf64_Shdr + + if $%% = SECTION_OFFSET + store SHT_NOBITS at section_table : Elf64_Shdr.sh_type + SECTION_INDEX * sizeof Elf64_Shdr + section $ + else + store SHT_PROGBITS at section_table : Elf64_Shdr.sh_type + SECTION_INDEX * sizeof Elf64_Shdr + UNINITIALIZED_LENGTH = $% - $%% + section $ + db UNINITIALIZED_LENGTH dup 0 + end if + + store SECTION_INDEX at symbol_table : Elf64_Sym.st_shndx + SECTION_SYMBOL_INDEX * sizeof Elf64_Sym + store STT_SECTION + STB_LOCAL shl 4 at symbol_table : Elf64_Sym.st_info + SECTION_SYMBOL_INDEX * sizeof Elf64_Sym + + if RELOCATION_INDEX > SECTION_RELOCATION_INDEX + + store RELOCATIONS_OFFSET + SECTION_RELOCATION_INDEX * sizeof Elf64_Rela at section_table : Elf64_Shdr.sh_offset + (SECTION_INDEX+1) * sizeof Elf64_Shdr + store (RELOCATION_INDEX - SECTION_RELOCATION_INDEX) * sizeof Elf64_Rela at section_table : Elf64_Shdr.sh_size + (SECTION_INDEX+1) * sizeof Elf64_Shdr + store SHT_RELA at section_table : Elf64_Shdr.sh_type + (SECTION_INDEX+1) * sizeof Elf64_Shdr + store SYMBOL_TABLE_SECTION_INDEX at section_table : Elf64_Shdr.sh_link + (SECTION_INDEX+1) * sizeof Elf64_Shdr + store SECTION_INDEX at section_table : Elf64_Shdr.sh_info + (SECTION_INDEX+1) * sizeof Elf64_Shdr + store sizeof Elf64_Rela at section_table : Elf64_Shdr.sh_entsize + (SECTION_INDEX+1) * sizeof Elf64_Shdr + store 8 at section_table : Elf64_Shdr.sh_addralign + (SECTION_INDEX+1) * sizeof Elf64_Shdr + + store SECTION_NAME_POSITION at section_table : Elf64_Shdr.sh_name + (SECTION_INDEX+1) * sizeof Elf64_Shdr + store SECTION_NAME_POSITION + 5 at section_table : Elf64_Shdr.sh_name + SECTION_INDEX * sizeof Elf64_Shdr + store SECTION_NAME_POSITION + 5 at symbol_table : Elf64_Sym.st_name + SECTION_SYMBOL_INDEX * sizeof Elf64_Sym + store '.rela' + SECTION_NAME shl (5*8) : 5 + lengthof (string SECTION_NAME) at string_table:SECTION_NAME_POSITION + SECTION_NAME_POSITION = SECTION_NAME_POSITION + 5 + lengthof (string SECTION_NAME) + 1 + + SECTION_INDEX = SECTION_INDEX + 2 + SECTION_SYMBOL_INDEX = SECTION_SYMBOL_INDEX + 1 + + else + store SECTION_NAME_POSITION at section_table : Elf64_Shdr.sh_name + SECTION_INDEX * sizeof Elf64_Shdr + store SECTION_NAME_POSITION at symbol_table : Elf64_Sym.st_name + SECTION_SYMBOL_INDEX * sizeof Elf64_Sym + store SECTION_NAME : lengthof (string SECTION_NAME) at string_table:SECTION_NAME_POSITION + SECTION_NAME_POSITION = SECTION_NAME_POSITION + lengthof (string SECTION_NAME) + 1 + + SECTION_INDEX = SECTION_INDEX + 1 + SECTION_SYMBOL_INDEX = SECTION_SYMBOL_INDEX + 1 + + end if + + end if + + end namespace +end macro + +macro section? declaration* + namespace ELF + + section + + DEFINED_SECTION = 1 + SECTION_FLAGS = SHF_ALLOC + SECTION_OFFSET = $% + SECTION_ALIGN = 8 + + match name attributes, declaration + + SECTION_NAME = name + + local seq,list + match flags =align? boundary, attributes + SECTION_ALIGN = boundary + define seq flags + else match =align? boundary, attributes + SECTION_ALIGN = boundary + define seq + else + define seq attributes + end match + while 1 + match car cdr, seq + define list car + define seq cdr + else + match any, seq + define list any + end match + break + end match + end while + irpv attribute, list + match =writeable?, attribute + SECTION_FLAGS = SECTION_FLAGS or SHF_WRITE + else match =executable?, attribute + SECTION_FLAGS = SECTION_FLAGS or SHF_EXECINSTR + else + err 'unknown attribute "',`attribute,'"' + end match + end irpv + + else + + SECTION_NAME = declaration + + end match + + section_org + + SECTION_RELOCATION_INDEX = RELOCATION_INDEX + + end namespace +end macro + +calminstruction align? boundary,value:? + check ELF.SECTION_ALIGN mod (boundary) = 0 + jyes allowed + arrange value, =err 'section not aligned enough' + assemble value + exit + allowed: + compute boundary, (boundary-1)-($-ELF.SECTION_BASE+boundary-1) mod boundary + arrange value, =db boundary =dup value + assemble value +end calminstruction + +macro public? declaration* + namespace ELF + match value =as? str, declaration + SYMBOL_VALUE = value + SYMBOL_SIZE = sizeof value + SYMBOL_NAME = string str + else + SYMBOL_VALUE = declaration + SYMBOL_SIZE = sizeof declaration + SYMBOL_NAME = `declaration + end match + if SYMBOL_VALUE relativeto 1 elementof SYMBOL_VALUE & 1 elementof (1 metadataof SYMBOL_VALUE) relativeto relocatable & 1 scaleof (1 metadataof SYMBOL_VALUE) > 0 + SYMBOL_SECTION_INDEX = 1 scaleof (1 metadataof SYMBOL_VALUE) + SYMBOL_VALUE = SYMBOL_VALUE - 1 elementof SYMBOL_VALUE + else + SYMBOL_SECTION_INDEX = SHN_ABS + end if + store STRING_POSITION at symbol_table : Elf64_Sym.st_name + SYMBOL_INDEX * sizeof Elf64_Sym + store SYMBOL_NAME : lengthof SYMBOL_NAME at string_table:STRING_POSITION + STRING_POSITION = STRING_POSITION + lengthof SYMBOL_NAME + 1 + store SYMBOL_VALUE at symbol_table : Elf64_Sym.st_value + SYMBOL_INDEX * sizeof Elf64_Sym + store SYMBOL_SIZE at symbol_table : Elf64_Sym.st_size + SYMBOL_INDEX * sizeof Elf64_Sym + store SYMBOL_SECTION_INDEX at symbol_table : Elf64_Sym.st_shndx + SYMBOL_INDEX * sizeof Elf64_Sym + if SYMBOL_SIZE + store STT_OBJECT + STB_GLOBAL shl 4 at symbol_table : Elf64_Sym.st_info + SYMBOL_INDEX * sizeof Elf64_Sym + else + store STT_FUNC + STB_GLOBAL shl 4 at symbol_table : Elf64_Sym.st_info + SYMBOL_INDEX * sizeof Elf64_Sym + end if + SYMBOL_INDEX = SYMBOL_INDEX + 1 + end namespace +end macro + +macro extrn? declaration* + namespace ELF + local sym,psym + element sym : relocatable * (-1) + SYMBOL_INDEX + element psym : PLT + SYMBOL_INDEX + match str =as? name:size, declaration + label name:size at sym + label PLT.name at psym + SYMBOL_NAME = string str + SYMBOL_SIZE = size + else match name:size, declaration + label name:size at sym + label PLT.name at psym + SYMBOL_NAME = `name + SYMBOL_SIZE = size + else match str =as? name, declaration + label name at sym + label PLT.name at psym + SYMBOL_NAME = string str + SYMBOL_SIZE = 0 + else + label declaration at sym + label PLT.declaration at psym + SYMBOL_NAME = `declaration + SYMBOL_SIZE = 0 + end match + store STRING_POSITION at symbol_table : Elf64_Sym.st_name + SYMBOL_INDEX * sizeof Elf64_Sym + store SYMBOL_NAME : lengthof SYMBOL_NAME at string_table:STRING_POSITION + STRING_POSITION = STRING_POSITION + lengthof SYMBOL_NAME + 1 + store SYMBOL_SIZE at symbol_table : Elf64_Sym.st_size + SYMBOL_INDEX * sizeof Elf64_Sym + store STT_NOTYPE + STB_GLOBAL shl 4 at symbol_table : Elf64_Sym.st_info + SYMBOL_INDEX * sizeof Elf64_Sym + SYMBOL_INDEX = SYMBOL_INDEX + 1 + end namespace +end macro + +element PLT? + +calminstruction calminstruction?.init? var*, val:0 + compute val, val + publish var, val +end calminstruction + +calminstruction calminstruction?.initsym? var*, val& + publish var, val +end calminstruction + +calminstruction calminstruction?.unique? name + local counter, buffer + init counter + compute counter, counter + 1 + arrange buffer, name#counter + publish name, buffer +end calminstruction + +calminstruction calminstruction?.asm? line& + local tmp, ln, buffer + initsym tmp, unique ln + assemble tmp + publish ln, line + arrange buffer, =assemble ln + assemble buffer +end calminstruction + +calminstruction dword? value + compute value, value + check ~ value relativeto 0 & value relativeto 1 elementof value & 1 elementof (1 metadataof value) relativeto ELF.relocatable + jyes r_32 + check ~ value relativeto 0 & (value + ELF.SECTION_BASE) relativeto 1 elementof (value + ELF.SECTION_BASE) + jno plain + check 1 elementof (1 metadataof (value + ELF.SECTION_BASE)) relativeto ELF.relocatable + jyes r_pc32 + check 1 elementof (1 metadataof (value + ELF.SECTION_BASE)) relativeto PLT + jyes r_plt32 + plain: + asm emit 4: value + exit + local offset, addend, info + r_32: + compute offset, $% + asm emit 4: ? + check $% > offset + jno done + compute offset, offset - ELF.SECTION_OFFSET + compute addend, 0 scaleof value + compute info, ELF.R_32 + (0 scaleof (1 metadataof value)) shl 32 + jump add_relocation + r_pc32: + compute offset, $% + asm emit 4: ? + check $% > offset + jno done + compute offset, offset - ELF.SECTION_OFFSET + compute addend, 0 scaleof (value + ELF.SECTION_BASE + offset) + compute info, ELF.R_PC32 + (0 scaleof (1 metadataof value)) shl 32 + jump add_relocation + r_plt32: + compute offset, $% + asm emit 4: ? + check $% > offset + jno done + compute offset, offset - ELF.SECTION_OFFSET + compute addend, 0 scaleof (value + ELF.SECTION_BASE + offset) + compute info, ELF.R_PLT32 + (0 scaleof (1 metadataof value)) shl 32 + jump add_relocation + add_relocation: + local Rela + compute Rela, ELF.RELOCATION_INDEX * sizeof Elf64_Rela + asm store offset at ELF.relocations : Rela + Elf64_Rela.r_offset + asm store addend at ELF.relocations : Rela + Elf64_Rela.r_addend + asm store info at ELF.relocations : Rela + Elf64_Rela.r_info + compute ELF.RELOCATION_INDEX, ELF.RELOCATION_INDEX + 1 + done: +end calminstruction + +calminstruction qword? value + compute value, value + check ~ value relativeto 0 & value relativeto 1 elementof value & 1 elementof (1 metadataof value) relativeto ELF.relocatable + jyes r_64 + plain: + asm emit 8: value + exit + local offset, addend, info + r_64: + compute offset, $% + asm emit 8: ? + check $% > offset + jno done + compute offset, offset - ELF.SECTION_OFFSET + compute addend, 0 scaleof value + compute info, ELF.R_64 + (0 scaleof (1 metadataof value)) shl 32 + add_relocation: + local Rela + compute Rela, ELF.RELOCATION_INDEX * sizeof Elf64_Rela + asm store offset at ELF.relocations : Rela + Elf64_Rela.r_offset + asm store addend at ELF.relocations : Rela + Elf64_Rela.r_addend + asm store info at ELF.relocations : Rela + Elf64_Rela.r_info + compute ELF.RELOCATION_INDEX, ELF.RELOCATION_INDEX + 1 + done: +end calminstruction + +iterate , dd,dword, dq,qword + + calminstruction dd? definitions& + local value, n + start: + match value=,definitions, definitions, () + jyes recognize + match value, definitions + arrange definitions, + recognize: + match n =dup? value, value, () + jyes duplicate + match ?, value + jyes reserve + arrange value, =dword value + assemble value + next: + match , definitions + jno start + take , definitions + take definitions, definitions + jyes next + exit + reserve: + arrange value, =dd ? + assemble value + jump next + duplicate: + match (value), value + stack: + check n + jno next + take definitions, value + arrange value, definitions + compute n, n - 1 + jump stack + end calminstruction + + calminstruction (label) dd? definitions& + local cmd + arrange cmd, =label label : =dword + assemble cmd + arrange cmd, =dd definitions + assemble cmd + end calminstruction + +end iterate + +postpone + purge section? + section + namespace ELF + + SECTION_NAME_TABLE_SIZE := SECTION_NAME_POSITION + STRING_TABLE_SIZE := STRING_POSITION + + NUMBER_OF_SECTION_SYMBOLS := SECTION_SYMBOL_INDEX + NUMBER_OF_SYMBOLS := SYMBOL_INDEX + SYMBOL_TABLE_SIZE := NUMBER_OF_SYMBOLS * sizeof Elf64_Sym + + NUMBER_OF_RELOCATIONS := RELOCATION_INDEX + rb (-$%) and 111b + RELOCATIONS_OFFSET = $% + load byte_sequence : NUMBER_OF_RELOCATIONS * sizeof Elf64_Rela from relocations:0 + db byte_sequence + + store _symtab at section_table : Elf64_Shdr.sh_name + SECTION_INDEX * sizeof Elf64_Shdr + store $% at section_table : Elf64_Shdr.sh_offset + SECTION_INDEX * sizeof Elf64_Shdr + store SYMBOL_TABLE_SIZE at section_table : Elf64_Shdr.sh_size + SECTION_INDEX * sizeof Elf64_Shdr + store sizeof Elf64_Sym at section_table : Elf64_Shdr.sh_entsize + SECTION_INDEX * sizeof Elf64_Shdr + store 8 at section_table : Elf64_Shdr.sh_addralign + SECTION_INDEX * sizeof Elf64_Shdr + store SHT_SYMTAB at section_table : Elf64_Shdr.sh_type + SECTION_INDEX * sizeof Elf64_Shdr + store STRING_TABLE_SECTION_INDEX at section_table : Elf64_Shdr.sh_link + SECTION_INDEX * sizeof Elf64_Shdr + store NUMBER_OF_SECTION_SYMBOLS at section_table : Elf64_Shdr.sh_info + SECTION_INDEX * sizeof Elf64_Shdr + SYMBOL_TABLE_SECTION_INDEX := SECTION_INDEX + load byte_sequence : SYMBOL_TABLE_SIZE from symbol_table:0 + db byte_sequence + SECTION_INDEX = SECTION_INDEX + 1 + + store _strtab at section_table : Elf64_Shdr.sh_name + SECTION_INDEX * sizeof Elf64_Shdr + store $% at section_table : Elf64_Shdr.sh_offset + SECTION_INDEX * sizeof Elf64_Shdr + store STRING_TABLE_SIZE at section_table : Elf64_Shdr.sh_size + SECTION_INDEX * sizeof Elf64_Shdr + store 1 at section_table : Elf64_Shdr.sh_addralign + SECTION_INDEX * sizeof Elf64_Shdr + store SHT_STRTAB at section_table : Elf64_Shdr.sh_type + SECTION_INDEX * sizeof Elf64_Shdr + STRING_TABLE_SECTION_INDEX := SECTION_INDEX + load byte_sequence : STRING_TABLE_SIZE from string_table:0 + db byte_sequence + SECTION_INDEX = SECTION_INDEX + 1 + + assert SECTION_INDEX <= SHN_LORESERVE + + NUMBER_OF_SECTIONS := SECTION_INDEX + rb (-$%) and 111b + SECTION_TABLE_OFFSET := $% + load byte_sequence : NUMBER_OF_SECTIONS * sizeof Elf64_Shdr from section_table:0 + db byte_sequence + + end namespace +end postpone diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/format/elfexe.inc b/x86_64_sse2_x87/fasm/examples/x86/include/format/elfexe.inc new file mode 100644 index 0000000..8648da3 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/format/elfexe.inc @@ -0,0 +1,326 @@ + +ELFCLASSNONE = 0 +ELFCLASS32 = 1 +ELFCLASS64 = 2 + +ELFDATANONE = 0 +ELFDATA2LSB = 1 +ELFDATA2MSB = 2 + +ELFOSABI_NONE = 0 +ELFOSABI_HPUX = 1 +ELFOSABI_NETBSD = 2 +ELFOSABI_GNU = 3 +ELFOSABI_LINUX = 3 +ELFOSABI_SOLARIS = 6 +ELFOSABI_AIX = 7 +ELFOSABI_IRIX = 8 +ELFOSABI_FREEBSD = 9 +ELFOSABI_TRU64 = 10 +ELFOSABI_MODESTO = 11 +ELFOSABI_OPENBSD = 12 +ELFOSABI_OPENVMS = 13 +ELFOSABI_NSK = 14 +ELFOSABI_AROS = 15 +ELFOSABI_FENIXOS = 16 +ELFOSABI_CLOUDABI = 17 +ELFOSABI_OPENVOS = 18 + +ET_NONE = 0 +ET_REL = 1 +ET_EXEC = 2 +ET_DYN = 3 +ET_CORE = 4 +ET_LOPROC = 0xff00 +ET_HIPROC = 0xffff + +EM_NONE = 0 +EM_M32 = 1 +EM_SPARC = 2 +EM_386 = 3 +EM_68K = 4 +EM_88K = 5 +EM_860 = 7 +EM_MIPS = 8 +EM_X86_64 = 62 + +EV_NONE = 0 +EV_CURRENT = 1 + +PT_NULL = 0 +PT_LOAD = 1 +PT_DYNAMIC = 2 +PT_INTERP = 3 +PT_NOTE = 4 +PT_SHLIB = 5 +PT_PHDR = 6 +PT_GNU_EH_FRAME = 0x6474e550 +PT_GNU_STACK = 0x6474e551 +PT_GNU_RELRO = 0x6474e552 +PT_LOPROC = 0x70000000 +PT_HIPROC = 0x7fffffff + +PF_X = 1 +PF_W = 2 +PF_R = 4 +PF_MASKOS = 0x0ff00000 +PF_MASKPROC = 0xf0000000 + +calminstruction align? boundary,value:? + compute boundary, (boundary-1)-($-ELF.DYNAMIC+boundary-1) mod boundary + arrange value, =db boundary =dup value + assemble value +end calminstruction + +ELF:: + +namespace ELF + + if defined Settings.Class + CLASS := Settings.Class + else + CLASS := ELFCLASS32 + end if + + if defined Settings.Type + TYPE := Settings.Type + else + TYPE := ET_EXEC + end if + + if defined Settings.Machine + MACHINE := Settings.Machine + else + MACHINE := EM_386 + end if + + if defined Settings.ABI + ABI := Settings.ABI + else + ABI := ELFOSABI_NONE + end if + + if TYPE = ET_DYN + element DYNAMIC + else + DYNAMIC := 0 + end if + + if defined Settings.BaseAddress + BASE_ADDRESS := DYNAMIC + Settings.BaseAddress + else + BASE_ADDRESS := DYNAMIC + 8048000h + end if + + if defined Settings.LoadHeaders + LOAD_HEADERS := Settings.LoadHeaders + else + LOAD_HEADERS := 1 + end if + + Header: + + e_ident db 0x7F,'ELF',CLASS,ELFDATA2LSB,EV_CURRENT,ABI,(16-$) dup 0 + e_type dw TYPE + e_machine dw MACHINE + e_version dd EV_CURRENT + if CLASS <> ELFCLASS64 + e_entry dd start - DYNAMIC + e_phoff dd ProgramHeader + e_shoff dd 0 + e_flags dd 0 + e_ehsize dw ProgramHeader + e_phentsize dw SEGMENT_HEADER_LENGTH + e_phnum dw NUMBER_OF_SEGMENTS + e_shentsize dw 28h + e_shnum dw 0 + e_shstrndx dw 0 + else + e_entry dq start - DYNAMIC + e_phoff dq ProgramHeader + e_shoff dq 0 + e_flags dd 0 + e_ehsize dw ProgramHeader + e_phentsize dw SEGMENT_HEADER_LENGTH + e_phnum dw NUMBER_OF_SEGMENTS + e_shentsize dw 40h + e_shnum dw 0 + e_shstrndx dw 0 + end if + + ProgramHeader: + if CLASS <> ELFCLASS64 + p_type dd PT_LOAD + p_offset dd 0 + p_vaddr dd BASE_ADDRESS - DYNAMIC + p_paddr dd BASE_ADDRESS - DYNAMIC + p_filesz dd 0 + p_memsz dd 0 + p_flags dd PF_R+PF_W+PF_X + p_align dd 1000h + else + p_type dd PT_LOAD + p_flags dd PF_R+PF_W+PF_X + p_offset dq 0 + p_vaddr dq BASE_ADDRESS - DYNAMIC + p_paddr dq BASE_ADDRESS - DYNAMIC + p_filesz dq 0 + p_memsz dq 0 + p_align dq 1000h + end if + + SEGMENT_HEADER_LENGTH = $ - ProgramHeader + + db (NUMBER_OF_SEGMENTS-1)*SEGMENT_HEADER_LENGTH dup 0 + + HEADERS_OFFSET = 0 + HEADERS_BASE = BASE_ADDRESS + OVERLAY_HEADERS = LOAD_HEADERS + + SEGMENT_INDEX = 0 + DEFINED_SEGMENT = 0 + SEGMENT_TYPE = PT_LOAD + FILE_OFFSET = $% + SEGMENT_BASE = BASE_ADDRESS + FILE_OFFSET and 0FFFh + org SEGMENT_BASE + start: + +end namespace + +RVA? equ -ELF.BASE_ADDRESS + + +macro entry? address* + namespace ELF + store address-DYNAMIC at ELF:e_entry + end namespace +end macro + +macro segment? + namespace ELF + + DEFINED_SEGMENT_SIZE = $ - SEGMENT_BASE + + if (DEFINED_SEGMENT | DEFINED_SEGMENT_SIZE > 0) & SEGMENT_TYPE = PT_LOAD & OVERLAY_HEADERS + FILE_OFFSET = HEADERS_OFFSET + SEGMENT_BASE = HEADERS_BASE + OVERLAY_HEADERS = 0 + end if + + store SEGMENT_BASE-DYNAMIC at ELF:p_vaddr+SEGMENT_INDEX*SEGMENT_HEADER_LENGTH + store SEGMENT_BASE-DYNAMIC at ELF:p_paddr+SEGMENT_INDEX*SEGMENT_HEADER_LENGTH + store FILE_OFFSET at ELF:p_offset+SEGMENT_INDEX*SEGMENT_HEADER_LENGTH + + if SEGMENT_TYPE = PT_LOAD + + RAW_DATA_SIZE = $%% - FILE_OFFSET + SEGMENT_SIZE = $ - SEGMENT_BASE + store RAW_DATA_SIZE at ELF:p_filesz+SEGMENT_INDEX*SEGMENT_HEADER_LENGTH + store SEGMENT_SIZE at ELF:p_memsz+SEGMENT_INDEX*SEGMENT_HEADER_LENGTH + + if DEFINED_SEGMENT | DEFINED_SEGMENT_SIZE > 0 + FILE_OFFSET = $%% + align 1000h + SEGMENT_BASE = $ + FILE_OFFSET and 0FFFh + end if + + section SEGMENT_BASE + + else + + if OVERLAY_HEADERS = 0 & ( LOAD_HEADERS | SEGMENT_TYPE = PT_GNU_RELRO ) & ~ SEGMENT_TYPE = PT_GNU_STACK & ~ SEGMENT_TYPE = PT_NOTE + OVERLAY_HEADERS = 1 + HEADERS_OFFSET = FILE_OFFSET + HEADERS_BASE = SEGMENT_BASE + end if + + FILE_OFFSET = $% + SEGMENT_SIZE = $ - SEGMENT_BASE + store SEGMENT_SIZE at ELF:p_filesz+SEGMENT_INDEX*SEGMENT_HEADER_LENGTH + store SEGMENT_SIZE at ELF:p_memsz+SEGMENT_INDEX*SEGMENT_HEADER_LENGTH + + if OVERLAY_HEADERS = 0 + SEGMENT_BASE = DYNAMIC + (SEGMENT_BASE-DYNAMIC) and not 0FFFh + FILE_OFFSET and 0FFFh + else + SEGMENT_BASE = $ + end if + + if $% > $%% + store 0:byte at $-1 + end if + org SEGMENT_BASE + + end if + + if DEFINED_SEGMENT | DEFINED_SEGMENT_SIZE > 0 + SEGMENT_INDEX = SEGMENT_INDEX + 1 + end if + + end namespace +end macro + +macro segment? attributes* + namespace ELF + + segment + + SEGMENT_TYPE = PT_LOAD + SEGMENT_FLAGS = 0 + + local seq,list + define seq attributes + while 1 + match car cdr, seq + define list car + define seq cdr + else + match any, seq + define list any + end match + break + end match + end while + irpv attribute, list + match =readable?, attribute + SEGMENT_FLAGS = SEGMENT_FLAGS or PF_R + else match =writeable?, attribute + SEGMENT_FLAGS = SEGMENT_FLAGS or PF_W + else match =executable?, attribute + SEGMENT_FLAGS = SEGMENT_FLAGS or PF_X + else match =interpreter?, attribute + SEGMENT_TYPE = PT_INTERP + else match =dynamic?, attribute + SEGMENT_TYPE = PT_DYNAMIC + else match =note?, attribute + SEGMENT_TYPE = PT_NOTE + else match =gnustack?, attribute + SEGMENT_TYPE = PT_GNU_STACK + else match =gnuehframe?, attribute + SEGMENT_TYPE = PT_GNU_EH_FRAME + else match =gnurelro?, attribute + SEGMENT_TYPE = PT_GNU_RELRO + else + err 'unknown attribute "',`attribute,'"' + end match + end irpv + + DEFINED_SEGMENT = 1 + + store SEGMENT_TYPE at ELF:p_type+SEGMENT_INDEX*SEGMENT_HEADER_LENGTH + store SEGMENT_FLAGS at ELF:p_flags+SEGMENT_INDEX*SEGMENT_HEADER_LENGTH + + if SEGMENT_TYPE = PT_LOAD + store 1000h at ELF:p_align+SEGMENT_INDEX*SEGMENT_HEADER_LENGTH + else + store 1 at ELF:p_align+SEGMENT_INDEX*SEGMENT_HEADER_LENGTH + end if + + end namespace +end macro + +postpone + purge segment? + segment + namespace ELF + NUMBER_OF_SEGMENTS := SEGMENT_INDEX + end namespace +end postpone diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/format/format.inc b/x86_64_sse2_x87/fasm/examples/x86/include/format/format.inc new file mode 100644 index 0000000..e386c7b --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/format/format.inc @@ -0,0 +1,328 @@ + +macro format?.MZ? + format binary as 'exe' + include 'format/mz.inc' + include 'p6.inc' +end macro + +macro format?.PE? settings + PE.Settings.Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE or IMAGE_FILE_32BIT_MACHINE or IMAGE_FILE_LINE_NUMS_STRIPPED or IMAGE_FILE_LOCAL_SYMS_STRIPPED + PE.Settings.DllCharacteristics = 0 + local seq + define seq settings: + while 1 + match :, seq + break + else match =DLL? more, seq + PE.Settings.Characteristics = PE.Settings.Characteristics or IMAGE_FILE_DLL + redefine seq more + else match =large? more, seq + PE.Settings.Characteristics = PE.Settings.Characteristics or IMAGE_FILE_LARGE_ADDRESS_AWARE + redefine seq more + else match =WDM? more, seq + PE.Settings.DllCharacteristics = PE.Settings.DllCharacteristics or IMAGE_DLLCHARACTERISTICS_WDM_DRIVER + redefine seq more + else match =NX? more, seq + PE.Settings.DllCharacteristics = PE.Settings.DllCharacteristics or IMAGE_DLLCHARACTERISTICS_NX_COMPAT + redefine seq more + else match =at? base =on? stub :, seq + PE.Settings.ImageBase = base + PE.Settings.Stub = stub + break + else match =at? base :, seq + PE.Settings.ImageBase = base + break + else match =on? stub :, seq + PE.Settings.Stub = stub + break + else + match =GUI? more, seq + PE.Settings.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI + redefine seq more + else match =console? more, seq + PE.Settings.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI + redefine seq more + else match =native? more, seq + PE.Settings.Subsystem = IMAGE_SUBSYSTEM_NATIVE + PE.Settings.SectionAlignment = 32 + PE.Settings.FileAlignment = 32 + redefine seq more + else match =EFI? more, seq + PE.Settings.Magic = 0x20B + PE.Settings.Subsystem = IMAGE_SUBSYSTEM_EFI_APPLICATION + redefine seq more + else match =EFIboot? more, seq + PE.Settings.Magic = 0x20B + PE.Settings.Subsystem = IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER + redefine seq more + else match =EFIruntime? more, seq + PE.Settings.Magic = 0x20B + PE.Settings.Subsystem = IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER + redefine seq more + else + err 'invalid argument' + break + end match + match V.v more, seq + PE.Settings.MajorSubsystemVersion = V + PE.Settings.MinorSubsystemVersion = v + redefine seq more + end match + end match + end while + if PE.Settings.Characteristics and IMAGE_FILE_DLL + format binary as 'dll' + else + format binary as 'exe' + end if + include 'format/pe.inc' + include 'p6.inc' + use32 +end macro + +macro format?.PE64? settings + PE.Settings.Magic = 0x20B + PE.Settings.Machine = IMAGE_FILE_MACHINE_AMD64 + PE.Settings.Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE or IMAGE_FILE_LARGE_ADDRESS_AWARE or IMAGE_FILE_LINE_NUMS_STRIPPED or IMAGE_FILE_LOCAL_SYMS_STRIPPED + PE.Settings.DllCharacteristics = 0 + PE.Settings.MajorSubsystemVersion = 5 + PE.Settings.MinorSubsystemVersion = 0 + local seq + define seq settings: + while 1 + match :, seq + break + else match =DLL? more, seq + PE.Settings.Characteristics = PE.Settings.Characteristics or IMAGE_FILE_DLL + redefine seq more + else match =WDM? more, seq + PE.Settings.DllCharacteristics = PE.Settings.DllCharacteristics or IMAGE_DLLCHARACTERISTICS_WDM_DRIVER + redefine seq more + else match =NX? more, seq + PE.Settings.DllCharacteristics = PE.Settings.DllCharacteristics or IMAGE_DLLCHARACTERISTICS_NX_COMPAT + redefine seq more + else match =at? base =on? stub :, seq + PE.Settings.ImageBase = base + PE.Settings.Stub = stub + break + else match =at? base :, seq + PE.Settings.ImageBase = base + break + else match =on? stub :, seq + PE.Settings.Stub = stub + break + else + match =GUI? more, seq + PE.Settings.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI + redefine seq more + else match =console? more, seq + PE.Settings.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI + redefine seq more + else match =native? more, seq + PE.Settings.Subsystem = IMAGE_SUBSYSTEM_NATIVE + PE.Settings.SectionAlignment = 32 + PE.Settings.FileAlignment = 32 + redefine seq more + else match =EFI? more, seq + PE.Settings.Subsystem = IMAGE_SUBSYSTEM_EFI_APPLICATION + redefine seq more + else match =EFIboot? more, seq + PE.Settings.Subsystem = IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER + redefine seq more + else match =EFIruntime? more, seq + PE.Settings.Subsystem = IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER + redefine seq more + else + err 'invalid argument' + break + end match + match V.v more, seq + PE.Settings.MajorSubsystemVersion = V + PE.Settings.MinorSubsystemVersion = v + redefine seq more + end match + end match + end while + if PE.Settings.Characteristics and IMAGE_FILE_DLL + format binary as 'dll' + else + format binary as 'exe' + end if + include 'format/pe.inc' + include 'x64.inc' + use64 +end macro + +macro format?.ELF? variant + match , variant + format binary as 'o' + include 'format/elf32.inc' + include 'p6.inc' + use32 + else match =executable? settings, variant: + match brand =at? base:, settings + ELF.Settings.ABI = brand + ELF.Settings.BaseAddress = base + else match =at? base:, settings + ELF.Settings.BaseAddress = base + else match brand:, settings + ELF.Settings.ABI = brand + end match + include 'format/elfexe.inc' + include 'p6.inc' + use32 + else match =dynamic? settings, variant: + ELF.Settings.Type = ET_DYN + match brand =at? base:, settings + ELF.Settings.ABI = brand + ELF.Settings.BaseAddress = base + else match =at? base:, settings + ELF.Settings.BaseAddress = base + else match brand:, settings + ELF.Settings.ABI = brand + end match + include 'format/elfexe.inc' + include 'p6.inc' + use32 + else + err 'invalid argument' + end match +end macro + +macro format?.ELF64? variant + match , variant + format binary as 'o' + include 'format/elf64.inc' + include 'x64.inc' + use64 + else match =executable? settings, variant: + ELF.Settings.Class = ELFCLASS64 + ELF.Settings.Machine = EM_X86_64 + ELF.Settings.BaseAddress = 400000h + match brand =at? base:, settings + ELF.Settings.ABI = brand + ELF.Settings.BaseAddress = base + else match =at? base:, settings + ELF.Settings.BaseAddress = base + else match brand:, settings + ELF.Settings.ABI = brand + end match + include 'format/elfexe.inc' + include 'x64.inc' + use64 + else match =dynamic? settings, variant: + ELF.Settings.Class = ELFCLASS64 + ELF.Settings.Type = ET_DYN + ELF.Settings.Machine = EM_X86_64 + ELF.Settings.BaseAddress = 400000h + match brand =at? base:, settings + ELF.Settings.ABI = brand + ELF.Settings.BaseAddress = base + else match =at? base:, settings + ELF.Settings.BaseAddress = base + else match brand:, settings + ELF.Settings.ABI = brand + end match + include 'format/elfexe.inc' + include 'x64.inc' + use64 + else + err 'invalid argument' + end match +end macro + +macro format?.ELFx32? variant + match , variant + format binary as 'o' + ELF.Settings.Machine = EM_X86_64 + include 'format/elf32.inc' + include 'x64.inc' + use64 + else match =executable? settings, variant: + ELF.Settings.Class = ELFCLASS32 + ELF.Settings.Machine = EM_X86_64 + ELF.Settings.BaseAddress = 400000h + match brand =at? base:, settings + ELF.Settings.ABI = brand + ELF.Settings.BaseAddress = base + else match =at? base:, settings + ELF.Settings.BaseAddress = base + else match brand:, settings + ELF.Settings.ABI = brand + end match + include 'format/elfexe.inc' + include 'x64.inc' + use64 + else + err 'invalid argument' + end match +end macro + +macro format?.COFF? + format binary as 'obj' + include 'format/coff.inc' + include 'p6.inc' + use32 +end macro + +macro format?.MS? variant + match =COFF?, variant + format binary as 'obj' + COFF.Settings.Characteristics = IMAGE_FILE_32BIT_MACHINE or IMAGE_FILE_LINE_NUMS_STRIPPED or IMAGE_FILE_BYTES_REVERSED_LO + include 'format/coffms.inc' + include 'p6.inc' + use32 + else + err 'invalid argument' + end match +end macro + +macro format?.MS64? variant + match =COFF?, variant + format binary as 'obj' + COFF.Settings.Machine = IMAGE_FILE_MACHINE_AMD64 + COFF.Settings.Characteristics = IMAGE_FILE_32BIT_MACHINE or IMAGE_FILE_LINE_NUMS_STRIPPED or IMAGE_FILE_BYTES_REVERSED_LO + include 'format/coffms.inc' + include 'x64.inc' + use64 + else + err 'invalid argument' + end match +end macro + +macro format?.MachO? variant + match , variant + format binary as 'o' + MachO.Settings.FileType equ MH_OBJECT + include 'format/macho.inc' + include 'p6.inc' + use32 + else match =executable?, variant + MachO.Settings.BaseAddress = 0x1000 + include 'format/macho.inc' + include 'p6.inc' + use32 + else + err 'invalid argument' + end match +end macro + +macro format?.MachO64? variant + MachO.Settings.ProcessorType equ CPU_TYPE_X86_64 + MachO.Settings.ProcessorSubtype equ CPU_SUBTYPE_X86_64_ALL + match , variant + format binary as 'o' + MachO.Settings.FileType equ MH_OBJECT + include 'format/macho.inc' + include 'x64.inc' + use64 + else match =executable?, variant + MachO.Settings.BaseAddress = 0x100000000 + include 'format/macho.inc' + include 'x64.inc' + use64 + else + err 'invalid argument' + end match +end macro + diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/format/macho.inc b/x86_64_sse2_x87/fasm/examples/x86/include/format/macho.inc new file mode 100644 index 0000000..8541efd --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/format/macho.inc @@ -0,0 +1,1175 @@ + +CPU_TYPE_ANY = -1 +CPU_ARCH_ABI64 = 0x1000000 +CPU_TYPE_VAX = 1 +CPU_TYPE_ROMP = 2 +CPU_TYPE_NS32032 = 4 +CPU_TYPE_NS32332 = 5 +CPU_TYPE_MC680x0 = 6 +CPU_TYPE_I386 = 7 +CPU_TYPE_X86_64 = CPU_TYPE_I386 or CPU_ARCH_ABI64 +CPU_TYPE_MIPS = 8 +CPU_TYPE_NS32532 = 9 +CPU_TYPE_HPPA = 11 +CPU_TYPE_ARM = 12 +CPU_TYPE_MC88000 = 13 +CPU_TYPE_SPARC = 14 +CPU_TYPE_I860 = 15 +CPU_TYPE_I860_LITTLE = 16 +CPU_TYPE_RS6000 = 17 +CPU_TYPE_MC98000 = 18 +CPU_TYPE_POWERPC = 18 +CPU_TYPE_POWERPC64 = CPU_TYPE_POWERPC or CPU_ARCH_ABI64 +CPU_TYPE_VEO = 255 + +CPU_SUBTYPE_MASK = 0xff000000 +CPU_SUBTYPE_LIB64 = 0x80000000 + +CPU_SUBTYPE_I386_ALL = 3 +CPU_SUBTYPE_X86_64_ALL = CPU_SUBTYPE_I386_ALL +CPU_SUBTYPE_386 = 3 +CPU_SUBTYPE_486 = 4 +CPU_SUBTYPE_486SX = 4 + 128 +CPU_SUBTYPE_586 = 5 +CPU_SUBTYPE_PENT = 5 + 0 shl 4 +CPU_SUBTYPE_PENTPRO = 6 + 1 shl 4 +CPU_SUBTYPE_PENTII_M3 = 6 + 3 shl 4 +CPU_SUBTYPE_PENTII_M5 = 6 + 5 shl 4 +CPU_SUBTYPE_PENTIUM_4 = 10 + 0 shl 4 + +MH_OBJECT = 0x1 +MH_EXECUTE = 0x2 +MH_FVMLIB = 0x3 +MH_CORE = 0x4 +MH_PRELOAD = 0x5 +MH_DYLIB = 0x6 +MH_DYLINKER = 0x7 +MH_BUNDLE = 0x8 +MH_DYLIB_STUB = 0x9 +MH_DSYM = 0xA +MH_KEXT_BUNDLE = 0xB + +MH_NOUNDEFS = 0x1 +MH_INCRLINK = 0x2 +MH_DYLDLINK = 0x4 +MH_BINDATLOAD = 0x8 +MH_PREBOUND = 0x10 +MH_SPLIT_SEGS = 0x20 +MH_LAZY_INIT = 0x40 +MH_TWOLEVEL = 0x80 +MH_FORCE_FLAT = 0x100 +MH_NOMULTIDEFS = 0x200 +MH_NOFIXPREBINDING = 0x400 +MH_PREBINDABLE = 0x800 +MH_ALLMODSBOUND = 0x1000 +MH_SUBSECTIONS_VIA_SYMBOLS = 0x2000 +MH_CANONICAL = 0x4000 +MH_WEAK_DEFINES = 0x8000 +MH_BINDS_TO_WEAK = 0x10000 +MH_ALLOW_STACK_EXECUTION = 0x20000 +MH_ROOT_SAFE = 0x40000 +MH_SETUID_SAFE = 0x80000 +MH_NO_REEXPORTED_DYLIBS = 0x100000 +MH_PIE = 0x200000 +MH_DEAD_STRIPPABLE_DYLIB = 0x400000 +MH_HAS_TLV_DESCRIPTORS = 0x800000 +MH_NO_HEAP_EXECUTION = 0x1000000 +MH_APP_EXTENSION_SAFE = 0x2000000 + +LC_REQ_DYLD = 0x80000000 +LC_SEGMENT = 0x1 +LC_SYMTAB = 0x2 +LC_SYMSEG = 0x3 +LC_THREAD = 0x4 +LC_UNIXTHREAD = 0x5 +LC_LOADFVMLIB = 0x6 +LC_IDFVMLIB = 0x7 +LC_IDENT = 0x8 +LC_FVMFILE = 0x9 +LC_PREPAGE = 0xa +LC_DYSYMTAB = 0xb +LC_LOAD_DYLIB = 0xc +LC_ID_DYLIB = 0xd +LC_LOAD_DYLINKER = 0xe +LC_ID_DYLINKER = 0xf +LC_PREBOUND_DYLIB = 0x10 +LC_ROUTINES = 0x11 +LC_SUB_FRAMEWORK = 0x12 +LC_SUB_UMBRELLA = 0x13 +LC_SUB_CLIENT = 0x14 +LC_SUB_LIBRARY = 0x15 +LC_TWOLEVEL_HINTS = 0x16 +LC_PREBIND_CKSUM = 0x17 +LC_LOAD_WEAK_DYLIB = 0x18 +LC_SEGMENT_64 = 0x19 +LC_ROUTINES_64 = 0x1a +LC_UUID = 0x1b +LC_RPATH = 0x1c + LC_REQ_DYLD +LC_CODE_SIGNATURE = 0x1d +LC_SEGMENT_SPLIT_INFO = 0x1e +LC_REEXPORT_DYLIB = 0x1f + LC_REQ_DYLD +LC_LAZY_LOAD_DYLIB = 0x20 +LC_ENCRYPTION_INFO = 0x21 +LC_DYLD_INFO = 0x22 +LC_DYLD_INFO_ONLY = 0x22 + LC_REQ_DYLD + +SG_HIGHVM = 0x1 +SG_FVMLIB = 0x2 +SG_NORELOC = 0x4 + +SECTION_TYPE = 0x000000ff +SECTION_ATTRIBUTES = 0xffffff00 + +S_REGULAR = 0x0 +S_ZEROFILL = 0x1 +S_CSTRING_LITERALS = 0x2 +S_4BYTE_LITERALS = 0x3 +S_8BYTE_LITERALS = 0x4 +S_LITERAL_POINTERS = 0x5 +S_NON_LAZY_SYMBOL_POINTERS = 0x6 +S_LAZY_SYMBOL_POINTERS = 0x7 +S_SYMBOL_STUBS = 0x8 +S_MOD_INIT_FUNC_POINTERS = 0x9 +S_MOD_TERM_FUNC_POINTERS = 0x0a +S_COALESCED = 0x0b +S_GB_ZEROFILL = 0x0c +S_INTERPOSING = 0x0d +S_16BYTE_LITERALS = 0x0e +S_DTRACE_DOF = 0x0f +S_LAZY_DYLIB_SYMBOL_POINTERS = 0x10 +S_THREAD_LOCAL_REGULAR = 0x11 +S_THREAD_LOCAL_ZEROFILL = 0x12 +S_THREAD_LOCAL_VARIABLES = 0x13 +S_THREAD_LOCAL_VARIABLE_POINTERS = 0x14 +S_THREAD_LOCAL_INIT_FUNCTION_POINTERS = 0x15 + +SECTION_ATTRIBUTES_USR = 0xff000000 +S_ATTR_PURE_INSTRUCTIONS = 0x80000000 +S_ATTR_NO_TOC = 0x40000000 +S_ATTR_STRIP_STATIC_SYMS = 0x20000000 +S_ATTR_NO_DEAD_STRIP = 0x10000000 +S_ATTR_LIVE_SUPPORT = 0x08000000 +S_ATTR_SELF_MODIFYING_CODE = 0x04000000 +S_ATTR_DEBUG = 0x02000000 + +SECTION_ATTRIBUTES_SYS = 0x00ffff00 +S_ATTR_SOME_INSTRUCTIONS = 0x00000400 +S_ATTR_EXT_RELOC = 0x00000200 +S_ATTR_LOC_RELOC = 0x00000100 + +VM_PROT_NONE = 0x00 +VM_PROT_READ = 0x01 +VM_PROT_WRITE = 0x02 +VM_PROT_EXECUTE = 0x04 +VM_PROT_DEFAULT = VM_PROT_READ or VM_PROT_WRITE +VM_PROT_ALL = VM_PROT_READ or VM_PROT_WRITE or VM_PROT_EXECUTE +VM_PROT_NO_CHANGE = 0x08 +VM_PROT_COPY = 0x10 +VM_PROT_WANTS_COPY = 0x10 + +x86_THREAD_STATE32 = 1 +x86_FLOAT_STATE32 = 2 +x86_EXCEPTION_STATE32 = 3 +x86_THREAD_STATE64 = 4 +x86_FLOAT_STATE64 = 5 +x86_EXCEPTION_STATE64 = 6 +x86_THREAD_STATE = 7 +x86_FLOAT_STATE = 8 +x86_EXCEPTION_STATE = 9 +x86_DEBUG_STATE32 = 10 +x86_DEBUG_STATE64 = 11 +x86_DEBUG_STATE = 12 +THREAD_STATE_NONE = 13 + +N_STAB = 0xe0 +N_PEXT = 0x10 +N_TYPE = 0x0e +N_EXT = 0x01 +N_UNDF = 0x0 +N_ABS = 0x2 +N_SECT = 0xe +N_PBUD = 0xc +N_INDR = 0xa + +NO_SECT = 0 +MAX_SECT = 255 + +REFERENCE_TYPE = 0xf +REFERENCE_FLAG_UNDEFINED_NON_LAZY = 0 +REFERENCE_FLAG_UNDEFINED_LAZY = 1 +REFERENCE_FLAG_DEFINED = 2 +REFERENCE_FLAG_PRIVATE_DEFINED = 3 +REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY = 4 +REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY = 5 +REFERENCED_DYNAMICALLY = 0x0010 + +GENERIC_RELOC_VANILLA = 0 +GENERIC_RELOC_PAIR = 1 +GENERIC_RELOC_SECTDIFF = 2 +GENERIC_RELOC_PB_LA_PTR = 3 +GENERIC_RELOC_LOCAL_SECTDIFF = 4 +GENERIC_RELOC_TLV = 5 + +X86_64_RELOC_UNSIGNED = 0 +X86_64_RELOC_SIGNED = 1 +X86_64_RELOC_BRANCH = 2 +X86_64_RELOC_GOT_LOAD = 3 +X86_64_RELOC_GOT = 4 +X86_64_RELOC_SUBTRACTOR = 5 +X86_64_RELOC_SIGNED_1 = 6 +X86_64_RELOC_SIGNED_2 = 7 +X86_64_RELOC_SIGNED_4 = 8 +X86_64_RELOC_TLV = 9 + +; Basic layer: command headers + +MachO:: + +namespace MachO + + if defined Settings.ProcessorType + CPUTYPE := Settings.ProcessorType + else + CPUTYPE := CPU_TYPE_I386 + end if + + if defined Settings.ProcessorSubtype + CPUSUBTYPE := Settings.ProcessorSubtype + else + if CPUTYPE and CPU_ARCH_ABI64 + CPUSUBTYPE := CPU_SUBTYPE_I386_ALL + CPU_SUBTYPE_LIB64 + else + CPUSUBTYPE := CPU_SUBTYPE_I386_ALL + end if + end if + + if defined Settings.FileType + FILETYPE := Settings.FileType + else + FILETYPE := MH_EXECUTE + end if + + if defined Settings.Flags + FLAGS := Settings.Flags + else if FILETYPE <> MH_OBJECT + FLAGS := MH_NOUNDEFS + MH_DYLDLINK + else + FLAGS := 0 + end if + + if defined Settings.SegmentAlignment + SEGMENT_ALIGNMENT := Settings.SegmentAlignment + else + SEGMENT_ALIGNMENT := 1000h + end if + + if defined Settings.FileAlignment + FILE_ALIGNMENT := Settings.FileAlignment + else + FILE_ALIGNMENT := 1000h + end if + + if defined Settings.HeaderPad + HEADER_PAD := Settings.HeaderPad + else + HEADER_PAD := 16 + end if + + if CPUTYPE and CPU_ARCH_ABI64 + magic dd 0xFEEDFACF + else + magic dd 0xFEEDFACE + end if + cputype dd CPUTYPE + cpusubtype dd CPUSUBTYPE + filetype dd FILETYPE + ncmds dd NUMBER_OF_COMMANDS + sizeofcmds dd SIZE_OF_COMMANDS + flags dd FLAGS + if CPUTYPE and CPU_ARCH_ABI64 + reserved dd ? + end if + + COMMAND_NUMBER = 0 + COMMAND_POSITION = $ + + commands db SIZE_OF_COMMANDS dup 0, HEADER_PAD dup ? + + org $ + + macro command type + if MachO.COMMAND_POSITION > 0 + virtual at MachO.COMMAND_POSITION + MachO.COMMAND_POSITION = 0 + end if + match t, type + if MachO.COMMAND_NUMBER > 0 + repeat 1, p:MachO.COMMAND_NUMBER + MachO.load#p.SIZE := $ - MachO.load#p.cmd + end repeat + end if + MachO.COMMAND_NUMBER = MachO.COMMAND_NUMBER + 1 + MachO.COMMAND = $ + repeat 1, n:MachO.COMMAND_NUMBER + namespace MachO.load#n + cmd dd t + cmdsize dd SIZE + end namespace + end repeat + end match + end macro + + macro text + if MachO.COMMAND_POSITION = 0 + MachO.COMMAND_POSITION = $ + load MachO.COMMAND_DATA:$-$$ from $$ + store MachO.COMMAND_DATA:$-$$ at MachO:$$ + end virtual + end if + end macro + +end namespace + +calminstruction align? boundary,value:? + compute boundary, (boundary-1)-($+boundary-1) mod boundary + arrange value, =db boundary =dup value + assemble value +end calminstruction + +postpone + MachO.text + namespace MachO + if COMMAND_NUMBER > 0 + repeat 1, p:COMMAND_NUMBER + load#p.SIZE := COMMAND_POSITION - MachO.load#p.cmd + end repeat + end if + NUMBER_OF_COMMANDS := COMMAND_NUMBER + SIZE_OF_COMMANDS := COMMAND_POSITION - commands + end namespace +end postpone + +; Intermediate layer: segments and sections + +namespace MachO + + if defined Settings.BaseAddress + + VM_ADDRESS = Settings.BaseAddress + + if CPUTYPE and CPU_ARCH_ABI64 + MachO.command LC_SEGMENT_64 + else + MachO.command LC_SEGMENT + end if + namespace MachO.segment1 + segname emit 16: '__PAGEZERO' + if CPUTYPE and CPU_ARCH_ABI64 + vmaddr dq 0 + vmsize dq VM_ADDRESS + fileoff dq 0 + filesize dq 0 + else + vmaddr dd 0 + vmsize dd VM_ADDRESS + fileoff dd 0 + filesize dd 0 + end if + maxprot dd 0 + initprot dd 0 + nsects dd 0 + flags dd 0 + end namespace + + SEGMENT_NUMBER = 1 + else + VM_ADDRESS = 0 + SEGMENT_NUMBER = 0 + end if + + GLOBAL_SECTION_NUMBER = 0 + TEXT_OFFSET = $% + +end namespace + +macro MachO.segment declaration + MachO.close_segment + + local NAME,VMADDR,VMSIZE,FILEOFF,FILESIZE,MAXPROT,INITPROT,NSECTS + + INITPROT = VM_PROT_NONE + MAXPROT = VM_PROT_ALL + match name attributes, declaration + NAME = name + local sequence + define sequence attributes: + while 1 + match attribute tail, sequence + match =readable?, attribute + INITPROT = INITPROT or VM_PROT_READ + MAXPROT = MAXPROT or VM_PROT_READ + else match =notreadable?, attribute + INITPROT = INITPROT and not VM_PROT_READ + MAXPROT = MAXPROT and not VM_PROT_READ + else match =writeable?, attribute + INITPROT = INITPROT or VM_PROT_WRITE + MAXPROT = MAXPROT or VM_PROT_WRITE + else match =writable?, attribute + INITPROT = INITPROT or VM_PROT_WRITE + MAXPROT = MAXPROT or VM_PROT_WRITE + else match =notwriteable?, attribute + INITPROT = INITPROT and not VM_PROT_WRITE + MAXPROT = MAXPROT and not VM_PROT_WRITE + else match =notwritable?, attribute + INITPROT = INITPROT and not VM_PROT_WRITE + MAXPROT = MAXPROT and not VM_PROT_WRITE + else match =executable?, attribute + INITPROT = INITPROT or VM_PROT_EXECUTE + MAXPROT = MAXPROT or VM_PROT_EXECUTE + else match =notexecutable?, attribute + INITPROT = INITPROT and not VM_PROT_EXECUTE + MAXPROT = MAXPROT and not VM_PROT_EXECUTE + else + err 'unknown attribute "',`attribute,'"' + end match + redefine sequence tail + else + break + end match + end while + else + NAME = definition + end match + if NAME eqtype '' & lengthof NAME > 16 + err 'name too long' + end if + + VMADDR := MachO.VM_ADDRESS + + if $% = MachO.TEXT_OFFSET & FILESIZE > 0 & MachO.FILETYPE <> MH_OBJECT + MachO.FILE_OFFSET = 0 + else + MachO.FILE_OFFSET = $% + end if + + if FILESIZE + FILEOFF := MachO.FILE_OFFSET + else + FILEOFF := 0 + end if + + MachO.SEGMENT_NUMBER = MachO.SEGMENT_NUMBER + 1 + if MachO.CPUTYPE and CPU_ARCH_ABI64 + MachO.command LC_SEGMENT_64 + else + MachO.command LC_SEGMENT + end if + repeat 1, s:MachO.SEGMENT_NUMBER + namespace MachO.segment#s + segname emit 16: NAME + if MachO.CPUTYPE and CPU_ARCH_ABI64 + vmaddr dq VMADDR + vmsize dq VMSIZE + fileoff dq FILEOFF + filesize dq FILESIZE + else + vmaddr dd VMADDR + vmsize dd VMSIZE + fileoff dd FILEOFF + filesize dd FILESIZE + end if + maxprot dd MAXPROT + initprot dd INITPROT + nsects dd NSECTS + flags dd 0 + end namespace + repeat NSECTS + namespace MachO.segment#s#_section#% + sectname emit 16: ? + segname emit 16: ? + if MachO.CPUTYPE and CPU_ARCH_ABI64 + addr dq ? + size dq ? + else + addr dd ? + size dd ? + end if + offset dd ? + align. dd ? + reloff dd ? + nreloc dd ? + flags dd ? + reserved1 dd ? + reserved2 dd ? + if MachO.CPUTYPE and CPU_ARCH_ABI64 + dd ? + end if + end namespace + end repeat + end repeat + + macro MachO.close_segment + MachO.close_section + local OFFSET,NEXT,TOP,FILL + TOP = $% + NSECTS := MachO.SECTION_NUMBER + if MachO.FILE_OFFSET >= $%% + FILL = MachO.FILE_OFFSET + else + FILL = $%% + end if + repeat 1, s:MachO.SEGMENT_NUMBER + repeat NSECTS, n:2 + if % = 1 + load OFFSET from MachO:MachO.segment#s#_section#%.offset + else + OFFSET = NEXT + end if + if % < %% + load NEXT from MachO:MachO.segment#s#_section#n.offset + else + NEXT = TOP + end if + if OFFSET >= $%% + store S_ZEROFILL at MachO:MachO.segment#s#_section#%.flags + store 0 at MachO:MachO.segment#s#_section#%.size + else + FILL = NEXT + end if + end repeat + end repeat + FILESIZE := FILL - MachO.FILE_OFFSET + FILL = FILL - $%% + MachO.SECTION_BYPASS = 1 + section MachO.VM_ADDRESS + FILESIZE - FILL + restore MachO.SECTION_BYPASS + db FILL dup 0 + if FILESIZE + rb (MachO.FILE_ALIGNMENT-$%) and (MachO.FILE_ALIGNMENT-1) + end if + MachO.VM_ADDRESS = MachO.VM_ADDRESS + TOP - MachO.FILE_OFFSET + MachO.VM_ADDRESS = MachO.VM_ADDRESS + (MachO.SEGMENT_ALIGNMENT-MachO.VM_ADDRESS) and (MachO.SEGMENT_ALIGNMENT-1) + VMSIZE := MachO.VM_ADDRESS - VMADDR + purge MachO.close_segment + end macro + + MachO.text + org VMADDR + $% - MachO.FILE_OFFSET + MachO.SECTION_NUMBER = 0 + +end macro + +macro MachO.section declaration + if MachO.SEGMENT_NUMBER = 0 + MachO.segment '__TEXT' readable writable executable + end if + MachO.close_section + + local SECTNAME,SEGNAME,FLAGS,ALIGN,RESERVED1,RESERVED2 + + FLAGS = S_REGULAR + ALIGN = 0 + RESERVED1 = 0 + RESERVED2 = 0 + + local sequence + match segname:sectname tail, declaration: + SECTNAME = sectname + SEGNAME = segname + define sequence tail + else match name tail, declaration: + SECTNAME = name + repeat 1, s:MachO.SEGMENT_NUMBER + load SEGNAME from MachO:MachO.segment#s.segname + end repeat + define sequence tail + end match + + while 1 + match :, sequence + break + else match =align? boundary tail, sequence + ALIGN = bsf boundary + if bsf boundary <> bsr boundary + err 'invalid alignment value' + end if + redefine sequence tail + else match =flags?(value) tail, sequence + FLAGS = value + redefine sequence tail + else match =reserved1?(value) tail, sequence + RESERVED1 = value + redefine sequence tail + else match =reserved2?(value) tail, sequence + RESERVED2 = value + redefine sequence tail + else + err 'invalid arguments' + end match + end while + + MachO.text + if ALIGN + align 1 shl ALIGN + end if + + MachO.SECTION_ALIGN = 1 shl ALIGN + + MachO.SECTION_NUMBER = MachO.SECTION_NUMBER + 1 + MachO.GLOBAL_SECTION_NUMBER = MachO.GLOBAL_SECTION_NUMBER + 1 + + repeat 1, s:MachO.SEGMENT_NUMBER, t:MachO.SECTION_NUMBER + if ~ defined MachO.segment#s#_section#t.sectname + MachO.command + namespace MachO.segment#s#_section#t + sectname emit 16: ? + segname emit 16: ? + if MachO.CPUTYPE and CPU_ARCH_ABI64 + addr dq ? + size dq ? + else + addr dd ? + size dd ? + end if + offset dd ? + align. dd ? + reloff dd ? + nreloc dd ? + flags dd ? + reserved1 dd ? + reserved2 dd ? + if MachO.CPUTYPE and CPU_ARCH_ABI64 + dd ? + end if + end namespace + MachO.text + end if + store SECTNAME at MachO:MachO.segment#s#_section#t.sectname + store SEGNAME at MachO:MachO.segment#s#_section#t.segname + store $ at MachO:MachO.segment#s#_section#t.addr + store $% at MachO:MachO.segment#s#_section#t.offset + store FLAGS at MachO:MachO.segment#s#_section#t.flags + store ALIGN at MachO:MachO.segment#s#_section#t.align + store RESERVED1 at MachO:MachO.segment#s#_section#t.reserved1 + store RESERVED2 at MachO:MachO.segment#s#_section#t.reserved2 + end repeat + +end macro + +macro MachO.close_section + MachO.text + if MachO.SECTION_NUMBER + local SIZE + repeat 1, s:MachO.SEGMENT_NUMBER, t:MachO.SECTION_NUMBER + load OFFSET from MachO:MachO.segment#s#_section#t.offset + store $%-OFFSET at MachO:MachO.segment#s#_section#t.size + end repeat + end if +end macro + +macro MachO.close_segment +end macro + +postpone + MachO.close_segment + if MachO.FILETYPE <> MH_OBJECT & $%% < 1000h + store 0:byte at $-1 ; enforce minimum file size for OS X 10.10.5 and higher + end if +end postpone + +macro segment? args& + MachO.segment args +end macro + +macro section? args& + if defined MachO.SECTION_BYPASS + section args + else + MachO.section args + end if +end macro + +macro entry? regs& + iterate reg, regs + match name:value, reg + MachO.thread.name? := value + else if MachO.CPUTYPE = CPU_TYPE_I386 + MachO.thread.eip? := reg + else if MachO.CPUTYPE = CPU_TYPE_X86_64 + MachO.thread.rip? := reg + end if + end iterate + MachO.command LC_UNIXTHREAD + if MachO.CPUTYPE = CPU_TYPE_I386 + MachO.thread.flavor dd x86_THREAD_STATE32 + iterate name, eax,ebx,ecx,edx,edi,esi,ebp,esp,ss,eflags,eip,cs,ds,es,fs,gs + if % = 1 + MachO.thread.count dd %% + end if + if defined MachO.thread.name? + dd MachO.thread.name? + else + dd ? + end if + end iterate + else if MachO.CPUTYPE = CPU_TYPE_X86_64 + MachO.thread.flavor dd x86_THREAD_STATE64 + iterate name, rax,rbx,rcx,rdx,rdi,rsi,rbp,rsp,r8,r9,r10,r11,r12,r13,r14,r15,rip,rflags,cs,fs,gs + if % = 1 + MachO.thread.count dd %%*2 + end if + if defined MachO.thread.name? + dq MachO.thread.name? + else + dq ? + end if + end iterate + else + err 'CPU not supported' + end if + MachO.text +end macro + +; Upper layer: symbol tables + +define macroBuilder? + +macro macroBuilder? declaration& + macro macroBuilder?.definition + esc macro declaration + end macro +end macro + +macro macroBuilder?.line? content& + macro macroBuilder?.definition + macroBuilder?.definition + content + end macro +end macro + +macro macroBuilder?.end? + macroBuilder?.definition + esc end macro +end macro + +if MachO.FILETYPE <> MH_OBJECT + + namespace MachO + NSYMS = 0 + LIB_NUMBER = 0 + end namespace + + macro interpreter? path + MachO.command LC_LOAD_DYLINKER + namespace MachO.dylinker_command + lc_str dd dylinker-MachO.COMMAND + dylinker db path,0 + if MachO.CPUTYPE and CPU_ARCH_ABI64 + align 8 + else + align 4 + end if + end namespace + end macro + + macro uses? lib& + MachO.LIB_NUMBER = MachO.LIB_NUMBER + 1 + MachO.command LC_LOAD_DYLIB + repeat 1, l:MachO.LIB_NUMBER + namespace MachO.dylib#l#_command + lc_str dd dylib-MachO.COMMAND + timestamp dd 2 + match path (a.b.c=,x.y.z), lib + current_version dd (x and 0FFFFh) shl 16 + y shl 8 + z + compatibility_version dd (a and 0FFFFh) shl 16 + b shl 8 + c + dylib db path,0 + else + current_version dd 10000h + compatibility_version dd 10000h + dylib db lib,0 + end match + if MachO.CPUTYPE and CPU_ARCH_ABI64 + align 8 + else + align 4 + end if + end namespace + end repeat + end macro + + macro import? definitions& + iterate , definitions + MachO.NSYMS = MachO.NSYMS + 1 + define MachO.nlist name + name.imported := 1 + name.type := N_EXT + name.desc := REFERENCE_FLAG_UNDEFINED_NON_LAZY + define name.str string + end iterate + end macro + + MachO.__TEXT = 0 + MachO.__DATA = 0 + + macro segment? args& + MachO.segment args + if MachO.NSYMS + repeat 1, s:MachO.SEGMENT_NUMBER + load MachO.SEGNAME from MachO:MachO.segment#s.segname + end repeat + if ~MachO.__TEXT & MachO.SEGNAME = '__TEXT' + MachO.__TEXT = 1 + MachO.__stubs + else if ~MachO.__DATA & MachO.SEGNAME = '__DATA' + MachO.__DATA = 1 + MachO.__nl_symbol_ptr + end if + end if + end macro + + postpone + if MachO.NSYMS + + macroBuilder MachO.__stubs + + macroBuilder.line section '__stubs' flags(S_SYMBOL_STUBS+S_ATTR_SOME_INSTRUCTIONS+S_ATTR_PURE_INSTRUCTIONS) reserved1(0) reserved2(MachO.JUMP_SIZE) align 16 + + irpv sym, MachO.nlist + if sym.imported + macroBuilder.line sym: jmp [sym.ptr] + if % = 1 + macroBuilder.line MachO.JUMP_SIZE := $ - sym + end if + end if + end irpv + + macroBuilder.end + + macroBuilder MachO.__nl_symbol_ptr + + if MachO.CPUTYPE and CPU_ARCH_ABI64 + macroBuilder.line section '__nl_symbol_ptr' flags(S_NON_LAZY_SYMBOL_POINTERS) reserved1(0) align 8 + else + macroBuilder.line section '__nl_symbol_ptr' flags(S_NON_LAZY_SYMBOL_POINTERS) reserved1(0) align 4 + end if + + irpv sym, MachO.nlist + if sym.imported + if MachO.CPUTYPE and CPU_ARCH_ABI64 + macroBuilder.line sym.ptr dq 0 + else + macroBuilder.line sym.ptr dd 0 + end if + end if + end irpv + + macroBuilder.end + + if ~MachO.__TEXT + segment '__TEXT' readable executable + end if + + if ~MachO.__DATA + segment '__DATA' readable writable + end if + + segment '__LINKEDIT' readable + + MachO.SYMOFF := $% + irpv sym, MachO.nlist + namespace MachO.nlist#% + n_strx dd sym.strx + n_type db sym.type + n_sect db 0 + n_desc dw sym.desc + if MachO.CPUTYPE and CPU_ARCH_ABI64 + n_value dq sym + else + n_value dd sym + end if + end namespace + end irpv + + MachO.INDIRECTSYMOFF := $% + irpv sym, MachO.nlist + if sym.imported + dd %-1 + end if + end irpv + + MachO.STROFF := $% + db 20h,0 + irpv sym, MachO.nlist + sym.strx := $% - MachO.STROFF + db string sym.str, 0 + end irpv + MachO.STRSIZE := $% - MachO.STROFF + + MachO.command LC_SYMTAB + namespace MachO.symtab_command + symoff dd SYMOFF + nsyms dd NSYMS + stroff dd STROFF + strsize dd STRSIZE + end namespace + + MachO.command LC_DYSYMTAB + namespace MachO.dysymtab_command + ilocalsym dd 0 + nlocalsym dd 0 + iextdefsym dd 0 + nextdefsym dd 0 + iundefsym dd 0 + nundefsym dd NSYMS + tocoff dd 0 + ntoc dd 0 + modtaboff dd 0 + nmodtab dd 0 + extrefsymoff dd 0 + nextrefsyms dd 0 + indirectsymoff dd INDIRECTSYMOFF + nindirectsyms dd NSYMS + extreloff dd 0 + nextrel dd 0 + locreloff dd 0 + nlocrel dd 0 + end namespace + + end if + end postpone + +else + + namespace MachO + + element MachO.section? + element MachO.symbol? + + VM_ADDRESS = 0 + + define nlist null + null.value := 0 + null.type := 0 + null.sect := 0 + null.desc := 0 + null.str := '' + + NSYMS = 1 + NRELOC = 0 + + end namespace + + segment 0 readable writable executable + + macro MachO.section declaration + if MachO.SECTION_NUMBER + org 0 scaleof (1 metadataof $) + 0 scaleof $ + end if + MachO.section declaration + local sym + element sym : MachO.GLOBAL_SECTION_NUMBER * MachO.section + $ + org sym + MachO.SECTION_OFFSET = $% + MachO.SECTION_REL_INDEX = MachO.NRELOC + repeat 1, s:MachO.SEGMENT_NUMBER, t:MachO.SECTION_NUMBER + store MachO.RELOFF+MachO.SECTION_REL_INDEX*8 at MachO:MachO.segment#s#_section#t.reloff + end repeat + end macro + + macro MachO.close_section + MachO.close_section + if MachO.SECTION_NUMBER + repeat 1, s:MachO.SEGMENT_NUMBER, t:MachO.SECTION_NUMBER + store MachO.NRELOC-MachO.SECTION_REL_INDEX at MachO:MachO.segment#s#_section#t.nreloc + end repeat + end if + end macro + + macro public? declaration* + local name + define MachO.nlist name + match value =as? namestr, declaration + name = value + define name.str string namestr + else + name = declaration + define name.str `declaration + end match + if name relativeto 1 elementof name & 1 elementof (1 metadataof name) eq MachO.section + name.value := 0 scaleof (1 metadataof name) + 0 scaleof name + name.type := N_SECT + N_EXT + name.sect := 1 scaleof (1 metadataof name) + else + name.value := name + name.type := N_ABS + name.sect := 0 + end if + name.desc := REFERENCE_FLAG_DEFINED + MachO.NSYMS = MachO.NSYMS + 1 + end macro + + macro extrn? declaration* + match namestr =as? sym, declaration + define MachO.nlist sym + define sym.str string namestr + else + define MachO.nlist declaration + define declaration.str `declaration + end match + match sym, MachO.nlist + element sym : MachO.NSYMS * MachO.symbol + sym.type := N_EXT + sym.sect := 0 + sym.desc := 0 + sym.value := 0 + end match + MachO.NSYMS = MachO.NSYMS + 1 + end macro + + macro dword? value + local v, o + v = value + if ~ v relativeto 0 & v relativeto 1 elementof v & (1 elementof (1 metadataof v) eq MachO.section | 1 elementof (1 metadataof v) eq MachO.symbol) + MachO.reloc =: $% - MachO.SECTION_OFFSET + (1 scaleof (1 metadataof v)) shl 32 + 2 shl 57 + GENERIC_RELOC_VANILLA shl 60 + if 1 elementof (1 metadataof v) eq MachO.symbol + MachO.reloc = MachO.reloc + 1 shl 59 + end if + o = $% + emit dword: 0 scaleof (1 metadataof v) + 0 scaleof v + if $% > o + MachO.NRELOC = MachO.NRELOC + 1 + else + restore MachO.reloc + end if + else if ~ v relativeto 0 & (v + $) relativeto 1 elementof (v + $) & (1 elementof (1 metadataof (v + $)) eq MachO.section | 1 elementof (1 metadataof (v + $)) eq MachO.symbol) + if MachO.CPUTYPE = CPU_TYPE_X86_64 + if 1 elementof (1 metadataof (v + $)) eq MachO.symbol + v = v + $ + 4 + else + v = v + 0 scaleof (1 metadataof v) - 0 scaleof (1 metadataof $) + end if + MachO.reloc =: $% - MachO.SECTION_OFFSET + (1 scaleof (1 metadataof (v + $))) shl 32 + 1 shl 56 + 2 shl 57 + X86_64_RELOC_SIGNED_4 shl 60 + else + v = v + 0 scaleof (1 metadataof v) - 0 scaleof (1 metadataof $) + MachO.reloc =: $% - MachO.SECTION_OFFSET + (1 scaleof (1 metadataof (v + $))) shl 32 + 1 shl 56 + 2 shl 57 + GENERIC_RELOC_VANILLA shl 60 + end if + if 1 elementof (1 metadataof (v + $)) eq MachO.symbol + MachO.reloc = MachO.reloc + 1 shl 59 + end if + o = $% + emit dword: 0 scaleof v + if $% > o + MachO.NRELOC = MachO.NRELOC + 1 + else + restore MachO.reloc + end if + else + emit dword: v + end if + end macro + + postpone + if MachO.NSYMS + + MachO.close_section + macro MachO.close_section + MachO.text + end macro + + rb (-$%) and 11b + + MachO.RELOFF := $% + irpv rel, MachO.reloc + dq rel + end irpv + + MachO.SYMOFF := $% + irpv sym, MachO.nlist + namespace MachO.nlist#% + n_strx dd sym.strx + n_type db sym.type + n_sect db sym.sect + n_desc dw sym.desc + if MachO.CPUTYPE and CPU_ARCH_ABI64 + n_value dq sym.value + else + n_value dd sym.value + end if + end namespace + end irpv + + MachO.STROFF := $% + irpv sym, MachO.nlist + sym.strx := $% - MachO.STROFF + db string sym.str, 0 + end irpv + MachO.STRSIZE := $% - MachO.STROFF + + MachO.command LC_SYMTAB + namespace MachO.symtab_command + symoff dd SYMOFF + nsyms dd NSYMS + stroff dd STROFF + strsize dd STRSIZE + end namespace + + end if + end postpone + + calminstruction align? boundary,value:? + check $ relativeto 0 | MachO.SECTION_ALIGN mod (boundary) = 0 + jyes allowed + arrange value, =err 'section not aligned enough' + assemble value + exit + allowed: + compute boundary, (boundary-1)-(0 scaleof $+boundary-1) mod boundary + arrange value, =db boundary =dup value + assemble value + end calminstruction + + calminstruction dd? definitions& + local value, n + start: + match value=,definitions, definitions, () + jyes recognize + match value, definitions + arrange definitions, + recognize: + match n =dup? value, value, () + jyes duplicate + match ?, value + jyes reserve + arrange value, =dword value + assemble value + next: + match , definitions + jno start + take , definitions + take definitions, definitions + jyes next + exit + reserve: + arrange value, =dd ? + assemble value + jump next + duplicate: + match (value), value + stack: + check n + jno next + take definitions, value + arrange value, definitions + compute n, n - 1 + jump stack + end calminstruction + + calminstruction (label) dd? definitions& + local cmd + arrange cmd, =label label : =dword + assemble cmd + arrange cmd, =dd definitions + assemble cmd + end calminstruction + +end if diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/format/mz.inc b/x86_64_sse2_x87/fasm/examples/x86/include/format/mz.inc new file mode 100644 index 0000000..c8b0b20 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/format/mz.inc @@ -0,0 +1,222 @@ + +MZ:: +.signature dw "MZ" +.bytes_in_last_page dw .LENGTH and 1FFh +.number_of_pages dw (.LENGTH-1) shr 9 + 1 +.number_of_relocations dw .NUMBER_OF_RELOCATIONS +.number_of_header_paragraphs dw .HEADER_LENGTH shr 4 +.minimum_heap dw (.LENGTH+.RESERVED_LENGTH-1) shr 4 - (.LENGTH-1) shr 4 +.maximum_heap dw 0FFFFh +.initial_ss dw 0 +.initial_sp dw 0 +.checksum dw 0 +.initial_ip dw 0 +.initial_cs dw 0 +.relocations_offset dw .relocations +.overlay_number dw 0 +.relocations dw .NUMBER_OF_RELOCATIONS dup (?,?) + rb 0Fh - ($%+0Fh) and 0Fh +.HEADER_LENGTH = $ +.RELOCATION_INDEX = 0 + +.ENTRY_DEFINED = 0 +.HEAP_DEFINED = 0 +.STACK_DEFINED = 0 +.STACK_LENGTH = 1000h + +org 0 + +macro entry? definition + local v + if MZ.ENTRY_DEFINED + err 'setting already specified' + else match seg:offs, definition + v = seg + if v relativeto MZ.segment + store v - MZ.segment : word at MZ : MZ.initial_cs + else + err 'incorrect segment' + end if + v = offs + if v >= 0 & v < 10000h + store v : word at MZ : MZ.initial_ip + else + err 'value out of range' + end if + MZ.ENTRY_DEFINED = 1 + else + err 'invalid argument' + end match +end macro + +macro heap? definition + local v,min + if MZ.HEAP_DEFINED + err 'setting already specified' + else + v = definition + if v >= 0 & v < 10000h + load min : word from MZ : MZ.minimum_heap + v = v + min + if v > 0FFFFh + v = 0FFFFh + end if + store v : word at MZ : MZ.maximum_heap + else + err 'value out of range' + end if + MZ.HEAP_DEFINED = 1 + end if +end macro + +macro stack? definition + local v + if MZ.STACK_DEFINED + err 'setting already specified' + else match seg:offs, definition + v = seg + if v relativeto MZ.segment + store v - MZ.segment : word at MZ : MZ.initial_ss + else + err 'incorrect segment' + end if + v = offs + if v >= 0 & v < 10000h + store v : word at MZ : MZ.initial_sp + else + err 'value out of range' + end if + MZ.STACK_DEFINED = 1 + MZ.STACK_LENGTH = 0 + else + MZ.STACK_DEFINED = 1 + MZ.STACK_LENGTH = definition + end match +end macro + +element MZ.segment + +macro segment? definition + rb 0Fh - ($%+0Fh) and 0Fh + match name =use16?, definition + name := MZ.segment + ($%-MZ.HEADER_LENGTH) shr 4 + use16 + else match name =use32?, definition + name := MZ.segment + ($%-MZ.HEADER_LENGTH) shr 4 + use32 + else match name, definition + name := MZ.segment + ($%-MZ.HEADER_LENGTH) shr 4 + end match + org 0 +end macro + +calminstruction calminstruction?.init? var*, val:0 + compute val, val + publish var, val +end calminstruction + +calminstruction calminstruction?.initsym? var*, val& + publish var, val +end calminstruction + +calminstruction calminstruction?.unique? name + local counter, buffer + init counter + compute counter, counter + 1 + arrange buffer, name#counter + publish name, buffer +end calminstruction + +calminstruction calminstruction?.asm? line& + local tmp, ln, buffer + initsym tmp, unique ln + assemble tmp + publish ln, line + arrange buffer, =assemble ln + assemble buffer +end calminstruction + +iterate , dw,word, dd,dword + + calminstruction word? value + compute value, value + check value relativeto MZ.segment + jno plain + local offset + compute offset, $% + asm emit word: value - MZ.segment + check $% > offset + jno done + compute offset, offset - MZ.HEADER_LENGTH + compute offset, offset and 0FFFFh + (offset and not 0FFFFh) shl 12 + asm store offset:4 at MZ : MZ.relocations + MZ.RELOCATION_INDEX shl 2 + compute MZ.RELOCATION_INDEX, MZ.RELOCATION_INDEX + 1 + done: + exit + plain: + asm emit word: value + end calminstruction + + calminstruction dw? definitions& + local value, n + start: + match value=,definitions, definitions, () + jyes recognize + match value, definitions + arrange definitions, + recognize: + match n =dup? value, value, () + jyes duplicate + match ?, value + jyes reserve + arrange value, =word value + assemble value + next: + match , definitions + jno start + take , definitions + take definitions, definitions + jyes next + exit + reserve: + arrange value, =dw ? + assemble value + jump next + duplicate: + match (value), value + stack: + check n + jno next + take definitions, value + arrange value, definitions + compute n, n - 1 + jump stack + end calminstruction + + calminstruction (label) dw? definitions& + local cmd + arrange cmd, =label label : =word + assemble cmd + arrange cmd, =dw definitions + assemble cmd + end calminstruction + +end iterate + +calminstruction align? boundary,value:? + compute boundary, (boundary-1)-((0 scaleof $)+boundary-1) mod boundary + arrange value, =db boundary =dup value + assemble value +end calminstruction + +postpone + if MZ.STACK_LENGTH + rb 0Fh - ($%+0Fh) and 0Fh + store ($%-MZ.HEADER_LENGTH) shr 4 : word at MZ : MZ.initial_ss + rb MZ.STACK_LENGTH + store MZ.STACK_LENGTH : word at MZ : MZ.initial_sp + end if + MZ.LENGTH = $%% + MZ.RESERVED_LENGTH = $%-$%% + MZ.NUMBER_OF_RELOCATIONS = MZ.RELOCATION_INDEX +end postpone diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/format/pe.inc b/x86_64_sse2_x87/fasm/examples/x86/include/format/pe.inc new file mode 100644 index 0000000..40d812f --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/format/pe.inc @@ -0,0 +1,1078 @@ + +IMAGE_FILE_MACHINE_UNKNOWN = 0x0 +IMAGE_FILE_MACHINE_AM33 = 0x1D3 +IMAGE_FILE_MACHINE_AMD64 = 0x8664 +IMAGE_FILE_MACHINE_ARM = 0x1C0 +IMAGE_FILE_MACHINE_ARMNT = 0x1C4 +IMAGE_FILE_MACHINE_ARM64 = 0xAA64 +IMAGE_FILE_MACHINE_EBC = 0xEBC +IMAGE_FILE_MACHINE_I386 = 0x14C +IMAGE_FILE_MACHINE_IA64 = 0x200 +IMAGE_FILE_MACHINE_M32R = 0x9041 +IMAGE_FILE_MACHINE_MIPS16 = 0x266 +IMAGE_FILE_MACHINE_MIPSFPU = 0x366 +IMAGE_FILE_MACHINE_MIPSFPU16 = 0x466 +IMAGE_FILE_MACHINE_POWERPC = 0x1F0 +IMAGE_FILE_MACHINE_POWERPCFP = 0x1F1 +IMAGE_FILE_MACHINE_R4000 = 0x166 +IMAGE_FILE_MACHINE_SH3 = 0x1A2 +IMAGE_FILE_MACHINE_SH3DSP = 0x1A3 +IMAGE_FILE_MACHINE_SH4 = 0x1A6 +IMAGE_FILE_MACHINE_SH5 = 0x1A8 +IMAGE_FILE_MACHINE_THUMB = 0x1C2 +IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x169 + +IMAGE_FILE_RELOCS_STRIPPED = 0x0001 +IMAGE_FILE_EXECUTABLE_IMAGE = 0x0002 +IMAGE_FILE_LINE_NUMS_STRIPPED = 0x0004 +IMAGE_FILE_LOCAL_SYMS_STRIPPED = 0x0008 +IMAGE_FILE_AGGRESSIVE_WS_TRIM = 0x0010 +IMAGE_FILE_LARGE_ADDRESS_AWARE = 0x0020 +IMAGE_FILE_BYTES_REVERSED_LO = 0x0080 +IMAGE_FILE_32BIT_MACHINE = 0x0100 +IMAGE_FILE_DEBUG_STRIPPED = 0x0200 +IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP = 0x0400 +IMAGE_FILE_NET_RUN_FROM_SWAP = 0x0800 +IMAGE_FILE_SYSTEM = 0x1000 +IMAGE_FILE_DLL = 0x2000 +IMAGE_FILE_UP_SYSTEM_ONLY = 0x4000 +IMAGE_FILE_BYTES_REVERSED_HI = 0x8000 + +IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA = 0x0020 +IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE = 0x0040 +IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY = 0x0080 +IMAGE_DLLCHARACTERISTICS_NX_COMPAT = 0x0100 +IMAGE_DLLCHARACTERISTICS_NO_ISOLATION = 0x0200 +IMAGE_DLLCHARACTERISTICS_NO_SEH = 0x0400 +IMAGE_DLLCHARACTERISTICS_NO_BIND = 0x0800 +IMAGE_DLLCHARACTERISTICS_APPCONTAINER = 0x1000 +IMAGE_DLLCHARACTERISTICS_WDM_DRIVER = 0x2000 +IMAGE_DLLCHARACTERISTICS_GUARD_CF = 0x4000 +IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE = 0x8000 + +IMAGE_SUBSYSTEM_UNKNOWN = 0 +IMAGE_SUBSYSTEM_NATIVE = 1 +IMAGE_SUBSYSTEM_WINDOWS_GUI = 2 +IMAGE_SUBSYSTEM_WINDOWS_CUI = 3 +IMAGE_SUBSYSTEM_POSIX_CUI = 7 +IMAGE_SUBSYSTEM_WINDOWS_CE_GUI = 9 +IMAGE_SUBSYSTEM_EFI_APPLICATION = 10 +IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER = 11 +IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER = 12 +IMAGE_SUBSYSTEM_EFI_ROM = 13 +IMAGE_SUBSYSTEM_XBOX = 14 + +IMAGE_SCN_TYPE_NO_PAD = 0x00000008 +IMAGE_SCN_CNT_CODE = 0x00000020 +IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040 +IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080 +IMAGE_SCN_LNK_OTHER = 0x00000100 +IMAGE_SCN_LNK_INFO = 0x00000200 +IMAGE_SCN_LNK_REMOVE = 0x00000800 +IMAGE_SCN_LNK_COMDAT = 0x00001000 +IMAGE_SCN_GPREL = 0x00008000 +IMAGE_SCN_MEM_PURGEABLE = 0x00020000 +IMAGE_SCN_MEM_16BIT = 0x00020000 +IMAGE_SCN_MEM_LOCKED = 0x00040000 +IMAGE_SCN_MEM_PRELOAD = 0x00080000 +IMAGE_SCN_ALIGN_1BYTES = 0x00100000 +IMAGE_SCN_ALIGN_2BYTES = 0x00200000 +IMAGE_SCN_ALIGN_4BYTES = 0x00300000 +IMAGE_SCN_ALIGN_8BYTES = 0x00400000 +IMAGE_SCN_ALIGN_16BYTES = 0x00500000 +IMAGE_SCN_ALIGN_32BYTES = 0x00600000 +IMAGE_SCN_ALIGN_64BYTES = 0x00700000 +IMAGE_SCN_ALIGN_128BYTES = 0x00800000 +IMAGE_SCN_ALIGN_256BYTES = 0x00900000 +IMAGE_SCN_ALIGN_512BYTES = 0x00A00000 +IMAGE_SCN_ALIGN_1024BYTES = 0x00B00000 +IMAGE_SCN_ALIGN_2048BYTES = 0x00C00000 +IMAGE_SCN_ALIGN_4096BYTES = 0x00D00000 +IMAGE_SCN_ALIGN_8192BYTES = 0x00E00000 +IMAGE_SCN_LNK_NRELOC_OVFL = 0x01000000 +IMAGE_SCN_MEM_DISCARDABLE = 0x02000000 +IMAGE_SCN_MEM_NOT_CACHED = 0x04000000 +IMAGE_SCN_MEM_NOT_PAGED = 0x08000000 +IMAGE_SCN_MEM_SHARED = 0x10000000 +IMAGE_SCN_MEM_EXECUTE = 0x20000000 +IMAGE_SCN_MEM_READ = 0x40000000 +IMAGE_SCN_MEM_WRITE = 0x80000000 + +IMAGE_REL_BASED_ABSOLUTE = 0 +IMAGE_REL_BASED_HIGH = 1 +IMAGE_REL_BASED_LOW = 2 +IMAGE_REL_BASED_HIGHLOW = 3 +IMAGE_REL_BASED_HIGHADJ = 4 +IMAGE_REL_BASED_DIR64 = 10 + +calminstruction align? boundary,value:? + check $ relativeto 0 | ( $ relativeto PE.RELOCATION & PE.SECTION_ALIGNMENT mod boundary = 0 ) + jyes allowed + arrange value, =err 'section not aligned enough' + assemble value + exit + allowed: + compute boundary, (boundary-1)-((0 scaleof $)+boundary-1) mod boundary + arrange value, =db boundary =dup value + assemble value +end calminstruction + +PE:: + +namespace PE + + if defined Settings.Magic + MAGIC = Settings.Magic + else + MAGIC = 0x10B + end if + + if defined Settings.Machine + MACHINE = Settings.Machine + else + MACHINE = IMAGE_FILE_MACHINE_I386 + end if + + if defined Settings.Characteristics + CHARACTERISTICS = Settings.Characteristics + else + CHARACTERISTICS = IMAGE_FILE_EXECUTABLE_IMAGE or IMAGE_FILE_32BIT_MACHINE + end if + + if defined Settings.DllCharacteristics + DLL_CHARACTERISTICS = Settings.DllCharacteristics + else + DLL_CHARACTERISTICS = 0 + end if + + if defined Settings.Subsystem + SUBSYSTEM = Settings.Subsystem + else + SUBSYSTEM = IMAGE_SUBSYSTEM_WINDOWS_CUI + end if + + if defined Settings.MajorSubsystemVersion + MAJOR_SUBSYSTEM_VERSION = Settings.MajorSubsystemVersion + else + MAJOR_SUBSYSTEM_VERSION = 3 + end if + + if defined Settings.MinorSubsystemVersion + MINOR_SUBSYSTEM_VERSION = Settings.MinorSubsystemVersion + else + MINOR_SUBSYSTEM_VERSION = 10 + end if + + if defined Fixups + element RELOCATION + DLL_CHARACTERISTICS = DLL_CHARACTERISTICS or IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE + else + RELOCATION := 0 + CHARACTERISTICS = CHARACTERISTICS or IMAGE_FILE_RELOCS_STRIPPED + end if + + if defined Settings.ImageBase + IMAGE_BASE := RELOCATION + Settings.ImageBase + else + IMAGE_BASE := RELOCATION + 400000h + end if + + if defined Settings.SectionAlignment + SECTION_ALIGNMENT := Settings.SectionAlignment + else + SECTION_ALIGNMENT := 1000h + end if + + if defined Settings.FileAlignment + FILE_ALIGNMENT := Settings.FileAlignment + else + FILE_ALIGNMENT := 512 + end if + + if defined Settings.LegacyHeaders + LEGACY_HEADERS := Settings.LegacyHeaders + else + LEGACY_HEADERS := 1 + end if + + NUMBER_OF_DIRECTORIES := 16 + + if defined Settings.Stub + + virtual at 0 + file Settings.Stub + if $ >= 1Ch + load SIGNATURE : word from 0 + if SIGNATURE = "MZ" | SIGNATURE = "ZM" + StubTemplate:: + end if + end if + end virtual + + if defined StubTemplate + + load .BYTES_IN_LAST_PAGE : word from StubTemplate:2 + load .NUMBER_OF_PAGES : word from StubTemplate:4 + .TEMPLATE_LENGTH = .NUMBER_OF_PAGES shl 9 - (-.BYTES_IN_LAST_PAGE) and 1FFh + + load .RELOCATIONS_OFFSET : word from StubTemplate:18h + if .RELOCATIONS_OFFSET >= 40h + file Settings.Stub,.TEMPLATE_LENGTH + else + load .NUMBER_OF_RELOCATIONS : word from StubTemplate:6 + .RELOCATIONS_LENGTH = .NUMBER_OF_RELOCATIONS shl 2 + load .NUMBER_OF_HEADER_PARAGRAPHS : word from StubTemplate:8 + .TEMPLATE_HEADER_LENGTH = .NUMBER_OF_HEADER_PARAGRAPHS shl 4 + + file Settings.Stub,1Ch + rb 40h - $ + file Settings.Stub:.RELOCATIONS_OFFSET,.RELOCATIONS_LENGTH + align 16 + .HEADER_LENGTH = $ + file Settings.Stub:.TEMPLATE_HEADER_LENGTH,.TEMPLATE_LENGTH-.TEMPLATE_HEADER_LENGTH + .LENGTH = $ + + store 40h : word at 18h + store .HEADER_LENGTH shr 4 : word at 8 + store .LENGTH and 1FFh : word at 2 + store (.LENGTH-1) shr 9 + 1 : word at 4 + end if + + store Header : dword at 3Ch + + else + + Stub: + .signature dw "MZ" + .bytes_in_last_page dw .LENGTH and 1FFh + .number_of_pages dw (.LENGTH-1) shr 9 + 1 + .number_of_relocations dw 0 + .number_of_header_paragraphs dw .HEADER_LENGTH shr 4 + .minimum_heap dw (10000h - (.LENGTH-.HEADER_LENGTH)) shr 4 + .maximum_heap dw 0FFFFh + .initial_ss dw (-100h) shr 4 + .initial_sp dw 0FFFEh + .checksum dw 0 + .initial_ip dw 100h + .initial_cs dw (-100h) shr 4 + .relocations_offset dw 40h + .overlay_number dw 0 + rb 3Ch - $ + .new_header_offset dd Header + + .HEADER_LENGTH = $ + + file Settings.Stub + + .LENGTH = $ + + end if + + else + + Stub: + .signature dw "MZ" + .bytes_in_last_page dw .LENGTH and 1FFh + .number_of_pages dw (.LENGTH-1) shr 9 + 1 + .number_of_relocations dw 0 + .number_of_header_paragraphs dw .HEADER_LENGTH shr 4 + .minimum_heap dw .STACK_LENGTH shr 4 + .maximum_heap dw 0FFFFh + .initial_ss dw 0 + .initial_sp dw .LENGTH - .HEADER_LENGTH + .STACK_LENGTH + .checksum dw 0 + .initial_ip dw 0 + .initial_cs dw 0 + .relocations_offset dw 40h + .overlay_number dw 0 + rb 3Ch - $ + .new_header_offset dd Header + + .HEADER_LENGTH = $ + .STACK_LENGTH = 100h + + namespace Stub + + include '../8086.inc' + + start: + push cs + pop ds + mov dx,message - start + mov ah,9 + int 21h + mov ax,4C01h + int 21h + + message db 'This program cannot be run in DOS mode.',0Dh,0Ah,24h + + end namespace + + align 16 + + .LENGTH = $ + + end if + + if defined Settings.Stamp + TIMESTAMP := Settings.Stamp + else + TIMESTAMP := __TIME__ + end if + + align 8 + + Header: + .Signature dw "PE",0 + .Machine dw MACHINE + .NumberOfSections dw NUMBER_OF_SECTIONS + .TimeDateStamp dd TIMESTAMP + .PointerToSymbolTable dd 0 + .NumberOfSymbols dd 0 + .SizeOfOptionalHeader dw SectionTable - OptionalHeader + .Characteristics dw CHARACTERISTICS + + OptionalHeader: + .Magic dw MAGIC + .MajorLinkerVersion db 0 + .MinorLinkerVersion db 0 + .SizeOfCode dd 0 + .SizeOfInitializedData dd 0 + .SizeOfUninitializedData dd 0 + .AddressOfEntryPoint dd 0 + .BaseOfCode dd 0 + if MAGIC <> 0x20B + .BaseOfData dd 0 + .ImageBase dd IMAGE_BASE - RELOCATION + else + .ImageBase dq IMAGE_BASE - RELOCATION + end if + .SectionAlignment dd SECTION_ALIGNMENT + .FileAlignment dd FILE_ALIGNMENT + .MajorOperatingSystemVersion dw 1 + .MinorOperatingSystemVersion dw 0 + .MajorImageVersion dw 0 + .MinorImageVersion dw 0 + .MajorSubsystemVersion dw MAJOR_SUBSYSTEM_VERSION + .MinorSubsystemVersion dw MINOR_SUBSYSTEM_VERSION + .Win32VersionValue dd 0 + .SizeOfImage dd SIZE_OF_IMAGE + .SizeOfHeaders dd SIZE_OF_HEADERS + .CheckSum dd 0 + .Subsystem dw SUBSYSTEM + .DllCharacteristics dw DLL_CHARACTERISTICS + if MAGIC <> 0x20B + .SizeOfStackReserve dd 1000h + .SizeOfStackCommit dd 1000h + .SizeOfHeapReserve dd 10000h + .SizeOfHeapCommit dd 0 + else + .SizeOfStackReserve dq 1000h + .SizeOfStackCommit dq 1000h + .SizeOfHeapReserve dq 10000h + .SizeOfHeapCommit dq 0 + end if + .LoaderFlags dd 0 + .NumberOfRvaAndSizes dd NUMBER_OF_DIRECTORIES + RvaAndSizes: + .Rva dd 0 + .Size dd 0 + .ENTRY_LENGTH = $ - RvaAndSizes + db (NUMBER_OF_DIRECTORIES-1)*RvaAndSizes.ENTRY_LENGTH dup 0 + SectionTable: + .Name dq '.flat' + .VirtualSize dd 0 + .VirtualAddress dd 0 + .SizeOfRawData dd 0 + .PointerToRawData dd 0 + .PointerToRelocations dd 0 + .PointerToLineNumbers dd 0 + .NumberOfRelocations dw 0 + .NumberOfLineNumbers dw 0 + .Characteristics dd IMAGE_SCN_MEM_EXECUTE + IMAGE_SCN_MEM_READ + IMAGE_SCN_MEM_WRITE + .ENTRY_LENGTH = $ - SectionTable + db (NUMBER_OF_SECTIONS-1)*SectionTable.ENTRY_LENGTH dup 0 + + HeadersEnd: + define CheckSumBlocks PE,0,HeadersEnd + + SECTION_INDEX = 0 + RELOCATION_INDEX = 0 + DEFINED_SECTION = 0 + SECTION_DIRECTORIES = 0 + align SECTION_ALIGNMENT + FIRST_SECTION_RVA: + section $%% + align FILE_ALIGNMENT,0 + SIZE_OF_HEADERS = $% + FILE_OFFSET = $% + SECTION_BASE = IMAGE_BASE + FIRST_SECTION_RVA + org SECTION_BASE + + store SECTION_BASE-IMAGE_BASE at PE:OptionalHeader.AddressOfEntryPoint + store SECTION_BASE-IMAGE_BASE at PE:SectionTable.VirtualAddress + store FILE_OFFSET at PE:SectionTable.PointerToRawData + + virtual at 0 + relocated_addresses:: rd NUMBER_OF_RELOCATIONS + end virtual + + virtual at 0 + relocation_types:: rw NUMBER_OF_RELOCATIONS + end virtual + +end namespace + +RVA? equ -PE.IMAGE_BASE + + +macro entry? address* + namespace PE + store address-IMAGE_BASE at PE:OptionalHeader.AddressOfEntryPoint + end namespace +end macro + +macro stack? reserve*,commit:1000h + namespace PE + store reserve at PE:OptionalHeader.SizeOfStackReserve + store commit at PE:OptionalHeader.SizeOfStackCommit + end namespace +end macro + +macro heap? reserve*,commit:0 + namespace PE + store reserve at PE:OptionalHeader.SizeOfHeapReserve + store commit at PE:OptionalHeader.SizeOfHeapCommit + end namespace +end macro + +calminstruction calminstruction?.init? var*, val:0 + compute val, val + publish var, val +end calminstruction + +calminstruction calminstruction?.initsym? var*, val& + publish var, val +end calminstruction + +calminstruction calminstruction?.unique? name + local counter, buffer + init counter + compute counter, counter + 1 + arrange buffer, name#counter + publish name, buffer +end calminstruction + +calminstruction calminstruction?.asm? line& + local tmp, ln, buffer + initsym tmp, unique ln + assemble tmp + publish ln, line + arrange buffer, =assemble ln + assemble buffer +end calminstruction + +macro section? + namespace PE + + repeat SECTION_DIRECTORIES + end data + end repeat + + local AREA,DATA_START,DATA_END + AREA:: + DATA_START = $$ + DATA_END = $-($%-$%%) + CheckSumBlocks reequ CheckSumBlocks,AREA,DATA_START,DATA_END + + SECTION_SIZE = $ - SECTION_BASE + store SECTION_SIZE at PE:SectionTable.VirtualSize+SECTION_INDEX*SectionTable.ENTRY_LENGTH + align SECTION_ALIGNMENT + SECTION_BASE = $ + section $%% + align FILE_ALIGNMENT,0 + RAW_DATA_SIZE = $% - FILE_OFFSET + store RAW_DATA_SIZE at PE:SectionTable.SizeOfRawData+SECTION_INDEX*SectionTable.ENTRY_LENGTH + FILE_OFFSET = $% + org SECTION_BASE + + load SECTION_CHARACTERISTICS from PE:SectionTable.Characteristics+SECTION_INDEX*SectionTable.ENTRY_LENGTH + if SECTION_SIZE > 0 & RAW_DATA_SIZE = 0 + SECTION_CHARACTERISTICS = SECTION_CHARACTERISTICS or IMAGE_SCN_CNT_UNINITIALIZED_DATA + store SECTION_CHARACTERISTICS at PE:SectionTable.Characteristics+SECTION_INDEX*SectionTable.ENTRY_LENGTH + end if + + if LEGACY_HEADERS + if SECTION_CHARACTERISTICS and IMAGE_SCN_CNT_CODE & RAW_DATA_SIZE > 0 + load CODE_SIZE from PE:OptionalHeader.SizeOfCode + if CODE_SIZE = 0 + load CODE_BASE from PE:SectionTable.VirtualAddress+SECTION_INDEX*SectionTable.ENTRY_LENGTH + store CODE_BASE at PE:OptionalHeader.BaseOfCode + end if + CODE_SIZE = CODE_SIZE + RAW_DATA_SIZE + store CODE_SIZE at PE:OptionalHeader.SizeOfCode + end if + if SECTION_CHARACTERISTICS and IMAGE_SCN_CNT_INITIALIZED_DATA & RAW_DATA_SIZE > 0 + load DATA_SIZE from PE:OptionalHeader.SizeOfInitializedData + if DATA_SIZE = 0 & MAGIC <> 0x20B + load DATA_BASE from PE:SectionTable.VirtualAddress+SECTION_INDEX*SectionTable.ENTRY_LENGTH + store DATA_BASE at PE:OptionalHeader.BaseOfData + end if + DATA_SIZE = DATA_SIZE + RAW_DATA_SIZE + store DATA_SIZE at PE:OptionalHeader.SizeOfInitializedData + end if + if SECTION_CHARACTERISTICS and IMAGE_SCN_CNT_UNINITIALIZED_DATA + load BSS_SIZE from PE:OptionalHeader.SizeOfUninitializedData + BSS_SIZE = BSS_SIZE + SECTION_SIZE + store BSS_SIZE at PE:OptionalHeader.SizeOfUninitializedData + end if + end if + + if DEFINED_SECTION | SECTION_SIZE > 0 + SECTION_INDEX = SECTION_INDEX + 1 + end if + + end namespace +end macro + +macro section? declaration* + namespace PE + + section + + DEFINED_SECTION = 1 + + store SECTION_BASE-IMAGE_BASE at PE:SectionTable.VirtualAddress+SECTION_INDEX*SectionTable.ENTRY_LENGTH + store FILE_OFFSET at PE:SectionTable.PointerToRawData+SECTION_INDEX*SectionTable.ENTRY_LENGTH + + SECTION_DIRECTORIES = 0 + + match name attributes, declaration + + store name:qword at PE:SectionTable.Name+SECTION_INDEX*SectionTable.ENTRY_LENGTH + + SECTION_CHARACTERISTICS = 0 + + local seq + define seq attributes: + while 1 + match :, seq + break + else match =readable? tail, seq + redefine seq tail + SECTION_CHARACTERISTICS = SECTION_CHARACTERISTICS or IMAGE_SCN_MEM_READ + else match =writeable? tail, seq + redefine seq tail + SECTION_CHARACTERISTICS = SECTION_CHARACTERISTICS or IMAGE_SCN_MEM_WRITE + else match =writable? tail, seq + redefine seq tail + SECTION_CHARACTERISTICS = SECTION_CHARACTERISTICS or IMAGE_SCN_MEM_WRITE + else match =executable? tail, seq + redefine seq tail + SECTION_CHARACTERISTICS = SECTION_CHARACTERISTICS or IMAGE_SCN_MEM_EXECUTE + else match =discardable? tail, seq + redefine seq tail + SECTION_CHARACTERISTICS = SECTION_CHARACTERISTICS or IMAGE_SCN_MEM_DISCARDABLE + else match =shareable? tail, seq + redefine seq tail + SECTION_CHARACTERISTICS = SECTION_CHARACTERISTICS or IMAGE_SCN_MEM_SHARED + else match =import? tail, seq + redefine seq tail + SECTION_DIRECTORIES = SECTION_DIRECTORIES + 1 + data import + else match =export? tail, seq + redefine seq tail + SECTION_DIRECTORIES = SECTION_DIRECTORIES + 1 + data export + else match =resource? =from? path tail, seq + redefine seq tail + SECTION_DIRECTORIES = SECTION_DIRECTORIES + 1 + data resource from path + else match =resource? tail, seq + redefine seq tail + SECTION_DIRECTORIES = SECTION_DIRECTORIES + 1 + data resource + else match =fixups? tail, seq + redefine seq tail + SECTION_DIRECTORIES = SECTION_DIRECTORIES + 1 + data fixups + else match =code? tail, seq + redefine seq tail + SECTION_CHARACTERISTICS = SECTION_CHARACTERISTICS or IMAGE_SCN_CNT_CODE + else match =data? tail, seq + redefine seq tail + SECTION_CHARACTERISTICS = SECTION_CHARACTERISTICS or IMAGE_SCN_CNT_INITIALIZED_DATA + else match =udata? tail, seq + redefine seq tail + SECTION_CHARACTERISTICS = SECTION_CHARACTERISTICS or IMAGE_SCN_CNT_UNINITIALIZED_DATA + else match attribute tail, seq + err 'unknown attribute "',`attribute,'"' + redefine seq : + end match + end while + + store SECTION_CHARACTERISTICS at PE:SectionTable.Characteristics+SECTION_INDEX*SectionTable.ENTRY_LENGTH + else + + store declaration:qword at PE:SectionTable.Name+SECTION_INDEX*SectionTable.ENTRY_LENGTH + + end match + + end namespace +end macro + +macro data? type* + namespace PE + local number,content + define content + match =export?, type + number = 0 + else match =import?, type + number = 1 + else match =resource? =from? path, type + number = 2 + define content resource_from path + else match =resource?, type + number = 2 + else match =fixups?, type + number = 5 + define content fixups + else + number = type + end match + define DATA_DIRECTORY number + load DATA_BASE:dword from PE:RvaAndSizes.Rva+DATA_DIRECTORY*RvaAndSizes.ENTRY_LENGTH + if DATA_BASE = 0 + store $-IMAGE_BASE:dword at PE:RvaAndSizes.Rva+DATA_DIRECTORY*RvaAndSizes.ENTRY_LENGTH + match instruction, content + instruction + end match + else + err 'data already defined' + end if + end namespace +end macro + +macro end?.data? + namespace PE + load DATA_BASE:dword from PE:RvaAndSizes.Rva+DATA_DIRECTORY*RvaAndSizes.ENTRY_LENGTH + store $-IMAGE_BASE-DATA_BASE:dword at PE:RvaAndSizes.Size+DATA_DIRECTORY*RvaAndSizes.ENTRY_LENGTH + restore DATA_DIRECTORY + end namespace +end macro + +macro PE.resource_directory? + namespace PE + Resource: rb RESOURCE_HEADERS_LENGTH + Resource.counter = 0 + define RESOURCE_DIRECTORIES_LIST Resource_root + Resource_root.counter = 0 + end namespace +end macro + +macro PE.resource_data? type*,id*,lang*,codepage:0 + namespace PE + local _type,_id,_lang + _type = type + _id = id + _lang = lang + if ~ type eqtype 0 + _type = _type shl 32 + end if + if ~ id eqtype 0 + _id = id shl 32 + end if + if ~ lang eqtype 0 + _lang = lang shl 32 + end if + repeat 1, %type:_type, %id:_id, %lang:_lang + if ~ defined Resource_#%type#_#%id.counter + if ~ defined Resource_#%type.counter + repeat 1, i:Resource_root.counter + Resource_root.entry#i = type + Resource_root.offset#i = (Resource_#%type - Resource) or 80000000h + end repeat + Resource_root.counter = Resource_root.counter + 1 + match list, RESOURCE_DIRECTORIES_LIST + define RESOURCE_DIRECTORIES_LIST list,Resource_#%type + end match + Resource_#%type.counter = 0 + end if + repeat 1, i:Resource_#%type.counter + Resource_#%type.entry#i = id + Resource_#%type.offset#i = (Resource_#%type#_#%id - Resource) or 80000000h + end repeat + Resource_#%type.counter = Resource_#%type.counter + 1 + + match list, RESOURCE_DIRECTORIES_LIST + define RESOURCE_DIRECTORIES_LIST list,Resource_#%type#_#%id + end match + Resource_#%type#_#%id.counter = 0 + end if + repeat 1, i:Resource_#%type#_#%id.counter + Resource_#%type#_#%id.entry#i = lang + Resource_#%type#_#%id.offset#i = Resource_#%type#_#%id#_#%lang - Resource + end repeat + Resource_#%type#_#%id.counter = Resource_#%type#_#%id.counter + 1 + repeat 1, i:Resource.counter + Resource_#%type#_#%id#_#%lang := Resource.entry#i + Resource.cp#i := codepage + Resource.data#i: + end repeat + end repeat + end namespace +end macro + +macro PE.end_resource_data? + namespace PE + repeat 1, i:Resource.counter + Resource.size#i := $ - Resource.data#i + end repeat + Resource.counter = Resource.counter + 1 + align 4 + end namespace +end macro + +macro PE.end_resource_directory? + namespace PE + RESOURCE_HEADERS_POINTER = 0 + + match list, RESOURCE_DIRECTORIES_LIST + iterate dir, list + dir := Resource + RESOURCE_HEADERS_POINTER + RESOURCE_HEADERS_POINTER = RESOURCE_HEADERS_POINTER + 16 + dir.counter * 8 + local x,y,z,a,b + x = dir.counter shr 1 + while x > 0 + y = x + while y < dir.counter + z = y + while z-x >= 0 + repeat 1, i:z, j:z-x + if dir.entry#i eqtype 0 + if ~ dir.entry#j eqtype 0 | dir.entry#i >= dir.entry#j + z = 0 + end if + else if ~ dir.entry#j eqtype 0 + a = dir.entry#i bswap lengthof dir.entry#i + b = dir.entry#j bswap lengthof dir.entry#j + if ( lengthof a >= lengthof b & a shr ((lengthof a - lengthof b)*8) >= b ) | ( lengthof a < lengthof b & a > b shr ((lengthof b - lengthof a)*8) ) + z = 0 + end if + end if + if z > 0 + a = dir.entry#i + b = dir.offset#i + dir.entry#i = dir.entry#j + dir.offset#i = dir.offset#j + dir.entry#j = a + dir.offset#j = b + z = z - x + end if + end repeat + end while + y = y + 1 + end while + x = x shr 1 + end while + end iterate + iterate dir, list + store __TIME__ : 4 at dir + 4 + dir.names_counter = 0 + repeat dir.counter, i:0 + if dir.entry#i eqtype 0 + store dir.entry#i : 4 at dir + 16 + i * 8 + else + dir.names_counter = dir.names_counter + 1 + repeat 1, %id:dir.entry#i + if ~ defined Resource_string#%id + restore Resource_string#%id + Resource_string#%id = Resource + RESOURCE_HEADERS_POINTER + if lengthof dir.entry#i and 1 + err 'a word-aligned string is expected as a name' + end if + RESOURCE_HEADERS_POINTER = RESOURCE_HEADERS_POINTER + lengthof dir.entry#i + 2 + store (lengthof dir.entry#i)/2 : 2 at Resource_string#%id + store dir.entry#i : lengthof dir.entry#i at Resource_string#%id + 2 + end if + store (Resource_string#%id - Resource) or 80000000h : 4 at dir + 16 + i * 8 + end repeat + end if + store dir.offset#i : 4 at dir + 16 + i * 8 + 4 + end repeat + store dir.names_counter : 2 at dir + 12 + store dir.counter - dir.names_counter : 2 at dir + 14 + end iterate + end match + + if RESOURCE_HEADERS_POINTER and 11b + RESOURCE_HEADERS_POINTER = RESOURCE_HEADERS_POINTER + 4 - RESOURCE_HEADERS_POINTER and 11b + end if + + repeat Resource.counter, i:0 + Resource.entry#i := Resource + RESOURCE_HEADERS_POINTER + RESOURCE_HEADERS_POINTER = RESOURCE_HEADERS_POINTER + 16 + store RVA(Resource.data#i) : 4 at Resource.entry#i + store Resource.size#i : 4 at Resource.entry#i + 4 + store Resource.cp#i : 4 at Resource.entry#i + 8 + end repeat + + RESOURCE_HEADERS_LENGTH = RESOURCE_HEADERS_POINTER + end namespace +end macro + +macro PE.resource_from path* + + local res_file,res_size,res_header_size,res_data_size,res_data + local offset,char,type,id,lang + + virtual at 0 + res_file:: file path + res_size := $ + end virtual + + PE.resource_directory + + offset = 0 + while offset < res_size + load res_header_size : 4 from res_file : offset + 4 + load res_data_size : 4 from res_file : offset + 0 + + if res_data_size > 0 + + offset =: offset + 8 + load char : 2 from res_file : offset + if char = 0FFFFh + load char : 2 from res_file : offset + 2 + type = +char + offset = offset + 4 + else + while 1 + if char = 0 + load type : (%-1)*2 from res_file : offset + offset = offset + (% + % and 1)*2 + break + end if + load char : 2 from res_file : offset + %*2 + end while + end if + load char : 2 from res_file : offset + if char = 0FFFFh + load char : 2 from res_file : offset + 2 + id = +char + offset = offset + 4 + else + while 1 + if char = 0 + load id : (%-1)*2 from res_file : offset + offset = offset + (% + % and 1)*2 + break + end if + load char : 2 from res_file : offset + %*2 + end while + end if + load char : 2 from res_file : offset + 6 + lang = +char + + restore offset + + PE.resource_data type,id,lang + load res_data : res_data_size from res_file : offset + res_header_size + db res_data + PE.end_resource_data + + end if + + offset = offset + res_header_size + res_data_size + if offset and 11b + offset = offset + 4 - offset and 11b + end if + end while + + PE.end_resource_directory + +end macro + +macro PE.fixups + namespace PE + Fixups: + calminstruction BuildFixups + local PAGE_RVA, BLOCK_HEADER, BLOCK_SIZE + local INDEX, ADDRESS, TYPE, FIXUP + compute PAGE_RVA, -1 + compute BLOCK_HEADER, 0 + compute BLOCK_SIZE, 0 + compute INDEX,0 + process: + check INDEX = NUMBER_OF_RELOCATIONS + jyes close_block + asm load ADDRESS:4 from relocated_addresses:INDEX shl 2 + check PAGE_RVA >= 0 & ADDRESS and not 0FFFh = PAGE_RVA + jyes append_to_block + close_block: + check BLOCK_HEADER + jno start_new_block + check BLOCK_SIZE and 11b + jno finish_block + asm dw 0 + compute BLOCK_SIZE, BLOCK_SIZE + 2 + finish_block: + asm store BLOCK_SIZE:4 at BLOCK_HEADER+4 + start_new_block: + check INDEX = NUMBER_OF_RELOCATIONS + jyes done + compute PAGE_RVA, ADDRESS and not 0FFFh + compute BLOCK_HEADER, $ + asm dd PAGE_RVA, 0 + compute BLOCK_SIZE, 8 + append_to_block: + asm load TYPE:2 from relocation_types:INDEX shl 1 + compute FIXUP, (ADDRESS and 0FFFh) or (TYPE shl 12) + asm dw FIXUP + compute BLOCK_SIZE, BLOCK_SIZE + 2 + compute INDEX, INDEX + 1 + jump process + done: + end calminstruction + BuildFixups + end namespace +end macro + +if defined PE.Fixups + + calminstruction dword? value + compute value, value + check value relativeto 0 | ~ value relativeto PE.RELOCATION + jyes plain + local offset + compute offset, $% + asm emit 4: value - PE.RELOCATION + check $% > offset + jno done + asm store $-4-PE.IMAGE_BASE:4 at PE.relocated_addresses:PE.RELOCATION_INDEX shl 2 + asm store IMAGE_REL_BASED_HIGHLOW:2 at PE.relocation_types:PE.RELOCATION_INDEX shl 1 + compute PE.RELOCATION_INDEX, PE.RELOCATION_INDEX + 1 + done: + exit + plain: + asm emit 4: value + end calminstruction + + calminstruction qword? value + compute value, value + check value relativeto 0 | ~ value relativeto PE.RELOCATION + jyes plain + local offset + compute offset, $% + asm emit 8: value - PE.RELOCATION + check $% > offset + jno done + asm store $-8-PE.IMAGE_BASE:4 at PE.relocated_addresses:PE.RELOCATION_INDEX shl 2 + asm store IMAGE_REL_BASED_DIR64:2 at PE.relocation_types:PE.RELOCATION_INDEX shl 1 + compute PE.RELOCATION_INDEX, PE.RELOCATION_INDEX + 1 + done: + exit + plain: + asm emit 8: value + end calminstruction + + iterate , dd,dword, dq,qword + + calminstruction dd? definitions& + local value, n + start: + match value=,definitions, definitions, () + jyes recognize + match value, definitions + arrange definitions, + recognize: + match n =dup? value, value, () + jyes duplicate + match ?, value + jyes reserve + arrange value, =dword value + assemble value + next: + match , definitions + jno start + take , definitions + take definitions, definitions + jyes next + exit + reserve: + arrange value, =dd ? + assemble value + jump next + duplicate: + match (value), value + stack: + check n + jno next + take definitions, value + arrange value, definitions + compute n, n - 1 + jump stack + end calminstruction + + calminstruction (label) dd? definitions& + local cmd + arrange cmd, =label label : =dword + assemble cmd + arrange cmd, =dd definitions + assemble cmd + end calminstruction + + end iterate + +end if + +postpone + purge section? + section + namespace PE + SIZE_OF_IMAGE := SECTION_BASE - IMAGE_BASE + NUMBER_OF_SECTIONS := SECTION_INDEX + NUMBER_OF_RELOCATIONS := RELOCATION_INDEX + end namespace +end postpone + +postpone ? + namespace PE + CHECKSUM = 0 + + calminstruction CheckSum + local AREA, DATA_START, DATA_END, POS, H + get_block: + match AREA=,DATA_START=,DATA_END=,CheckSumBlocks, CheckSumBlocks + jyes block_ready + match AREA=,DATA_START=,DATA_END, CheckSumBlocks + jyes last_block + exit + last_block: + arrange CheckSumBlocks, + block_ready: + compute POS, DATA_START + process_block: + check POS + 2 <= DATA_END + jno finish_block + asm load H:2 from AREA:POS + compute CHECKSUM, CHECKSUM + H + compute POS, POS + 2 + jump process_block + finish_block: + check POS + 1 = DATA_END + jno reduce_checksum + asm load H:1 from AREA:POS + compute CHECKSUM, CHECKSUM + H + reduce_checksum: + check CHECKSUM shr 16 + jno get_block + compute CHECKSUM, CHECKSUM shr 16 + CHECKSUM and 0FFFFh + jump reduce_checksum + done: + end calminstruction + + CheckSum + CHECKSUM = CHECKSUM + $% + store CHECKSUM at PE:OptionalHeader.CheckSum + end namespace +end postpone diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/p5.inc b/x86_64_sse2_x87/fasm/examples/x86/include/p5.inc new file mode 100644 index 0000000..f6fff0e --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/p5.inc @@ -0,0 +1,39 @@ + +calminstruction element? definition + local name, meta + match =tr? any, definition + jyes skip + arrange definition, =element? definition + assemble definition + skip: +end calminstruction + +include '80486.inc' + +purge element? + +iterate , wrmsr,<0Fh,30h>, rdtsc,<0Fh,31h>, rdmsr,<0Fh,32h>, rdpmc,<0Fh,33h>, cpuid,<0Fh,0A2h>, rsm,<0Fh,0AAh> + + define x86.instr? db opcode + + calminstruction instr? + assemble x86.instr? + end calminstruction + +end iterate + +calminstruction cmpxchg8b? dest* + asmcmd =x86.=parse_operand =@dest,dest + check @dest.type = 'mem' + jyes operand_ok + asmcmd =err 'invalid operand' + exit + operand_ok: + check @dest.size and not 8 + jno size_ok + asmcmd =err 'invalid operand size' + size_ok: + asmcmd =x86.=store_instruction <0Fh,0C7h>,=@dest,1 +end calminstruction + +include '80387.inc' diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/p6.inc b/x86_64_sse2_x87/fasm/examples/x86/include/p6.inc new file mode 100644 index 0000000..6de5bb8 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/p6.inc @@ -0,0 +1,65 @@ + +include 'p5.inc' + +iterate , sysenter,<0Fh,34h>, sysexit,<0Fh,35h> + + define x86.instr? db opcode + + calminstruction instr? + assemble x86.instr? + end calminstruction + +end iterate + +iterate , o,0, no,1, c,2, b,2, nae,2, nc,3, nb,3, ae,3, z,4, e,4, nz,5, ne,5, na,6, be,6, a,7, nbe,7, \ + s,8, ns,9, p,0Ah, pe,0Ah, np,0Bh, po,0Bh, l,0Ch, nge,0Ch, nl,0Dh, ge,0Dh, ng,0Eh, le,0Eh, g,0Fh, nle,0Fh + + calminstruction cmov#cond? dest*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + check @dest.type = 'reg' & (@src.type = 'mem' | @src.type = 'reg') + jyes cmov_rm_reg + asmcmd =err 'invalid combination of operands' + exit + cmov_rm_reg: + check @src.size and not @dest.size + jno cmov_rm_reg_ok + asmcmd =err 'operand sizes do not match' + cmov_rm_reg_ok: + asmcmd =x86.=select_operand_prefix =@src,=@dest.=size + asmcmd =x86.=store_instruction <0Fh,40h+code>,=@src,=@dest.=rm + end calminstruction + +end iterate + +iterate , fcmovb,<0DAh,0C0h>, fcmove,<0DAh,0C8h>, fcmovbe,<0DAh,0D0h>, fcmovu,<0DAh,0D8h>, \ + fcmovnb,<0DBh,0C0h>, fcmovne,<0DBh,0C8h>, fcmovnbe,<0DBh,0D0h>, fcmovnu,<0DBh,0D8h> + + calminstruction instr? dest*,src* + asmcmd =x87.=parse_operand =@dest,dest + asmcmd =x87.=parse_operand =@src,src + check @dest.type = 'streg' & @dest.rm = 0 & @src.type = 'streg' + jyes ok + asmcmd =err 'invalid operand' + exit + ok: + asmcmd =db opcode + =@src.=rm + end calminstruction + +end iterate + +iterate , fucomi,0DBh,5, fucomip,0DFh,5, fcomi,0DBh,6, fcomip,0DFh,6 + + calminstruction instr? src:st1 + asmcmd =x87.=parse_operand =@src,src + check @src.type = 'streg' + jyes ok + asmcmd =err 'invalid operand' + exit + ok: + local modrm + compute modrm, 11b shl 6 + postbyte shl 3 + @src.rm + asmcmd =db opcode, modrm + end calminstruction + +end iterate diff --git a/x86_64_sse2_x87/fasm/examples/x86/include/x64.inc b/x86_64_sse2_x87/fasm/examples/x86/include/x64.inc new file mode 100644 index 0000000..9f42c50 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/include/x64.inc @@ -0,0 +1,3549 @@ + +define x86 x86 + +element x86.reg +element x86.r8 : x86.reg + 1 +element x86.r16 : x86.reg + 2 +element x86.r32 : x86.reg + 4 +element x86.r64 : x86.reg + 8 + +element al? : x86.r8 + 0 +element cl? : x86.r8 + 1 +element dl? : x86.r8 + 2 +element bl? : x86.r8 + 3 + +element spl? : x86.r8 + 4 +element bpl? : x86.r8 + 5 +element sil? : x86.r8 + 6 +element dil? : x86.r8 + 7 + +element ah? : x86.r8 - 4 +element ch? : x86.r8 - 5 +element dh? : x86.r8 - 6 +element bh? : x86.r8 - 7 + +repeat 8, i:8 + element r#i#b? : x86.r8 + i + element r#i#l? : x86.r8 + i +end repeat + +element ax? : x86.r16 + 0 +element cx? : x86.r16 + 1 +element dx? : x86.r16 + 2 +element bx? : x86.r16 + 3 +element sp? : x86.r16 + 4 +element bp? : x86.r16 + 5 +element si? : x86.r16 + 6 +element di? : x86.r16 + 7 + +repeat 8, i:8 + element r#i#w? : x86.r16 + i +end repeat + +element eax? : x86.r32 + 0 +element ecx? : x86.r32 + 1 +element edx? : x86.r32 + 2 +element ebx? : x86.r32 + 3 +element esp? : x86.r32 + 4 +element ebp? : x86.r32 + 5 +element esi? : x86.r32 + 6 +element edi? : x86.r32 + 7 + +repeat 8, i:8 + element r#i#d? : x86.r32 + i +end repeat + +element rax? : x86.r64 + 0 +element rcx? : x86.r64 + 1 +element rdx? : x86.r64 + 2 +element rbx? : x86.r64 + 3 +element rsp? : x86.r64 + 4 +element rbp? : x86.r64 + 5 +element rsi? : x86.r64 + 6 +element rdi? : x86.r64 + 7 + +repeat 8, i:8 + element r#i? : x86.r64 + i +end repeat + +element x86.ip + +element eip? : x86.ip + 4 +element rip? : x86.ip + 8 + +element x86.sreg + +element es? : x86.sreg + 0 +element cs? : x86.sreg + 1 +element ss? : x86.sreg + 2 +element ds? : x86.sreg + 3 +element fs? : x86.sreg + 4 +element gs? : x86.sreg + 5 + +element x86.creg + +element x86.crx : x86.creg + 0 +element x86.drx : x86.creg + 1 + +repeat 16, i:0 + element cr#i? : x86.crx + i + element dr#i? : x86.drx + i +end repeat + +define x86.byte? :1 +define x86.word? :2 +define x86.dword? :4 +define x86.pword? :6 +define x86.fword? :6 +define x86.qword? :8 +define x86.tword? :10 +define x86.tbyte? :10 +define x86.dqword? :16 + +x86.mode = 16 + +macro use16? + x86.mode = 16 +end macro + +macro use32? + x86.mode = 32 +end macro + +macro use64? + x86.mode = 64 +end macro + +x86.REX_REQUIRED = 100h +x86.REX_FORBIDDEN = 200h + +define @dest @dest +define @src @src +define @aux @aux + +macro calminstruction?.asmcmd? pattern& + local cmd + arrange cmd, pattern + assemble cmd +end macro + +calminstruction x86.parse_operand namespace, operand + + local size, type, segment_prefix, prefix, opcode_prefix, rex_prefix + local imm, unresolved, displacement, displacement_size, auto_relative + local address, address_registers, segment, offset + local mode, mod, rm + local scale, index, base + + local i, pre, suf, sym + + compute segment_prefix, 0 + compute prefix, 0 + compute opcode_prefix, 0 + compute rex_prefix, 0 + + compute size, 0 + compute displacement_size, 0 + + transform operand + + match pre suf, operand + jno no_size_prefix + transform pre, x86 + jno no_size_prefix + match :size, pre + jno no_size_prefix + arrange operand, suf + no_size_prefix: + + match [address], operand + jyes memory_operand + match =ptr? address, operand + jyes memory_operand + match segment:offset, operand + jyes far_operand + + immediate_operand: + compute type, 'imm' + compute imm, +operand + + compute unresolved, 0 + check defined operand + jyes operand_resolved + compute unresolved, 1 + operand_resolved: + + check imm eq 1 elementof imm + jno export_immediate + check 1 metadataof (1 metadataof imm) relativeto x86.reg + jyes register_operand + check 1 metadataof imm relativeto x86.sreg + jyes segment_register_operand + + jump export_immediate + + register_operand: + + compute type, 'reg' + compute mode, x86.mode + compute mod, 11b + compute rm, 1 metadataof imm - 1 elementof (1 metadataof imm) + check size & size <> 1 metadataof (1 metadataof imm) - x86.reg + jyes operand_sizes_do_not_match + compute size, 1 metadataof (1 metadataof imm) - x86.reg + check rm < 0 + jyes register_precluding_rex + check size = 1 & rm >= 4 & rm < 8 + jyes register_requiring_rex + + jump export_register + + register_precluding_rex: + compute rm, x86.REX_FORBIDDEN - rm + jump export_register + + register_requiring_rex: + compute rm, x86.REX_REQUIRED + rm + jump export_register + + segment_register_operand: + + compute type, 'sreg' + compute mode, x86.mode + compute mod, 11b + compute rm, 1 metadataof imm - x86.sreg + check size & size <> 2 & size <> 4 + jyes invalid_operand_size + + jump export_register + + memory_operand: + compute type, 'mem' + + match segment:address, address + jno segment_prefix_ok + check segment eq 1 elementof segment & 1 metadataof segment relativeto x86.sreg + jno invalid_operand + compute segment, 1 metadataof segment - x86.sreg + check segment >= 4 + jyes segment_prefix_386 + compute segment_prefix, 26h + segment shl 3 + jump segment_prefix_ok + segment_prefix_386: + compute segment_prefix, 64h + segment-4 + segment_prefix_ok: + + compute mode, 0 + + match pre suf, address + jno no_address_size_prefix + transform pre, x86 + jno no_address_size_prefix + match :pre, pre + jno no_address_size_prefix + arrange address, suf + check pre = 2 | pre = 4 | pre = 8 + jno invalid_address_size + compute mode, pre shl 3 + no_address_size_prefix: + + compute scale, 0 + compute index, 0 + compute base, 0 + + check size + jyes size_override + compute size, sizeof address + size_override: + + compute address, address + compute address_registers, 0 + compute i, 1 + extract_registers: + check i > elementsof address + jyes registers_extracted + check i metadataof address relativeto x86.r16 | i metadataof address relativeto x86.r32 | i metadataof address relativeto x86.r64 | i metadataof address relativeto x86.ip + jno next_term + compute address_registers, address_registers + i elementof address * i scaleof address + next_term: + compute i, i+1 + jump extract_registers + registers_extracted: + compute displacement, address - address_registers + compute auto_relative, 0 + + check address_registers eq 0 + jyes direct_address + check mode & mode <> 0 scaleof (1 metadataof (1 metadataof address_registers)) shl 3 & ~ 1 metadataof address_registers relativeto x86.ip + jyes invalid_address + check 1 metadataof address_registers relativeto x86.r64 | 1 metadataof address_registers relativeto x86.r32 + jyes address_32bit_64bit + check 1 metadataof address_registers relativeto x86.r16 + jyes address_16bit + check address_registers eq rip | address_registers eq eip + jyes rip_relative_address + jump invalid_address + + rip_relative_address: + compute mode, 0 scaleof (1 metadataof address_registers) shl 3 + compute mod, 0 + compute rm, 5 + compute displacement_size, 4 + jump export_address + + direct_address: + compute mod, 0 + check x86.mode = 64 + jyes direct_address_in_long_mode + check mode = 0 + jno mode_ok + compute mode, x86.mode + check mode = 16 & displacement relativeto 0 & displacement >= 10000h + jno mode_ok + compute mode, 32 + mode_ok: + check mode = 16 + jyes direct_address_16bit + direct_address_32bit: + compute rm, 5 + compute displacement_size, 4 + jump export_address + direct_address_16bit: + compute rm, 6 + compute displacement_size, 2 + jump export_address + + direct_address_in_long_mode: + compute displacement_size, 4 + check mode = 0 & segment_prefix < 64h + jyes auto_relative_address + check mode = 16 + jyes invalid_address_size + compute rm, 4 + compute base, 5 + compute index, 4 + compute scale, 1 + check mode = 32 + jno direct_address_displacement_ready + check ~ displacement relativeto 0 | displacement >= 100000000h | displacement < -100000000h + jyes address_out_of_range + compute displacement, displacement and 0FFFFFFFFh + direct_address_displacement_ready: + check displacement relativeto 0 & displacement > 7FFFFFFFh & displacement < 100000000h + jyes direct_address_switch_to_32bit + compute mode, 64 + compute displacement_size, 8 + jump export_address + direct_address_switch_to_32bit: + compute mode, 32 + jump export_address + + auto_relative_address: + compute mode, 64 + compute rm, 5 + compute auto_relative, 1 + jump export_address + + address_16bit: + compute mode, 16 + + check address_registers relativeto bx+si + jyes rm_0 + check address_registers relativeto bx+di + jyes rm_1 + check address_registers relativeto bp+si + jyes rm_2 + check address_registers relativeto bp+di + jyes rm_3 + check address_registers relativeto si + jyes rm_4 + check address_registers relativeto di + jyes rm_5 + check address_registers relativeto bp + jyes rm_6 + check address_registers relativeto bx + jyes rm_7 + jump invalid_address + + rm_0: + compute rm, 0 + jump rm_ok + rm_1: + compute rm, 1 + jump rm_ok + rm_2: + compute rm, 2 + jump rm_ok + rm_3: + compute rm, 3 + jump rm_ok + rm_4: + compute rm, 4 + jump rm_ok + rm_5: + compute rm, 5 + jump rm_ok + rm_6: + compute rm, 6 + jump rm_ok + rm_7: + compute rm, 7 + rm_ok: + + check displacement relativeto 0 + jno displacement_16bit + check displacement = 0 & rm <> 6 + jyes displacement_empty + check displacement<80h & displacement>=-80h + jyes displacement_8bit + check displacement-10000h>=-80h & displacement<10000h + jyes displacement_8bit_wrap_16bit + displacement_16bit: + compute displacement_size, 2 + compute mod, 2 + jump export_address + displacement_empty: + compute displacement_size, 0 + compute mod, 0 + jump export_address + displacement_8bit_wrap_16bit: + compute displacement, displacement-10000h + displacement_8bit: + compute displacement_size, 1 + compute mod, 1 + jump export_address + + address_32bit_64bit: + + local address_registers_type + check 1 metadataof address_registers relativeto x86.r64 + jyes address_64bit + address_32bit: + compute mode, 32 + compute address_registers_type, x86.r32 + jump check_address_registers + address_64bit: + compute mode, 64 + compute address_registers_type, x86.r64 + check_address_registers: + check 2 scaleof address_registers = 0 + jyes one_register + check 3 scaleof address_registers = 0 & 2 metadataof address_registers relativeto address_registers_type + jyes two_registers + jump invalid_address + + one_register: + compute scale, 1 scaleof address_registers + compute base, 1 metadataof address_registers - address_registers_type + check scale = 1 + jyes one_register_unscaled + check base <> 4 & (scale = 4 | scale = 8) + jyes one_register_scaled + check base <> 4 & (scale = 2 | scale = 3 | scale = 5 | scale = 9) + jyes one_register_split + jump invalid_address + one_register_unscaled: + check base and 111b = 4 + jyes one_register_unscaled_in_sib + compute rm, base + jump setup_displacement + one_register_unscaled_in_sib: + compute rm, 4 + compute index, 4 + jump setup_displacement + one_register_scaled: + compute rm, 4 + compute index, base + compute base, 5 + jump index_only + one_register_split: + compute rm, 4 + compute index, base + compute scale, scale - 1 + jump setup_displacement + two_registers: + compute rm,4 + check 1 scaleof address_registers = 1 + jyes base_first + check 2 scaleof address_registers = 1 + jyes base_second + jump invalid_address + base_first: + compute base, 1 metadataof address_registers - address_registers_type + compute index, 2 metadataof address_registers - address_registers_type + compute scale, 2 scaleof address_registers + jump process_sib + base_second: + compute base, 2 metadataof address_registers - address_registers_type + compute index, 1 metadataof address_registers - address_registers_type + compute scale, 1 scaleof address_registers + process_sib: + check index = 4 + jyes forbidden_index + check (x86.mode <> 64 & segment_prefix = 36h) & index = 5 & scale = 1 + jyes switch_to_index + check (x86.mode = 64 | segment_prefix = 3Eh) & base = 5 & scale = 1 + jyes switch_to_base + check scale > 2 & scale <> 4 & scale <> 8 + jyes invalid_address + jump setup_displacement + forbidden_index: + check scale = 1 + jno invalid_address + compute index, base + compute base, 4 + jump setup_displacement + switch_to_index: + compute index, base + compute base,5 + jump setup_displacement + switch_to_base: + compute base, index + compute index, 5 + jump setup_displacement + + setup_displacement: + check displacement relativeto 0 + jno displacement_32bit + check displacement = 0 & rm and 111b <> 5 & (rm <> 4 | base and 111b <> 5) + jyes displacement_empty + check displacement < 80h & displacement >= -80h + jyes displacement_8bit + check displacement - 1 shl mode >= -80h & displacement < 1 shl mode + jyes displacement_8bit_wrap + check (x86.mode = 64 | segment_prefix = 3Eh) & base = 5 & index = 5 & scale = 1 + jno displacement_32bit + compute scale, 2 + jump index_only + displacement_32bit: + compute displacement_size, 4 + compute mod, 2 + jump export_address + displacement_8bit_wrap: + compute displacement, displacement - 1 shl mode + jump displacement_8bit + index_only: + compute displacement_size, 4 + compute mod, 0 + jump export_address + + far_operand: + compute type, 'far' + + check size & size <> 4 & size <> 6 & size <> 10 + jyes operand_sizes_do_not_match + + arrange sym, namespace.=segment + publish sym, segment + + arrange sym, namespace.=offset + publish sym, offset + + jump export_common + + export_immediate: + + arrange sym, namespace.=imm + publish sym, imm + + arrange sym, namespace.=unresolved + publish sym, unresolved + + jump export_common + + export_address: + + arrange sym, namespace.=address + publish sym, address + + arrange sym, namespace.=address_registers + publish sym, address_registers + + arrange sym, namespace.=scale + publish sym, scale + + arrange sym, namespace.=index + publish sym, index + + arrange sym, namespace.=base + publish sym, base + + arrange sym, namespace.=auto_relative + publish sym, auto_relative + + arrange sym, namespace.=displacement + publish sym, displacement + + export_register: + + arrange sym, namespace.=mode + publish sym, mode + + arrange sym, namespace.=mod + publish sym, mod + + arrange sym, namespace.=rm + publish sym, rm + + export_common: + + arrange sym, namespace.=type + publish sym, type + + arrange sym, namespace.=size + publish sym, size + + arrange sym, namespace.=displacement_size + publish sym, displacement_size + + arrange sym, namespace.=segment_prefix + publish sym, segment_prefix + + arrange sym, namespace.=prefix + publish sym, prefix + + arrange sym, namespace.=opcode_prefix + publish sym, opcode_prefix + + arrange sym, namespace.=rex_prefix + publish sym, rex_prefix + exit + + invalid_operand: + asmcmd =err 'invalid operand' + exit + invalid_operand_size: + asmcmd =err 'invalid operand size' + exit + operand_sizes_do_not_match: + asmcmd =err 'operand sizes do not match' + exit + invalid_address: + asmcmd =err 'invalid address' + exit + invalid_address_size: + asmcmd =err 'invalid address size' + exit + address_out_of_range: + asmcmd =err 'address out of range' + exit + +end calminstruction + +calminstruction x86.parse_jump_operand namespace, operand + + local jump_type + local sym + + match =far? operand, operand + jyes far_jump + match =near? operand, operand + jyes near_jump + match =short? operand, operand + jyes short_jump + compute jump_type, '' + jump parse_operand + far_jump: + compute jump_type, 'far' + jump parse_operand + near_jump: + compute jump_type, 'near' + jump parse_operand + short_jump: + compute jump_type, 'short' + + parse_operand: + + arrange operand, =x86.=parse_operand namespace, operand + assemble operand + + arrange sym, namespace.=jump_type + publish sym, jump_type + + local type, size + + arrange type, namespace.=type + check type = 'imm' + jno done + + arrange size, namespace.=size + check size = 0 + jno verify_target_address + + compute size, x86.mode shr 3 + arrange sym, namespace.=size + publish sym, size + + verify_target_address: + + local imm + + arrange sym, namespace.=imm + compute imm, sym + + check imm relativeto 0 + jno done + check imm < 0 + jyes negative + check imm >= 1 shl (size*8) + jno done + out_of_range: + asmcmd =err 'value out of range' + exit + negative: + check imm < - 1 shl (size*8-1) + jyes out_of_range + compute imm, imm and (1 shl (size*8) - 1) + publish sym, imm + + done: + +end calminstruction + +calminstruction x86.select_operand_prefix rm_operand*,size* + + local sym, prefix + + check (size = 2 & x86.mode <> 16) | (size = 4 & x86.mode = 16) + jyes prefix_66h + check size = 8 + jyes prefix_48h + check size <> 0 & size <> 2 & size <> 4 + jyes invalid_size + exit + + prefix_66h: + compute prefix, 66h + arrange sym, rm_operand.=prefix + publish sym, prefix + exit + + prefix_48h: + compute prefix, 48h + arrange sym, rm_operand.=prefix + publish sym, prefix + exit + + invalid_size: + asmcmd =err 'invalid operand size' + +end calminstruction + +calminstruction x86.store_operand_prefix size*, reg:0 + + local rex_prefix + + compute rex_prefix, 0 + + check (size = 2 & x86.mode <> 16) | (size = 4 & x86.mode = 16) + jyes prefix_66h + check size = 8 + jyes rex_8 + check size <> 0 & size <> 2 & size <> 4 + jno check_register + asmcmd =err 'invalid operand size' + jump check_register + + prefix_66h: + asmcmd =db 66h + jump check_register + + rex_8: + compute rex_prefix, 48h + + check_register: + check reg and 1000b + jyes rex_1 + check reg and x86.REX_REQUIRED + jno rex_ready + compute rex_prefix, rex_prefix or 40h + jump rex_ready + rex_1: + compute rex_prefix, rex_prefix or 41h + + rex_ready: + check rex_prefix + jno rex_prefix_ok + check x86.mode < 64 + jyes instruction_requires_long_mode + check reg and x86.REX_FORBIDDEN + jno store_rex_prefix + asmcmd =err 'disallowed combination of registers' + jump store_rex_prefix + instruction_requires_long_mode: + asmcmd =err 'instruction requires long mode' + store_rex_prefix: + asmcmd =db rex_prefix + rex_prefix_ok: + +end calminstruction + +calminstruction x86.store_instruction opcode*,rm_operand*,reg*,imm_size:0,imm + + local segment_prefix, prefix, opcode_prefix, rex_prefix + local mode, mod, rm + local scale, index, base + local displacement, displacement_size, auto_relative + local sym + + arrange segment_prefix, rm_operand.=segment_prefix + arrange prefix, rm_operand.=prefix + arrange opcode_prefix, rm_operand.=opcode_prefix + arrange rex_prefix, rm_operand.=rex_prefix + + arrange mode, rm_operand.=mode + arrange mod, rm_operand.=mod + arrange rm, rm_operand.=rm + + arrange scale, rm_operand.=scale + arrange index, rm_operand.=index + arrange base, rm_operand.=base + + arrange displacement_size, rm_operand.=displacement_size + arrange displacement, rm_operand.=displacement + arrange auto_relative, rm_operand.=auto_relative + + check segment_prefix + jno segment_prefix_ok + + check mode = 64 + jyes segment_in_long_mode + check mode = 16 & ( rm = 2 | rm = 3 | ( mod > 0 & rm = 6 ) ) + jyes ss_segment_default + check mode = 32 & ( ( mod > 0 & rm = 5 ) | ( rm = 4 & base = 4 ) | ( mod > 0 & rm = 4 & base = 5 ) ) + jyes ss_segment_default + + ds_segment_default: + check segment_prefix = 3Eh + jyes segment_prefix_ok + jump store_segment_prefix + ss_segment_default: + check segment_prefix = 36h + jyes segment_prefix_ok + jump store_segment_prefix + segment_in_long_mode: + check segment_prefix < 64h + jyes segment_prefix_ok + store_segment_prefix: + asmcmd =db segment_prefix + segment_prefix_ok: + + check mod <> 11b & mode <> x86.mode + jno addressing_prefix_ok + check mode = 64 | (mode = 16 & x86.mode = 64) + jno store_addressing_prefix + asmcmd =err 'illegal addressing mode' + store_addressing_prefix: + asmcmd =db 67h + addressing_prefix_ok: + + check (reg or rm) and x86.REX_REQUIRED + jno rex_1 + compute rex_prefix, rex_prefix or 40h + rex_1: + check rm and 1000b | (mod <> 11b & mode > 16 & rm = 4 & base and 1000b) + jno rex_2 + compute rex_prefix, rex_prefix or 41h + rex_2: + check mod <> 11b & mode > 16 & rm = 4 & index and 1000b + jno rex_4 + compute rex_prefix, rex_prefix or 42h + rex_4: + check reg and 1000b + jno rex_8 + compute rex_prefix, rex_prefix or 44h + rex_8: + check prefix = 48h + jno operand_prefix + compute rex_prefix, rex_prefix or 48h + jump operand_prefix_ok + + operand_prefix: + check prefix + jno operand_prefix_ok + asmcmd =db prefix + operand_prefix_ok: + + check opcode_prefix + jno opcode_prefix_ok + asmcmd =db opcode_prefix + opcode_prefix_ok: + + check rex_prefix + jno rex_prefix_ok + check x86.mode < 64 + jyes instruction_requires_long_mode + check reg and x86.REX_FORBIDDEN + jno store_rex_prefix + asmcmd =err 'disallowed combination of registers' + jump store_rex_prefix + instruction_requires_long_mode: + asmcmd =err 'instruction requires long mode' + store_rex_prefix: + asmcmd =db rex_prefix + rex_prefix_ok: + + local modrm, sib + + compute modrm, mod shl 6 + (reg and 111b) shl 3 + rm and 111b + asmcmd =db opcode, modrm + + check mod <> 11b & rm = 4 & mode <> 16 + jno sib_ok + compute sib, (bsf scale) shl 6 + (index and 111b) shl 3 + base and 111b + asmcmd =db sib + sib_ok: + + check displacement_size = 1 + jyes displacement_8bit + check displacement_size = 2 + jyes displacement_16bit + check displacement_size = 4 | displacement_size = 8 + jno displacement_ok + + compute displacement, displacement + + check auto_relative + jno auto_relative_ok + check imm_size < 8 + jyes adjust_auto_relative_displacement + compute displacement, displacement - ($ + 4 + 4) + jump auto_relative_ok + adjust_auto_relative_displacement: + compute displacement, displacement - ($ + 4 + imm_size) + auto_relative_ok: + + check mode = 64 & displacement relativeto 0 + jno displacement_ready + check displacement - 1 shl 64 >= -80000000h & displacement < 1 shl 64 + jyes adjust_displacement_wrap + check displacement >= -80000000h & displacement < 80000000h + jyes displacement_ready + asmcmd =err 'address value out of signed range' + adjust_displacement_wrap: + compute displacement, displacement - 1 shl 64 + displacement_ready: + + arrange sym, rm_operand.=displacement + publish sym, displacement + + asmcmd =dd rm_operand.=displacement + + jump displacement_ok + displacement_16bit: + asmcmd =dw rm_operand.=displacement + jump displacement_ok + displacement_8bit: + asmcmd =db rm_operand.=displacement + displacement_ok: + + check imm_size = 1 + jyes immediate_8bit + check imm_size = 2 + jyes immediate_16bit + check imm_size = 4 + jyes immediate_32bit + check imm_size = 8 + jno immediate_ok + asmcmd =x86.=simm32 imm + jump immediate_ok + immediate_32bit: + asmcmd =dd imm + jump immediate_ok + immediate_16bit: + asmcmd =dw imm + jump immediate_ok + immediate_8bit: + asmcmd =db imm + immediate_ok: + +end calminstruction + +calminstruction x86.simm32 imm + compute x86.imm64, +imm + check x86.imm64 eqtype 0.0 + jno check_range + asmcmd =virtual =at 0 + asmcmd =emit 8:=x86.=imm64 + asmcmd =load =x86.=imm64:8 =from 0 + asmcmd =end =virtual + compute x86.imm64, +@x86.imm64 + check_range: + check x86.imm64 relativeto 0 & x86.imm64 - 1 shl 64 >= -80000000h & x86.imm64 < 1 shl 64 + jyes wrap + check x86.imm64 relativeto 0 & (x86.imm64 >= 80000000h | x86.imm64 < -80000000h) + jyes out_of_range + asmcmd =dd =x86.=imm64 + exit + wrap: + asmcmd =dd =x86.=imm64 - 1 =shl 64 + exit + out_of_range: + asmcmd =err 'immediate value out of signed range' +end calminstruction + + +iterate , add,0, or,8, adc,10h, sbb,18h, and,20h, sub,28h, xor,30h, cmp,38h + + calminstruction instr? dest*,src* + + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + + local opcode, rm, size + + compute opcode, basecode + + check @dest.size = 0 & @src.size = 0 + jyes operand_size_not_specified + check @dest.size <> 0 & @src.size <> 0 & @dest.size <> @src.size + jyes operand_sizes_do_not_match + + compute size, @dest.size or @src.size + + main: + + check @src.type = 'reg' & ( @dest.type = 'reg' | @dest.type = 'mem' ) + jyes reg_rm + check @src.type = 'mem' & @dest.type = 'reg' + jyes rm_reg + check @src.type = 'imm' & ( @dest.type = 'reg' | @dest.type = 'mem' ) + jyes rm_imm + + asmcmd =err 'invalid combination of operands' + exit + + operand_size_not_specified: + asmcmd =err 'operand size not specified' + compute size, 0 + jump main + + operand_sizes_do_not_match: + asmcmd =err 'operand sizes do not match' + compute size, 0 + jump main + + reg_rm: + check size > 1 + jno reg_rm_store + asmcmd =x86.=select_operand_prefix =@dest,size + compute opcode, opcode + 1 + reg_rm_store: + asmcmd =x86.=store_instruction opcode,=@dest,=@src.=rm + exit + + rm_reg: + compute opcode, opcode + 2 + check size > 1 + jno rm_reg_store + asmcmd =x86.=select_operand_prefix =@src,size + compute opcode, opcode + 1 + rm_reg_store: + asmcmd =x86.=store_instruction opcode,=@src,=@dest.=rm + exit + + rm_imm: + check size > 1 + jyes rm_imm_word + check @dest.type = 'reg' & @dest.rm = 0 + jyes al_imm + + compute opcode, opcode shr 3 + asmcmd =x86.=store_instruction 80h,=@dest,opcode,1,=@src.=imm + exit + + al_imm: + compute opcode, opcode+4 + asmcmd =db opcode, =@src.=imm + exit + + rm_imm_word: + + asmcmd =x86.=select_operand_prefix =@dest,size + + check @src.imm eqtype 0.0 + jno rm_imm_optimize + + asmcmd =virtual =at 0 + asmcmd =emit size:=@src.=imm + asmcmd =load =@src.=imm:size =from 0 + asmcmd =end =virtual + compute @src.imm, +@src.imm + + rm_imm_optimize: + check @src.imm relativeto 0 & @src.imm < 80h & @src.imm >= -80h + jyes rm_simm + check @src.imm relativeto 0 & @src.imm - 1 shl (size shl 3) >= -80h & @src.imm < 1 shl (size shl 3) + jyes rm_simm_wrap + check @dest.type = 'reg' & @dest.rm = 0 + jyes ax_imm + + compute rm, opcode shr 3 + asmcmd =x86.=store_instruction 81h,=@dest,rm,size,=@src.=imm + exit + + ax_imm: + check @dest.prefix + jno ax_imm_prefix_ok + asmcmd =db =@dest.=prefix + ax_imm_prefix_ok: + compute opcode, opcode+5 + asmcmd =db opcode + check size = 4 + jyes imm32 + check size = 8 + jyes simm32 + asmcmd =emit size:=@src.=imm + exit + imm32: + asmcmd =dd =@src.=imm + exit + simm32: + asmcmd =x86.=simm32 =@src.=imm + exit + + rm_simm_wrap: + compute @src.imm, @src.imm - 1 shl (size shl 3) + + rm_simm: + compute rm, opcode shr 3 + asmcmd =x86.=store_instruction 83h,=@dest,rm,1,=@src.=imm + + end calminstruction + +end iterate + +iterate , not,2, neg,3, mul,4, div,6, idiv,7 + + calminstruction instr? src* + + asmcmd =x86.=parse_operand =@src,src + + check @src.size = 0 + jyes operand_size_not_specified + + main: + check @src.type = 'mem' | @src.type = 'reg' + jno invalid_operand + check @src.size > 1 + jyes rm_word + + asmcmd =x86.=store_instruction 0F6h,=@src,postbyte + exit + + rm_word: + asmcmd =x86.=select_operand_prefix =@src,=@src.=size + asmcmd =x86.=store_instruction 0F7h,=@src,postbyte + exit + + operand_size_not_specified: + asmcmd =err 'operand size not specified' + jump main + + invalid_operand: + asmcmd =err 'invalid operand' + exit + + end calminstruction + +end iterate + +calminstruction mov? dest*,src* + + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + + local ext, rm, size + + check @dest.size = 0 & @src.size = 0 & @dest.type <> 'sreg' + jyes operand_size_not_specified + check @dest.size <> 0 & @src.size <> 0 & @dest.size <> @src.size + jyes operand_sizes_do_not_match + + compute size, @dest.size or @src.size + + main: + + check @src.type = 'reg' & ( @dest.type = 'reg' | @dest.type = 'mem' ) + jyes mov_rm_reg + check @src.type = 'mem' & @dest.type = 'reg' + jyes mov_reg_mem + check @src.type = 'imm' & ( @dest.type = 'reg' | @dest.type = 'mem' ) + jyes mov_rm_imm + check @src.type = 'reg' & @dest.type = 'imm' + jyes mov_creg_reg + check @src.type = 'sreg' & ( @dest.type = 'reg' | @dest.type = 'mem' ) + jyes mov_rm_sreg + check @dest.type = 'sreg' & @dest.rm <> 1 & ( @src.type = 'reg' | @src.type = 'mem' ) + jyes mov_sreg_rm + + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' + exit + + operand_size_not_specified: + asmcmd =err 'operand size not specified' + compute size, 0 + jump main + + operand_sizes_do_not_match: + asmcmd =err 'operand sizes do not match' + compute size, 0 + jump main + + mov_rm_reg: + check @src.type = 'reg' & @dest.type = 'mem' & @src.rm = 0 & @dest.address_registers eq 0 & \ + ~ @dest.auto_relative & ( @dest.displacement_size <> 8 | ~ @dest.displacement relativeto 0 | @dest.displacement and 0FFFFFFFF80000000h <> 0FFFFFFFF80000000h) + jyes mov_dirmem_ax + check size > 1 + jno mov_reg_rm_8bit + asmcmd =x86.=select_operand_prefix =@dest,size + asmcmd =x86.=store_instruction 89h,=@dest,=@src.=rm + exit + mov_reg_rm_8bit: + asmcmd =x86.=store_instruction 88h,=@dest,=@src.=rm + exit + + mov_reg_mem: + check @src.type = 'mem' & @dest.type = 'reg' & @dest.rm = 0 & @src.address_registers eq 0 & \ + ~ @src.auto_relative & ( @src.displacement_size <> 8 | ~ @src.displacement relativeto 0 | @src.displacement and 0FFFFFFFF80000000h <> 0FFFFFFFF80000000h) + jyes mov_ax_dirmem + check size > 1 + jno mov_mem_reg_8bit + asmcmd =x86.=select_operand_prefix =@src,size + asmcmd =x86.=store_instruction 8Bh,=@src,=@dest.=rm + exit + mov_mem_reg_8bit: + asmcmd =x86.=store_instruction 8Ah,=@src,=@dest.=rm + exit + + mov_dirmem_ax: + check x86.mode = 64 + jyes mov_dirmem_ax_longmode + check @dest.segment_prefix = 0 | @dest.segment_prefix = 3Eh + jyes dest_seg_ok + asmcmd =db =@dest.=segment_prefix + dest_seg_ok: + check @dest.mode = x86.mode + jyes dest_addr_size_ok + asmcmd =db 67h + dest_addr_size_ok: + check size > 1 + jno mov_dirmem_al + asmcmd =x86.=store_operand_prefix size + asmcmd =db 0A3h + jump dest_displacement + mov_dirmem_al: + asmcmd =db 0A2h + dest_displacement: + check @dest.mode = 16 + jyes dest_displacement_16bit + check @dest.displacement_size = 8 + jyes dest_displacement_64bit + asmcmd =dd =@dest.=address + exit + dest_displacement_16bit: + asmcmd =dw =@dest.=address + exit + dest_displacement_64bit: + asmcmd =dq =@dest.=address + exit + mov_dirmem_ax_longmode: + check @dest.displacement_size = 8 & @dest.displacement relativeto 0 & @dest.displacement >= 0 & @dest.displacement < 100000000h + jno dest_displacement_size_ok + compute @dest.displacement_size, 4 + dest_displacement_size_ok: + check @dest.segment_prefix & @dest.segment_prefix >= 64h + jno dest_longmode_seg_ok + asmcmd =db =@dest.=segment_prefix + dest_longmode_seg_ok: + check @dest.mode = 16 + jyes illegal_addressing_mode + check @dest.displacement_size = 8 + jyes dest_addr_size_ok + asmcmd =db 67h + jump dest_addr_size_ok + + mov_ax_dirmem: + check x86.mode = 64 + jyes mov_ax_dirmem_longmode + check @src.segment_prefix = 0 | @src.segment_prefix = 3Eh + jyes src_seg_ok + asmcmd =db =@src.=segment_prefix + src_seg_ok: + check @src.mode = x86.mode + jyes src_addr_size_ok + asmcmd =db 67h + src_addr_size_ok: + check size > 1 + jno mov_al_dirmem + asmcmd =x86.=store_operand_prefix size + asmcmd =db 0A1h + jump src_displacement + mov_al_dirmem: + asmcmd =db 0A0h + src_displacement: + check @src.mode = 16 + jyes src_displacement_16bit + check @src.displacement_size = 8 + jyes src_displacement_64bit + asmcmd =dd =@src.=address + exit + src_displacement_16bit: + asmcmd =dw =@src.=address + exit + src_displacement_64bit: + asmcmd =dq =@src.=address + exit + mov_ax_dirmem_longmode: + check @src.displacement_size = 8 & @src.displacement relativeto 0 & @src.displacement >= 0 & @src.displacement < 100000000h + jno src_displacement_size_ok + compute @src.displacement_size, 4 + src_displacement_size_ok: + check @src.segment_prefix & @src.segment_prefix >= 64h + jno src_longmode_seg_ok + asmcmd =db =@src.=segment_prefix + src_longmode_seg_ok: + check @src.mode = 16 + jyes illegal_addressing_mode + check @src.displacement_size = 8 + jyes src_addr_size_ok + asmcmd =db 67h + jump src_addr_size_ok + + mov_rm_imm: + check @dest.type = 'mem' + jyes mov_mem_imm + check @dest.type = 'reg' & 1 metadataof (1 metadataof @src.imm) relativeto x86.creg & @src.imm relativeto 1 elementof @src.imm + jyes mov_reg_creg + + mov_reg_imm: + check size > 1 + jno mov_reg_imm_8bit + check @src.imm eqtype 0.0 + jno mov_reg_imm_optimize + asmcmd =virtual =at 0 + asmcmd =emit size:=@src.=imm + asmcmd =load =@src.=imm:size =from 0 + asmcmd =end =virtual + compute @src.imm, +@src.imm + mov_reg_imm_optimize: + check size = 8 & @src.imm relativeto 0 & @src.imm < 80000000h & @src.imm >= -80000000h + jyes mov_reg_simm + check size = 8 & @src.imm relativeto 0 & @src.imm - 1 shl 64 < 80000000h & @src.imm - 1 shl 64 >= -80000000h + jyes mov_reg_simm_wrap + asmcmd =x86.=store_operand_prefix size,=@dest.=rm + asmcmd =db 0B8h + =@dest.=rm =and 111b + check size = 2 + jyes src_imm_16bit + check size = 4 + jyes src_imm_32bit + asmcmd =dq =@src.=imm + exit + src_imm_32bit: + asmcmd =dd =@src.=imm + exit + src_imm_16bit: + asmcmd =dw =@src.=imm + exit + mov_reg_imm_8bit: + asmcmd =x86.=store_operand_prefix 0,=@dest.=rm + asmcmd =db 0B0h + =@dest.=rm =and 111b + asmcmd =db =@src.=imm + exit + mov_reg_simm_wrap: + compute @src.imm, @src.imm - 1 shl 64 + mov_reg_simm: + asmcmd =x86.=select_operand_prefix =@dest,size + asmcmd =x86.=store_instruction 0C7h,=@dest,0,size,=@src.=imm + exit + + mov_mem_imm: + check size > 1 + jno mov_mem_imm_8bit + asmcmd =x86.=select_operand_prefix =@dest,size + asmcmd =x86.=store_instruction 0C7h,=@dest,0,size,=@src.=imm + exit + mov_mem_imm_8bit: + asmcmd =x86.=store_instruction 0C6h,=@dest,0,1,=@src.=imm + exit + + mov_reg_creg: + check (x86.mode <> 64 & @dest.size = 4) | (x86.mode = 64 & @dest.size = 8) + jno invalid_operand_size + compute ext, 20h + 1 metadataof (1 metadataof @src.imm) - x86.creg + compute rm, 1 metadataof @src.imm - 1 elementof (1 metadataof @src.imm) + asmcmd =x86.=store_instruction <0Fh,ext>,=@dest,rm + exit + + mov_creg_reg: + check 1 metadataof (1 metadataof @dest.imm) relativeto x86.creg & @dest.imm relativeto 1 elementof @dest.imm + jno invalid_combination_of_operands + check (x86.mode <> 64 & @src.size = 4) | (x86.mode = 64 & @src.size = 8) + jno invalid_operand_size + compute ext, 22h + 1 metadataof (1 metadataof @dest.imm) - x86.creg + compute rm, 1 metadataof @dest.imm - 1 elementof (1 metadataof @dest.imm) + asmcmd =x86.=store_instruction <0Fh,ext>,=@src,rm + exit + + mov_rm_sreg: + check @dest.type = 'mem' + jyes mov_mem_sreg + mov_reg_sreg: + check size > 1 + jno invalid_operand_size + asmcmd =x86.=select_operand_prefix =@dest,size + jump mov_rm_sreg_store + mov_mem_sreg: + check size and not 2 + jyes invalid_operand_size + mov_rm_sreg_store: + asmcmd =x86.=store_instruction 8Ch,=@dest,=@src.=rm + exit + + mov_sreg_rm: + check size = 1 + jyes invalid_operand_size + asmcmd =x86.=store_instruction 8Eh,=@src,=@dest.=rm + exit + + invalid_operand_size: + asmcmd =err 'invalid operand size' + exit + + illegal_addressing_mode: + asmcmd =err 'illegal addressing mode' + exit + +end calminstruction + +calminstruction test? dest*,src* + + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + + local ext, rm, size + + check @dest.size = 0 & @src.size = 0 + jyes operand_size_not_specified + check @dest.size <> 0 & @src.size <> 0 & @dest.size <> @src.size + jyes operand_sizes_do_not_match + + compute size, @dest.size or @src.size + + main: + + check @src.type = 'reg' & ( @dest.type = 'reg' | @dest.type = 'mem' ) + jyes test_reg_rm + check @src.type = 'mem' & @dest.type = 'reg' + jyes test_mem_reg + check @src.type = 'imm' & ( @dest.type = 'reg' | @dest.type = 'mem' ) + jyes test_rm_imm + + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' + exit + + operand_size_not_specified: + asmcmd =err 'operand size not specified' + compute size, 0 + jump main + + operand_sizes_do_not_match: + asmcmd =err 'operand sizes do not match' + compute size, 0 + jump main + + test_reg_rm: + check size > 1 + jno test_reg_rm_8bit + asmcmd =x86.=select_operand_prefix =@dest,size + asmcmd =x86.=store_instruction 85h,=@dest,=@src.=rm + exit + test_reg_rm_8bit: + asmcmd =x86.=store_instruction 84h,=@dest,=@src.=rm + exit + + test_mem_reg: + check size > 1 + jno test_mem_reg_8bit + asmcmd =x86.=select_operand_prefix =@src,size + asmcmd =x86.=store_instruction 85h,=@src,=@dest.=rm + exit + test_mem_reg_8bit: + asmcmd =x86.=store_instruction 84h,=@src,=@dest.=rm + exit + + test_rm_imm: + check size > 1 + jno test_rm_imm_8bit + check @dest.type = 'reg' & @dest.rm = 0 + jyes test_ax_imm + asmcmd =x86.=select_operand_prefix =@dest,size + asmcmd =x86.=store_instruction 0F7h,=@dest,0,size,=@src.=imm + exit + + test_ax_imm: + asmcmd =x86.=store_operand_prefix size + asmcmd =db 0A9h + check size = 2 + jyes src_imm_16bit + check size = 4 + jyes src_imm_32bit + asmcmd =x86.=simm32 =@src.=imm + exit + src_imm_16bit: + asmcmd =dw =@src.=imm + exit + src_imm_32bit: + asmcmd =dd =@src.=imm + exit + + test_rm_imm_8bit: + check @dest.type = 'reg' & @dest.rm = 0 + jyes test_al_imm + asmcmd =x86.=store_instruction 0F6h,=@dest,0,1,=@src.=imm + exit + test_al_imm: + asmcmd =db 0A8h, =@src.=imm + exit + +end calminstruction + +calminstruction xchg? dest*,src* + + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + + local ext, rm, size + + check @dest.size = 0 & @src.size = 0 + jyes operand_size_not_specified + check @dest.size <> 0 & @src.size <> 0 & @dest.size <> @src.size + jyes operand_sizes_do_not_match + + compute size, @dest.size or @src.size + + main: + + check @src.type = 'reg' & @dest.type = 'reg' + jyes xchg_reg_reg + check @src.type = 'reg' & @dest.type = 'mem' + jyes xchg_reg_rm + check @src.type = 'mem' & @dest.type = 'reg' + jyes xchg_rm_reg + + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' + exit + + operand_size_not_specified: + asmcmd =err 'operand size not specified' + compute size, 0 + jump main + + operand_sizes_do_not_match: + asmcmd =err 'operand sizes do not match' + compute size, 0 + jump main + + xchg_reg_reg: + check (@src.rm & @dest.rm) | (size = 4 & @src.rm or @dest.rm = 0) + jyes xchg_rm_reg + check size > 1 + jno xchg_rm_reg_8bit + compute @src.rm, @src.rm or @dest.rm + asmcmd =x86.=store_operand_prefix size,=@src.=rm + local opcode + compute opcode, 90h + @src.rm and 111b + asmcmd =db opcode + exit + + xchg_reg_rm: + check size > 1 + jno xchg_reg_rm_8bit + asmcmd =x86.=select_operand_prefix =@dest,size + asmcmd =x86.=store_instruction 87h,=@dest,=@src.=rm + exit + xchg_reg_rm_8bit: + asmcmd =x86.=store_instruction 86h,=@dest,=@src.=rm + exit + + xchg_rm_reg: + check size > 1 + jno xchg_rm_reg_8bit + asmcmd =x86.=select_operand_prefix =@src,size + asmcmd =x86.=store_instruction 87h,=@src,=@dest.=rm + exit + xchg_rm_reg_8bit: + asmcmd =x86.=store_instruction 86h,=@src,=@dest.=rm + exit + +end calminstruction + +iterate , inc,0 ,dec,1 + + calminstruction instr? dest* + + asmcmd =x86.=parse_operand =@dest,dest + + check @dest.size + jyes main + + asmcmd =err 'operand size not specified' + + main: + check @dest.type = 'mem' | (x86.mode = 64 & @dest.type = 'reg') + jyes inc_rm + check @dest.type = 'reg' + jyes inc_reg + + asmcmd =err 'invalid operand' + exit + + inc_reg: + check @dest.size > 1 + jno inc_rm_8bit + asmcmd =x86.=store_operand_prefix =@dest.=size + local opcode + compute opcode, 40h + @dest.rm + postbyte shl 3 + asmcmd =db opcode + exit + + inc_rm: + check @dest.size > 1 + jno inc_rm_8bit + asmcmd =x86.=select_operand_prefix =@dest,=@dest.=size + asmcmd =x86.=store_instruction 0FFh,=@dest,postbyte + exit + inc_rm_8bit: + asmcmd =x86.=store_instruction 0FEh,=@dest,postbyte + + end calminstruction + +end iterate + +calminstruction imul? dest*,src& + + local size + + asmcmd =x86.=parse_operand =@dest,dest + + match , src + jyes imul_rm + + local src1, src2 + + match src1 =, src2, src + jyes imul_second_source + + asmcmd =x86.=parse_operand =@src,src + + check @dest.size = 0 & @src.size = 0 + jyes operand_size_not_specified + check @dest.size <> 0 & @src.size <> 0 & @dest.size <> @src.size + jyes operand_sizes_do_not_match + + compute size, @dest.size or @src.size + + check @dest.type = 'reg' & (@src.type = 'reg' | @src.type = 'mem') + jyes imul_reg_rm + + check @src.type = 'imm' & @dest.type = 'reg' + jno invalid_combination_of_operands + + compute @aux.type, @src.type + compute @aux.imm, @src.imm + compute @src.type, @dest.type + compute @src.mod, @dest.mod + compute @src.rm, @dest.rm + + jump main + + imul_second_source: + asmcmd =x86.=parse_operand =@src,src1 + asmcmd =x86.=parse_operand =@aux,src2 + + check @dest.size = 0 & @src.size = 0 & @aux.size = 0 + jyes operand_size_not_specified + + compute size, @dest.size or @src.size or @aux.size + + check (@dest.size & @dest.size <> size) | (@src.size & @src.size <> size) | (@aux.size & @aux.size <> size) + jyes operand_sizes_do_not_match + + jump main + + operand_size_not_specified: + asmcmd =err 'operand size not specified' + compute size, 0 + jump main + + operand_sizes_do_not_match: + asmcmd =err 'operand sizes do not match' + compute size, 0 + jump main + + main: + check @aux.type = 'imm' & ( @src.type = 'mem' | @src.type = 'reg' ) & @dest.type = 'reg' + jyes imul_reg_rm_imm + + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' + exit + + imul_rm: + check @dest.size + jyes imul_rm_size_ok + asmcmd =err 'operand size not specified' + imul_rm_size_ok: + check @dest.type = 'mem' | @dest.type = 'reg' + jno invalid_combination_of_operands + check @dest.size > 1 + jno imul_rm_8bit + asmcmd =x86.=select_operand_prefix =@dest,=@dest.=size + asmcmd =x86.=store_instruction 0F7h,=@dest,5 + exit + imul_rm_8bit: + asmcmd =x86.=store_instruction 0F6h,=@dest,5 + exit + + imul_reg_rm: + asmcmd =x86.=select_operand_prefix =@src,size + asmcmd =x86.=store_instruction <0Fh,0AFh>,=@src,=@dest.=rm + exit + + imul_reg_rm_imm: + asmcmd =x86.=select_operand_prefix =@src,size + check @aux.imm eqtype 0.0 + jno imul_reg_rm_imm_optimize + asmcmd =virtual =at 0 + asmcmd =emit size:=@aux.=imm + asmcmd =load =@aux.=imm:size =from 0 + asmcmd =end =virtual + compute @aux.imm, +@aux.imm + imul_reg_rm_imm_optimize: + check @aux.imm relativeto 0 & @aux.imm < 80h & @aux.imm >= -80h + jyes imul_reg_rm_simm + check @aux.imm relativeto 0 & @aux.imm - 1 shl (size shl 3) >= -80h & @aux.imm < 1 shl (size shl 3) + jyes imul_reg_rm_simm_wrap + asmcmd =x86.=store_instruction 69h,=@src,=@dest.=rm,size,=@aux.=imm + exit + imul_reg_rm_simm_wrap: + compute @aux.imm, @aux.imm - 1 shl (size shl 3) + imul_reg_rm_simm: + asmcmd =x86.=store_instruction 6Bh,=@src,=@dest.=rm,1,=@aux.=imm + exit + +end calminstruction + +calminstruction x86.push_instruction size:0,src* + + local opcode + + asmcmd =x86.=parse_operand =@src,src + + check size <> 0 & @src.size and not size + jyes invalid_operand_size + compute size, size or @src.size + check size = 0 | size = 2 | (size = 4 & x86.mode < 64) | (size = 8 & x86.mode = 64) + jyes main + + invalid_operand_size: + asmcmd =err 'invalid operand size' + + main: + check (x86.mode <> 16 & size = 2) | (x86.mode = 16 & size = 4) + jno prefix_ready + compute @src.prefix, 66h + prefix_ready: + + check @src.type = 'mem' + jyes push_mem + + check @src.prefix + jno prefix_stored + asmcmd =db =@src.=prefix + prefix_stored: + + check @src.type = 'reg' + jyes push_reg + check @src.type = 'sreg' + jyes push_sreg + check @src.type = 'imm' + jyes push_imm + + invalid_operand: + asmcmd =err 'invalid operand' + exit + + push_mem: + asmcmd =x86.=store_instruction 0FFh,=@src,110b + exit + + push_reg: + check @src.rm and 1000b + jyes push_new_reg + compute opcode, 50h + @src.rm + asmcmd =db opcode + exit + push_new_reg: + compute opcode, 50h + @src.rm and 111b + asmcmd =db 41h,opcode + exit + + push_sreg: + check @src.rm >= 4 + jyes push_sreg_386 + check x86.mode = 64 + jyes invalid_operand + compute opcode, 6 + @src.rm shl 3 + asmcmd =db opcode + exit + push_sreg_386: + compute opcode, 0A0h + (@src.rm-4) shl 3 + asmcmd =db 0Fh,opcode + exit + + push_imm: + check size + jyes push_imm_size_ok + check x86.mode = 16 + jyes push_imm_16bit + check x86.mode = 32 + jyes push_imm_32bit + compute size, 8 + jump push_imm_size_ok + push_imm_32bit: + compute size, 4 + jump push_imm_size_ok + push_imm_16bit: + compute size, 2 + push_imm_size_ok: + + check @src.imm eqtype 0.0 + jno push_imm_check + asmcmd =virtual =at 0 + asmcmd =emit size:=@src.=imm + asmcmd =load =@src.=imm:size =from 0 + asmcmd =end =virtual + compute @src.imm, +@src.imm + push_imm_check: + check size = 8 & @src.imm relativeto 0 + jno push_imm_optimize + check @src.imm - 10000000000000000h >= -80000000h & @src.imm < 10000000000000000h + jyes push_imm_wrap + check @src.imm >= 80000000h | @src.imm < -80000000h + jyes out_of_signed_range + jump push_imm_optimize + push_imm_wrap: + compute @src.imm, @src.imm - 10000000000000000h + push_imm_optimize: + check @src.imm relativeto 0 & @src.imm < 80h & @src.imm >= -80h + jyes push_simm + check size = 2 & @src.imm relativeto 0 & @src.imm - 10000h >= -80h & @src.imm < 10000h + jyes push_simm_wrap + check size = 4 & @src.imm relativeto 0 & @src.imm - 100000000h >= -80h & @src.imm < 100000000h + jyes push_simm_wrap + asmcmd =db 68h + check size = 2 + jyes src_imm_16bit + asmcmd =dd =@src.=imm + exit + src_imm_16bit: + asmcmd =dw =@src.=imm + exit + push_simm_wrap: + compute @src.imm, @src.imm - 1 shl (size shl 3) + push_simm: + asmcmd =db 6Ah, =@src.=imm + exit + + out_of_signed_range: + asmcmd =err 'immediate value out of signed range' + exit + +end calminstruction + +calminstruction x86.pop_instruction size:0,dest* + + local opcode + + asmcmd =x86.=parse_operand =@dest,dest + + check size <> 0 & @dest.size and not size + jyes invalid_operand_size + compute size, size or @dest.size + check size = 0 | size = 2 | (size = 4 & x86.mode < 64) | (size = 8 & x86.mode = 64) + jyes main + + invalid_operand_size: + asmcmd =err 'invalid operand size' + + main: + check (x86.mode <> 16 & size = 2) | (x86.mode = 16 & size = 4) + jno prefix_ready + compute @dest.prefix, 66h + prefix_ready: + + check @dest.type = 'mem' + jyes pop_mem + + check @dest.prefix + jno prefix_stored + asmcmd =db =@dest.=prefix + prefix_stored: + + check @dest.type = 'reg' + jyes pop_reg + check @dest.type = 'sreg' + jyes pop_sreg + + invalid_operand: + asmcmd =err 'invalid operand' + exit + + pop_mem: + asmcmd =x86.=store_instruction 8Fh,=@dest,0 + exit + + pop_reg: + check @dest.rm and 1000b + jyes pop_new_reg + compute opcode, 58h + @dest.rm + asmcmd =db opcode + exit + pop_new_reg: + compute opcode, 58h + @dest.rm and 111b + asmcmd =db 41h,opcode + exit + + pop_sreg: + check @dest.rm = 1 + jyes invalid_operand + check @dest.rm >= 4 + jyes pop_sreg_386 + check x86.mode = 64 + jyes invalid_operand + compute opcode, 7 + @dest.rm shl 3 + asmcmd =db opcode + exit + pop_sreg_386: + compute opcode, 0A1h + (@dest.rm-4) shl 3 + asmcmd =db 0Fh,opcode + exit + +end calminstruction + +iterate reg, ax,cx,dx,bx,sp,bp,si,di,r8w,r9w,r10w,r11w,r12w,r13w,r14w,r15w, \ + eax,ecx,edx,ebx,esp,ebp,esi,edi,r8d,r9d,r10d,r11d,r12d,r13d,r14d,r15d, \ + rax,rcx,rdx,rbx,rsp,rbp,rsi,rdi,r8,r9,r10,r11,r12,r13,r14,r15, \ + es,cs,ss,ds,fs,gs + define x86.compact.reg? {reg} +end iterate + +iterate , push,push_instruction,0, pushw,push_instruction,2, pushd,push_instruction,4, pushq,push_instruction,8, \ + pop,pop_instruction,0, popw,pop_instruction,2, popd,pop_instruction,4, popq,pop_instruction,8 + + calminstruction instr? operand + + local head, tail + + match head tail, operand + jno plain + transform head, x86.compact + jno plain + match {head}, head + jno plain + loop: + asmcmd =x86.=handler size,head + match head tail, tail + jno final + transform head, x86.compact + jno error + match {head}, head + jyes loop + error: + asmcmd =err 'only register operands allowed in compact syntax' + exit + final: + transform tail, x86.compact + jno error + match {operand}, tail + jno error + plain: + asmcmd =x86.=handler size,operand + + end calminstruction + +end iterate + +iterate , ret,0C2h, retn,0C2h, retf,0CAh + + calminstruction instr? operand + match , operand + jyes ret_short + check operand + jno ret_short + asmcmd =db opcode + asmcmd =dw operand + exit + ret_short: + asmcmd =db opcode + 1 + end calminstruction + +end iterate + +iterate , retw,2,0C2h, retnw,2,0C2h, retd,4,0C2h, retnd,4,0C2h, retfw,2,0CAh, retfd,4,0CAh + + calminstruction instr? operand + check x86.mode < 64 + jyes illegal_instruction + asmcmd =x86.=store_operand_prefix size + match , operand + jyes ret_short + asmcmd =db opcode + asmcmd =dw operand + exit + ret_short: + asmcmd =db opcode + 1 + exit + illegal_instruction: + asmcmd =err 'illegal instruction' + end calminstruction + +end iterate + +iterate , retq,0C2h, retnq,0C2h, retfq,0CAh + + calminstruction instr? operand + check x86.mode = 64 + jno instruction_requires_long_mode + match , operand + jyes ret_short + asmcmd =db opcode + asmcmd =dw operand + exit + ret_short: + asmcmd =db opcode + 1 + exit + instruction_requires_long_mode: + asmcmd =err 'instruction requires long mode' + end calminstruction + +end iterate + +calminstruction lea? dest*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + check @src.type = 'mem' & @dest.type = 'reg' + jno invalid_combination_of_operands + asmcmd =x86.=select_operand_prefix =@src,=@dest.=size + asmcmd =x86.=store_instruction 8Dh,=@src,=@dest.=rm + exit + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' +end calminstruction + +iterate , les,0C4h, lds,0C5h + + calminstruction instr? dest*,src* + check x86.mode = 64 + jyes illegal_instruction + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + check (@dest.size = 2 & (@src.size <> 0 & @src.size <> 4)) | (@dest.size = 4 & (@src.size <> 0 & @src.size <> 6)) + jyes invalid_operand_size + check @src.type = 'mem' & @dest.type = 'reg' + jno invalid_combination_of_operands + asmcmd =x86.=select_operand_prefix =@src,=@dest.=size + asmcmd =x86.=store_instruction opcode,=@src,=@dest.=rm + exit + illegal_instruction: + asmcmd =err 'illegal instruction' + exit + invalid_operand_size: + asmcmd =err 'invalid operand size' + exit + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' + end calminstruction + +end iterate + +iterate , lss,<0Fh,0B2h>, lfs,<0Fh,0B4h>, lgs,<0Fh,0B5h> + + calminstruction instr? dest*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + check (@dest.size = 2 & (@src.size <> 0 & @src.size <> 4)) | (@dest.size = 4 & (@src.size <> 0 & @src.size <> 6)) | (@dest.size = 8 & (@src.size <> 0 & @src.size <> 10)) + jyes invalid_operand_size + check @src.type = 'mem' & @dest.type = 'reg' + jno invalid_combination_of_operands + asmcmd =x86.=select_operand_prefix =@src,=@dest.=size + asmcmd =x86.=store_instruction opcode,=@src,=@dest.=rm + exit + invalid_operand_size: + asmcmd =err 'invalid operand size' + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' + end calminstruction + +end iterate + +iterate , rol,0, ror,1, rcl,2, rcr,3, shl,4, sal, 4, shr,5, sar,7 + + calminstruction instr? dest*,cnt* + + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,cnt + + check @dest.size = 0 + jyes operand_size_not_specified + check @src.size and not 1 + jyes invalid_operand_size + + main: + check @src.type = 'reg' & @src.size = 1 & @src.rm = 1 & ( @dest.type = 'reg' | @dest.type = 'mem' ) + jyes shift_rm_cl + check @src.type = 'imm' & ( @dest.type = 'reg' | @dest.type = 'mem' ) + jyes shift_rm_imm + + asmcmd =err 'invalid combination of operands' + exit + + shift_rm_cl: + check @dest.size > 1 + jno shift_r8_cl + asmcmd =x86.=select_operand_prefix =@dest,=@dest.=size + asmcmd =x86.=store_instruction 0D3h,=@dest,postbyte + exit + shift_r8_cl: + asmcmd =x86.=store_instruction 0D2h,=@dest,postbyte + exit + shift_rm_imm: + check @dest.size > 1 + jno shift_rm8_imm + asmcmd =x86.=select_operand_prefix =@dest,=@dest.=size + check @src.imm = 1 + jyes shift_rm_1 + asmcmd =x86.=store_instruction 0C1h,=@dest,postbyte,1,=@src.=imm + exit + shift_rm_1: + asmcmd =x86.=store_instruction 0D1h,=@dest,postbyte + exit + shift_rm8_imm: + check @src.imm = 1 + jyes shift_rm8_1 + asmcmd =x86.=store_instruction 0C0h,=@dest,postbyte,1,=@src.=imm + exit + shift_rm8_1: + asmcmd =x86.=store_instruction 0D0h,=@dest,postbyte + exit + + operand_size_not_specified: + asmcmd =err 'operand size not specified' + jump main + invalid_operand_size: + asmcmd =err 'invalid operand size' + jump main + + end calminstruction + +end iterate + +iterate , shld,0A4h, shrd,0ACh + + calminstruction instr? dest*,src*,cnt* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + asmcmd =x86.=parse_operand =@aux,cnt + check @aux.size and not 1 + jyes invalid_operand_size + check @dest.size and not @src.size + jno main + asmcmd =err 'operand sizes do not match' + main: + check @aux.type = 'reg' & @aux.size = 1 & @aux.rm = 1 & @src.type = 'reg' & ( @dest.type = 'reg' | @dest.type = 'mem' ) + jyes shld_cl + check @aux.type = 'imm' & @src.type = 'reg' & ( @dest.type = 'reg' | @dest.type = 'mem' ) + jyes shld_imm + asmcmd =err 'invalid combination of operands' + exit + shld_cl: + asmcmd =x86.=select_operand_prefix =@dest,=@src.=size + asmcmd =x86.=store_instruction <0Fh,ext+1>,=@dest,=@src.=rm + exit + shld_imm: + asmcmd =x86.=select_operand_prefix =@dest,=@src.=size + asmcmd =x86.=store_instruction <0Fh,ext>,=@dest,=@src.=rm,1,=@aux.=imm + exit + invalid_operand_size: + asmcmd =err 'invalid operand size' + end calminstruction + +end iterate + +iterate , bt,4, bts,5, btr,6, btc,7 + + calminstruction instr? dest*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + check @src.type = 'reg' & (@dest.type = 'mem' | @dest.type = 'reg') + jyes bt_rm_reg + check @src.type = 'imm' & (@dest.type = 'mem' | @dest.type = 'reg') + jyes bt_rm_imm + asmcmd =err 'invalid combination of operands' + exit + bt_rm_reg: + local opcode + check @dest.size and not @src.size + jno bt_rm_reg_ok + asmcmd =err 'operand sizes do not match' + bt_rm_reg_ok: + compute opcode, 0A3h+(postbyte-4) shl 3 + asmcmd =x86.=select_operand_prefix =@dest,=@src.=size + asmcmd =x86.=store_instruction <0Fh,opcode>,=@dest,=@src.=rm + exit + bt_rm_imm: + check @src.size and not 1 + jno bt_rm_imm_ok + asmcmd =err 'invalid operand size' + bt_rm_imm_ok: + check @dest.size + jno operand_size_not_specified + asmcmd =x86.=select_operand_prefix =@dest,=@dest.=size + asmcmd =x86.=store_instruction <0Fh,0BAh>,=@dest,postbyte,1,=@src.=imm + exit + operand_size_not_specified: + asmcmd =err 'operand size not specified' + end calminstruction + +end iterate + +iterate , bsf,0BCh, bsr,0BDh + + calminstruction instr? dest*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + check @dest.type = 'reg' & (@src.type = 'mem' | @src.type = 'reg') + jyes bsf_rm_reg + asmcmd =err 'invalid combination of operands' + exit + bsf_rm_reg: + check @src.size and not @dest.size + jno bsf_rm_reg_ok + asmcmd =err 'operand sizes do not match' + bsf_rm_reg_ok: + asmcmd =x86.=select_operand_prefix =@src,=@dest.=size + asmcmd =x86.=store_instruction <0Fh,ext>,=@src,=@dest.=rm + end calminstruction + +end iterate + +iterate , movzx,0B6h, movsx,0BEh + + calminstruction instr? dest*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + check @dest.size > @src.size + jyes size_ok + asmcmd =err 'operand sizes do not match' + size_ok: + check @dest.type = 'reg' & (@src.type = 'mem' | @src.type = 'reg') + jyes operands_ok + asmcmd =err 'invalid combination of operands' + exit + operands_ok: + check @src.size = 2 + jyes movzx_word + check @src.size = 1 | (@src.size = 0 & @dest.size = 2) + jyes movzx_byte + check @src.size + jyes invalid_operand_size + asmcmd =err 'operand size not specified' + movzx_word: + asmcmd =x86.=select_operand_prefix =@src,=@dest.=size + asmcmd =x86.=store_instruction <0Fh,ext+1>,=@src,=@dest.=rm + exit + movzx_byte: + asmcmd =x86.=select_operand_prefix =@src,=@dest.=size + asmcmd =x86.=store_instruction <0Fh,ext>,=@src,=@dest.=rm + exit + invalid_operand_size: + asmcmd =err 'invalid operand size' + end calminstruction + +end iterate + +calminstruction movsxd? dest*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + check @dest.size > @src.size + jyes size_ok + asmcmd =err 'operand sizes do not match' + size_ok: + check @dest.type = 'reg' & (@src.type = 'mem' | @src.type = 'reg') + jyes operands_ok + asmcmd =err 'invalid combination of operands' + exit + operands_ok: + check @src.size and not 4 + jyes invalid_operand_size + asmcmd =x86.=select_operand_prefix =@src,=@dest.=size + asmcmd =x86.=store_instruction 63h,=@src,=@dest.=rm + exit + invalid_operand_size: + asmcmd =err 'invalid operand size' +end calminstruction + +iterate , o,0, no,1, c,2, b,2, nae,2, nc,3, nb,3, ae,3, z,4, e,4, nz,5, ne,5, na,6, be,6, a,7, nbe,7, \ + s,8, ns,9, p,0Ah, pe,0Ah, np,0Bh, po,0Bh, l,0Ch, nge,0Ch, nl,0Dh, ge,0Dh, ng,0Eh, le,0Eh, g,0Fh, nle,0Fh + + calminstruction set#cond? dest* + asmcmd =x86.=parse_operand =@dest,dest + check @dest.size > 1 + jno size_ok + asmcmd =err 'invalid operand size' + size_ok: + check @dest.type = 'reg' | @dest.type = 'mem' + jyes operand_ok + asmcmd =err 'invalid operand' + exit + operand_ok: + asmcmd =x86.=store_instruction <0Fh,90h+code>,=@dest,0 + end calminstruction + + calminstruction cmov#cond? dest*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + check @dest.type = 'reg' & (@src.type = 'mem' | @src.type = 'reg') + jyes cmov_rm_reg + asmcmd =err 'invalid combination of operands' + exit + cmov_rm_reg: + check @src.size and not @dest.size + jno cmov_rm_reg_ok + asmcmd =err 'operand sizes do not match' + cmov_rm_reg_ok: + asmcmd =x86.=select_operand_prefix =@src,=@dest.=size + asmcmd =x86.=store_instruction <0Fh,40h+code>,=@src,=@dest.=rm + end calminstruction + +end iterate + +calminstruction call? dest* + + asmcmd =x86.=parse_jump_operand =@dest,dest + + check @dest.type = 'imm' + jyes call_imm + check @dest.type = 'mem' | @dest.type = 'reg' + jyes call_rm + check @dest.type = 'far' + jyes call_direct_far + + invalid_operand: + asmcmd =err 'invalid operand' + exit + illegal_instruction: + asmcmd =err 'illegal instruction' + exit + + call_direct_far: + check @dest.jump_type & @dest.jump_type <> 'far' + jyes invalid_operand + check x86.mode = 64 + jyes illegal_instruction + check @dest.size = 0 + jyes prefix_ok + local size + compute size, @dest.size - 2 + asmcmd =x86.=store_operand_prefix size + prefix_ok: + check @dest.size and not 4 & @dest.size and not 6 + jyes invalid_operand + asmcmd =db 9Ah + check (@dest.size = 0 & x86.mode = 16) | @dest.size = 4 + jyes far_dword + asmcmd =dd =@dest.=offset + asmcmd =dw =@dest.=segment + exit + far_dword: + asmcmd =dw =@dest.=offset,=@dest.=segment + exit + + call_rm: + check @dest.size = 6 | @dest.size = 10 + jyes call_rm_pword + check @dest.size = 8 & x86.mode = 64 + jyes call_rm_qword + check @dest.size = 4 & x86.mode < 64 + jyes call_rm_dword + check @dest.size = 2 & x86.mode < 64 + jyes call_rm_word + check @dest.size + jyes invalid_operand + check x86.mode = 64 + jno rex_prefix_ok + compute @dest.prefix, 48h + rex_prefix_ok: + check @dest.jump_type = 'far' + jyes call_rm_far + check @dest.jump_type = 'near' + jyes call_rm_near + asmcmd =err 'operand size not specified' + exit + + call_rm_pword: + check @dest.jump_type & @dest.jump_type <> 'far' + jyes invalid_operand + local size + compute size, @dest.size - 2 + asmcmd =x86.=select_operand_prefix =@dest,size + call_rm_far: + asmcmd =x86.=store_instruction 0FFh,=@dest,11b + exit + + call_rm_qword: + check @dest.jump_type & @dest.jump_type <> 'near' + jyes invalid_operand + jump call_rm_near + + call_rm_dword: + check @dest.jump_type | @dest.type = 'reg' + jno call_rm_dword_auto + check @dest.jump_type = 'far' + jyes call_rm_dword_far + call_rm_dword_near: + asmcmd =x86.=select_operand_prefix =@dest,4 + jump call_rm_near + call_rm_dword_auto: + check x86.mode = 16 + jno call_rm_dword_near + call_rm_dword_far: + asmcmd =x86.=select_operand_prefix =@dest,2 + jump call_rm_far + + call_rm_word: + check @dest.jump_type & @dest.jump_type <> 'near' + jyes invalid_operand + asmcmd =x86.=select_operand_prefix =@dest,2 + call_rm_near: + asmcmd =x86.=store_instruction 0FFh,=@dest,10b + exit + + call_imm: + check @dest.jump_type & @dest.jump_type <> 'near' + jyes invalid_operand + check @dest.size = 8 & x86.mode = 64 + jyes call_imm_qword + check @dest.size = 2 & x86.mode < 64 + jyes call_imm_word + check @dest.size = 4 & x86.mode < 64 + jno invalid_operand + asmcmd =x86.=store_operand_prefix 4 + asmcmd =db 0E8h + compute @dest.imm, @dest.imm-($+4) + asmcmd =dd =@dest.=imm + exit + call_imm_word: + asmcmd =x86.=store_operand_prefix 2 + asmcmd =db 0E8h + compute @dest.imm, @dest.imm-($+2) + check @dest.imm relativeto 0 + jno store_word + compute @dest.imm, @dest.imm and 0FFFFh + store_word: + asmcmd =dw =@dest.=imm + exit + call_imm_qword: + asmcmd =db 0E8h + compute @dest.imm, @dest.imm-($+4) + check @dest.imm relativeto 0 & (@dest.imm < -80000000h | @dest.imm >= 80000000h) + jno store_dword + asmcmd =err 'relative jump out of range' + store_dword: + asmcmd =dd =@dest.=imm + exit + +end calminstruction + +calminstruction jmp? dest* + + asmcmd =x86.=parse_jump_operand =@dest,dest + + check @dest.type = 'imm' + jyes jmp_imm + check @dest.type = 'mem' | @dest.type = 'reg' + jyes jmp_rm + check @dest.type = 'far' + jyes jmp_direct_far + + invalid_operand: + asmcmd =err 'invalid operand' + exit + illegal_instruction: + asmcmd =err 'illegal instruction' + exit + + jmp_direct_far: + check @dest.jump_type & @dest.jump_type <> 'far' + jyes invalid_operand + check x86.mode = 64 + jyes illegal_instruction + check @dest.size = 0 + jyes prefix_ok + local size + compute size, @dest.size - 2 + asmcmd =x86.=store_operand_prefix size + prefix_ok: + check @dest.size and not 4 & @dest.size and not 6 + jyes invalid_operand + asmcmd =db 0EAh + check (@dest.size = 0 & x86.mode = 16) | @dest.size = 4 + jyes far_dword + asmcmd =dd =@dest.=offset + asmcmd =dw =@dest.=segment + exit + far_dword: + asmcmd =dw =@dest.=offset,=@dest.=segment + exit + + jmp_rm: + check @dest.size = 6 | @dest.size = 10 + jyes jmp_rm_pword + check @dest.size = 8 & x86.mode = 64 + jyes jmp_rm_qword + check @dest.size = 4 & x86.mode < 64 + jyes jmp_rm_dword + check @dest.size = 2 & x86.mode < 64 + jyes jmp_rm_word + check @dest.size + jyes invalid_operand + check x86.mode = 64 + jno rex_prefix_ok + compute @dest.prefix, 48h + rex_prefix_ok: + check @dest.jump_type = 'far' + jyes jmp_rm_far + check @dest.jump_type = 'near' + jyes jmp_rm_near + asmcmd =err 'operand size not specified' + exit + + jmp_rm_pword: + check @dest.jump_type & @dest.jump_type <> 'far' + jyes invalid_operand + local size + compute size, @dest.size - 2 + asmcmd =x86.=select_operand_prefix =@dest,size + jmp_rm_far: + asmcmd =x86.=store_instruction 0FFh,=@dest,101b + exit + + jmp_rm_qword: + check @dest.jump_type & @dest.jump_type <> 'near' + jyes invalid_operand + jump jmp_rm_near + + jmp_rm_dword: + check @dest.jump_type | @dest.type = 'reg' + jno jmp_rm_dword_auto + check @dest.jump_type = 'far' + jyes jmp_rm_dword_far + jmp_rm_dword_near: + asmcmd =x86.=select_operand_prefix =@dest,4 + jump jmp_rm_near + jmp_rm_dword_auto: + check x86.mode = 16 + jno jmp_rm_dword_near + jmp_rm_dword_far: + asmcmd =x86.=select_operand_prefix =@dest,2 + jump jmp_rm_far + + jmp_rm_word: + check @dest.jump_type & @dest.jump_type <> 'near' + jyes invalid_operand + asmcmd =x86.=select_operand_prefix =@dest,2 + jmp_rm_near: + asmcmd =x86.=store_instruction 0FFh,=@dest,100b + exit + + jmp_imm: + check @dest.jump_type & @dest.jump_type <> 'near' + jyes invalid_operand + check @dest.jump_type + jyes invalid_operand + check (@dest.size = 8 & x86.mode < 64) | (@dest.size < 8 & x86.mode = 64) + jyes invalid_operand + check @dest.size = 8 + jyes jmp_imm_prefix_ok + asmcmd =x86.=store_operand_prefix =@dest.=size + jmp_imm_prefix_ok: + check ~ $ relativeto 0 & @dest.imm relativeto 0 + jno jmp_optimize + compute @dest.imm, @dest.imm + $ - 0 scaleof $ + asmcmd =err 'invalid address' + jmp_optimize: + check @dest.unresolved + jyes jmp_imm_short + check @dest.imm relativeto $ + jno jmp_imm_near + check (@dest.imm-($+2)) < 80h & (@dest.imm-($+2)) >= -80h + jyes jmp_imm_short + check (@dest.imm-($+2)) and (1 shl (@dest.size*8) - 1) < 80h | (@dest.imm-($+2)) and (1 shl (@dest.size*8) - 1) >= 1 shl (@dest.size*8) - 80h + jyes jmp_imm_short + jmp_imm_near: + check @dest.size = 8 + jyes jmp_imm_qword + check @dest.size = 2 + jyes jmp_imm_word + check @dest.size = 4 + jno invalid_operand + asmcmd =db 0E9h + compute @dest.imm, @dest.imm-($+4) + asmcmd =dd =@dest.=imm + exit + jmp_imm_word: + asmcmd =db 0E9h + compute @dest.imm, @dest.imm-($+2) + check @dest.imm relativeto 0 + jno store_word + compute @dest.imm, @dest.imm and 0FFFFh + store_word: + asmcmd =dw =@dest.=imm + exit + jmp_imm_qword: + asmcmd =db 0E9h + compute @dest.imm, @dest.imm-($+4) + asmcmd =dd =@dest.=imm + check @dest.imm relativeto 0 & (@dest.imm < -80000000h | @dest.imm >= 80000000h) + jyes relative_jump_out_of_range + exit + jmp_imm_short_verify: + check (@dest.imm-($+2)) < 80h & (@dest.imm-($+2)) >= -80h + jyes jmp_imm_short + check (@dest.imm-($+2)) and (1 shl (@dest.size*8) - 1) < 80h | (@dest.imm-($+2)) and (1 shl (@dest.size*8) - 1) >= 1 shl (@dest.size*8) - 80h + jyes jmp_imm_short + asmcmd =db 0EBh, ? + relative_jump_out_of_range: + asmcmd =err 'relative jump out of range' + exit + jmp_imm_short: + asmcmd =db 0EBh + compute @dest.imm, (@dest.imm-($+1)) and 0FFh + asmcmd =db =@dest.=imm + exit + +end calminstruction + +iterate , jo,70h, jno,71h, jc,72h, jb,72h, jnae,72h, jnc,73h, jnb,73h, jae,73h, jz,74h, je,74h, jnz,75h, jne,75h, jna,76h, jbe,76h, ja,77h, jnbe,77h, \ + js,78h, jns,79h, jp,7Ah, jpe,7Ah, jnp,7Bh, jpo,7Bh, jl,7Ch, jnge,7Ch, jnl,7Dh, jge,7Dh, jng,7Eh, jle,7Eh, jg,7Fh, jnle,7Fh + + calminstruction instr? dest* + + asmcmd =x86.=parse_jump_operand =@dest,dest + + check @dest.type <> 'imm' | @dest.jump_type = 'far' + jyes invalid_operand + + check x86.mode = 64 | @dest.size = 8 + jyes long + asmcmd =x86.=store_operand_prefix =@dest.=size + jump prefix_ok + long: + check x86.mode < 64 | @dest.size <> 8 + jyes invalid_operand + prefix_ok: + + check ~ $ relativeto 0 & @dest.imm relativeto 0 + jno optimize + compute @dest.imm, @dest.imm + $ - 0 scaleof $ + asmcmd =err 'invalid address' + optimize: + + check @dest.jump_type <> 'near' & ( @dest.unresolved | ( @dest.imm relativeto $ & @dest.imm-($+2) < 80h & @dest.imm-($+2) >= -80h ) ) + jyes short + check @dest.jump_type <> 'near' & @dest.imm relativeto $ & ( (@dest.imm-($+2)) and (1 shl (@dest.size*8) - 1) < 80h | (@dest.imm-($+2)) and (1 shl (@dest.size*8) - 1) >= 1 shl (@dest.size*8) - 80h ) + jyes short + check @dest.jump_type = 'short' + jyes relative_jump_out_of_range + + asmcmd =db 0Fh,10h+opcode + + check @dest.size = 2 + jyes relative_word + compute @dest.imm, @dest.imm-($+4) + asmcmd =dd =@dest.=imm + exit + relative_word: + compute @dest.imm, @dest.imm-($+2) + check @dest.imm relativeto 0 + jno store_word + compute @dest.imm, @dest.imm and 0FFFFh + store_word: + asmcmd =dw =@dest.=imm + exit + + short: + compute @dest.imm, (@dest.imm-($+2)) and 0FFh + asmcmd =db opcode,=@dest.=imm + exit + + relative_jump_out_of_range: + asmcmd =db ?,? + asmcmd =err 'relative jump out of range' + exit + + invalid_operand: + asmcmd =err 'invalid operand' + exit + + end calminstruction +end iterate + +iterate , loopnz,0E0h,0, loopne,0E0h,0, loopz,0E1h,0, loope,0E1h,0, loop,0E2h,0, \ + loopnzw,0E0h,2, loopnew,0E0h,2, loopzw,0E1h,2, loopew,0E1h,2, loopw,0E2h,2, \ + loopnzd,0E0h,4, loopned,0E0h,4, loopzd,0E1h,4, looped,0E1h,4, loopd,0E2h,4, \ + loopnzq,0E0h,8, loopneq,0E0h,8, loopzq,0E1h,8, loopeq,0E1h,8, loopq,0E2h,8, \ + jcxz,0E3h,2, jecxz,0E3h,4, jrcxz,0E3h,8 + calminstruction instr? dest* + + asmcmd =x86.=parse_jump_operand =@dest,dest + + check @dest.type = 'imm' & ( @dest.jump_type = 'short' | ~ @dest.jump_type ) + jno invalid_operand + + check len shl 3 and not x86.mode + jno address_prefix_ok + check len = 8 | (len = 2 & x86.mode = 64) + jyes illegal_instruction + asmcmd =db 67h + address_prefix_ok: + check @dest.size shl 3 <> x86.mode + jno operand_prefix_ok + check @dest.size = 8 | x86.mode = 64 + jyes invalid_operand + asmcmd =db 66h + operand_prefix_ok: + + asmcmd =db opcode + + check @dest.imm-($+1) < 80h & @dest.imm-($+1) >= -80h + jyes relative_offset_ok + check (@dest.imm-($+2)) and (1 shl (@dest.size*8) - 1) < 80h | (@dest.imm-($+2)) and (1 shl (@dest.size*8) - 1) >= 1 shl (@dest.size*8) - 80h + jyes relative_offset_ok + asmcmd =db ? + asmcmd =err 'relative jump out of range' + exit + relative_offset_ok: + compute @dest.imm, (@dest.imm-($+1)) and 0FFh + asmcmd =db =@dest.=imm + exit + + illegal_instruction: + asmcmd =err 'illegal instruction' + exit + invalid_operand: + asmcmd =err 'invalid operand' + exit + + end calminstruction +end iterate + +iterate , nop,90h, int3,0CCh, into,0CEh, int1,0F1h, salc,0D6h, \ + hlt,0F4h, cmc,0F5h, clc,0F8h, stc,0F9h, cli,0FAh, sti,0FBh, cld,0FCh, std,0FDh, \ + pushf,9Ch, popf,9Dh, sahf,9Eh, lahf,9Fh, leave,0C9h, \ + insb,6Ch, outsb,6Eh, movsb,0A4h, cmpsb,0A6h, stosb,0AAh, lodsb,0ACh, scasb,0AEh, xlatb,0D7h, \ + clts,<0Fh,6>, invd,<0Fh,8>, wbinvd,<0Fh,9>, wrmsr,<0Fh,30h>, rdtsc,<0Fh,31h>, rdmsr,<0Fh,32h>, rdpmc,<0Fh,33h>, \ + sysenter,<0Fh,34h>, sysexit,<0Fh,35h>, cpuid,<0Fh,0A2h>, rsm,<0Fh,0AAh> + + define x86.instr? db opcode + + calminstruction instr? + assemble x86.instr? + end calminstruction + +end iterate + +define x86.o16? x86.store_operand_prefix 2 +define x86.o32? x86.store_operand_prefix 4 +define x86.o64? x86.store_operand_prefix 8 + +iterate , cbw,98h, cwd,99h, iretw,0CFh, \ + insw,6Dh, outsw,6Fh, movsw,0A5h, cmpsw,0A7h, stosw,0ABh, lodsw,0ADh, scasw,0AFh + + define x86.instr? db opcode + + calminstruction instr? + assemble x86.o16 + assemble x86.instr? + end calminstruction + +end iterate + +iterate , cwde,98h, cdq,99h, iretd,0CFh, \ + insd,6Dh, outsd,6Fh, stosd,0ABh, lodsd,0ADh, scasd,0AFh + + define x86.instr? db opcode + + calminstruction instr? + assemble x86.o32 + assemble x86.instr? + end calminstruction + +end iterate + +iterate , cdqe,98h, cqo,99h, iretq,0CFh, \ + movsq,0A5h, cmpsq,0A7h, stosq,0ABh, lodsq,0ADh, scasq,0AFh, \ + sysretq,<0Fh,7>, wrmsrq,<0Fh,30h>, rdmsrq,<0Fh,32h>, sysexitq,<0Fh,35h> + + define x86.instr? db opcode + + calminstruction instr? + assemble x86.o64 + assemble x86.instr? + end calminstruction + +end iterate + +iterate , swapgs,<0Fh,1,0F8h>, syscall,<0Fh,5>, sysret,<0Fh,7> + + define x86.instr? db opcode + + calminstruction instr? + check x86.mode = 64 + jyes ok + asmcmd =err 'instruction requires long mode' + exit + ok: + assemble x86.instr? + end calminstruction + +end iterate + +iterate , lock,0F0h, repnz,0F2h, repne,0F2h, rep,0F3h, repz,0F3h, repe,0F3h + + define x86.prefix? db opcode + + calminstruction prefix? instr& + assemble x86.prefix? + assemble instr + end calminstruction + +end iterate + +calminstruction int? number* + asmcmd =db 0CDh,number +end calminstruction + +calminstruction iret? + check x86.mode < 64 + jyes prefix_ok + asmcmd =db 48h + prefix_ok: + asmcmd =db 0CFh +end calminstruction + +iterate , daa,27h, das,2Fh, aaa,37h, aas,3Fh, pusha,60h, popa,61h + + define x86.instr? db opcode + + calminstruction instr? + check x86.mode < 64 + jyes allowed + asmcmd =err 'illegal instruction' + exit + allowed: + assemble x86.instr? + end calminstruction + +end iterate + +calminstruction aam? number:10 + check x86.mode < 64 + jyes allowed + asmcmd =err 'illegal instruction' + exit + allowed: + asmcmd =db 0D4h,number +end calminstruction + +calminstruction aad? number:10 + check x86.mode < 64 + jyes allowed + asmcmd =err 'illegal instruction' + exit + allowed: + asmcmd =db 0D5h,number +end calminstruction + +iterate , pushaw,60h, popaw,61h, pushfw,9Ch, popfw,9Dh + + define x86.instr? db opcode + + calminstruction instr? + check x86.mode < 64 + jyes allowed + asmcmd =err 'illegal instruction' + exit + allowed: + assemble x86.o16 + assemble x86.instr? + end calminstruction + +end iterate + +iterate , pushad,60h, popad,61h, pushfd,9Ch, popfd,9Dh + + define x86.instr? db opcode + + calminstruction instr? + check x86.mode < 64 + jyes allowed + asmcmd =err 'illegal instruction' + exit + allowed: + assemble x86.o32 + assemble x86.instr? + end calminstruction + +end iterate + +iterate , pushfq,9Ch, popfq,9Dh + + define x86.instr? db opcode + + calminstruction instr? + check x86.mode = 64 + jyes allowed + asmcmd =err 'instruction requires long mode' + exit + allowed: + assemble x86.instr? + end calminstruction + +end iterate + +calminstruction movs? dest*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + local size + check @dest.size = 0 & @src.size = 0 + jyes operand_size_not_specified + check @dest.size <> 0 & @src.size <> 0 & @dest.size <> @src.size + jyes operand_sizes_do_not_match + compute size, @dest.size or @src.size + size_ok: + check @src.type = 'mem' & @src.mod = 0 & @dest.type = 'mem' & @dest.mod = 0 & ( (x86.mode < 64 & @src.mode = 16 & @src.rm = 4 & @dest.mode = 16 & @dest.rm = 5) | (@src.mode > 16 & @src.rm = 6 & @dest.mode = @src.mode & @dest.rm = 7) ) & ( @dest.segment_prefix = 0 | @dest.segment_prefix = 26h | (x86.mode = 64 & @dest.segment_prefix < 64h)) + jno invalid_combination_of_operands + check @src.segment_prefix = 0 | @src.segment_prefix = 3Eh | (x86.mode = 64 & @src.segment_prefix < 64h) + jyes segment_prefix_ok + asmcmd =db =@src.=segment_prefix + segment_prefix_ok: + check @dest.mode = x86.mode + jyes address_prefix_ok + asmcmd =db 67h + address_prefix_ok: + check size > 1 + jyes movs_word + assemble x86.movsb + exit + movs_word: + asmcmd =x86.=store_operand_prefix size + assemble x86.movsw + exit + operand_size_not_specified: + asmcmd =err 'operand size not specified' + compute size, 0 + jump size_ok + operand_sizes_do_not_match: + asmcmd =err 'operand sizes do not match' + compute size, 0 + jump size_ok + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' + exit +end calminstruction + +calminstruction cmps? src*,dest* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + local size + check @dest.size = 0 & @src.size = 0 + jyes operand_size_not_specified + check @dest.size <> 0 & @src.size <> 0 & @dest.size <> @src.size + jyes operand_sizes_do_not_match + compute size, @dest.size or @src.size + size_ok: + check @src.type = 'mem' & @src.mod = 0 & @dest.type = 'mem' & @dest.mod = 0 & ( (x86.mode < 64 & @src.mode = 16 & @src.rm = 4 & @dest.mode = 16 & @dest.rm = 5) | (@src.mode > 16 & @src.rm = 6 & @dest.mode = @src.mode & @dest.rm = 7) ) & ( @dest.segment_prefix = 0 | @dest.segment_prefix = 26h | (x86.mode = 64 & @dest.segment_prefix < 64h)) + jno invalid_combination_of_operands + check @src.segment_prefix = 0 | @src.segment_prefix = 3Eh | (x86.mode = 64 & @src.segment_prefix < 64h) + jyes segment_prefix_ok + asmcmd =db =@src.=segment_prefix + segment_prefix_ok: + check @dest.mode = x86.mode + jyes address_prefix_ok + asmcmd =db 67h + address_prefix_ok: + check size > 1 + jyes cmps_word + assemble x86.cmpsb + exit + cmps_word: + asmcmd =x86.=store_operand_prefix size + assemble x86.cmpsw + exit + operand_size_not_specified: + asmcmd =err 'operand size not specified' + compute size, 0 + jump size_ok + operand_sizes_do_not_match: + asmcmd =err 'operand sizes do not match' + compute size, 0 + jump size_ok + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' + exit +end calminstruction + +calminstruction stos? dest* + asmcmd =x86.=parse_operand =@dest,dest + check @dest.size + jyes size_ok + asmcmd =err 'operand size not specified' + size_ok: + check @dest.type = 'mem' & @dest.mod = 0 & ( (x86.mode < 64 & @dest.mode = 16 & @dest.rm = 5) | (@dest.mode > 16 & @dest.rm = 7) ) & ( @dest.segment_prefix = 0 | @dest.segment_prefix = 26h | (x86.mode = 64 & @dest.segment_prefix < 64h)) + jno invalid_operand + check @dest.mode = x86.mode + jyes address_prefix_ok + asmcmd =db 67h + address_prefix_ok: + check @dest.size > 1 + jyes stos_word + assemble x86.stosb + exit + stos_word: + asmcmd =x86.=store_operand_prefix =@dest.=size + assemble x86.stosw + exit + invalid_operand: + asmcmd =err 'invalid operand' + exit +end calminstruction + +calminstruction lods? src* + asmcmd =x86.=parse_operand =@src,src + check @src.size + jyes size_ok + asmcmd =err 'operand size not specified' + size_ok: + check @src.type = 'mem' & @src.mod = 0 & ( (x86.mode < 64 & @src.mode = 16 & @src.rm = 4) | (@src.mode > 16 & @src.rm = 6) ) + jno invalid_operand + check @src.segment_prefix = 0 | @src.segment_prefix = 3Eh | (x86.mode = 64 & @src.segment_prefix < 64h) + jyes segment_prefix_ok + asmcmd =db =@src.=segment_prefix + segment_prefix_ok: + check @src.mode = x86.mode + jyes address_prefix_ok + asmcmd =db 67h + address_prefix_ok: + check @src.size > 1 + jyes lods_word + assemble x86.lodsb + exit + lods_word: + asmcmd =x86.=store_operand_prefix =@src.=size + assemble x86.lodsw + exit + invalid_operand: + asmcmd =err 'invalid operand' + exit +end calminstruction + +calminstruction scas? dest* + asmcmd =x86.=parse_operand =@dest,dest + check @dest.size + jyes size_ok + asmcmd =err 'operand size not specified' + size_ok: + check @dest.type = 'mem' & @dest.mod = 0 & ( (x86.mode < 64 & @dest.mode = 16 & @dest.rm = 5) | (@dest.mode > 16 & @dest.rm = 7) ) & ( @dest.segment_prefix = 0 | @dest.segment_prefix = 26h | (x86.mode = 64 & @dest.segment_prefix < 64h)) + jno invalid_operand + check @dest.mode = x86.mode + jyes address_prefix_ok + asmcmd =db 67h + address_prefix_ok: + check @dest.size > 1 + jyes scas_word + assemble x86.scasb + exit + scas_word: + asmcmd =x86.=store_operand_prefix =@dest.=size + assemble x86.scasw + exit + invalid_operand: + asmcmd =err 'invalid operand' + exit +end calminstruction + +calminstruction ins? dest*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + check @dest.size + jyes size_ok + asmcmd =err 'operand size not specified' + compute size, 0 + size_ok: + check @src.type = 'reg' & @src.size = 2 & @src.rm = 2 & @dest.type = 'mem' & @dest.mod = 0 & ( (x86.mode < 64 & @dest.mode = 16 & @dest.rm = 5) | (@dest.mode > 16 & @dest.rm = 7) ) & ( @dest.segment_prefix = 0 | @dest.segment_prefix = 26h | (x86.mode = 64 & @dest.segment_prefix < 64h)) + jno invalid_combination_of_operands + check @dest.mode = x86.mode + jyes address_prefix_ok + asmcmd =db 67h + address_prefix_ok: + check @dest.size > 1 + jyes ins_word + assemble x86.insb + exit + ins_word: + asmcmd =x86.=store_operand_prefix =@dest.=size + assemble x86.insw + exit + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' + exit +end calminstruction + +calminstruction outs? dest*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + check @src.size + jyes size_ok + asmcmd =err 'operand size not specified' + size_ok: + check @dest.type = 'reg' & @dest.size = 2 & @dest.rm = 2 & @src.type = 'mem' & @src.mod = 0 & ( (x86.mode < 64 & @src.mode = 16 & @src.rm = 4) | (@src.mode > 16 & @src.rm = 6) ) + jno invalid_combination_of_operands + check @src.segment_prefix = 0 | @src.segment_prefix = 3Eh | (x86.mode = 64 & @src.segment_prefix < 64h) + jyes segment_prefix_ok + asmcmd =db =@src.=segment_prefix + segment_prefix_ok: + check @src.mode = x86.mode + jyes address_prefix_ok + asmcmd =db 67h + address_prefix_ok: + check @src.size > 1 + jyes outs_word + assemble x86.outsb + exit + outs_word: + asmcmd =x86.=store_operand_prefix =@src.=size + assemble x86.outsw + exit + operand_size_not_specified: + asmcmd =err 'operand size not specified' + compute size, 0 + jump size_ok + operand_sizes_do_not_match: + asmcmd =err 'operand sizes do not match' + compute size, 0 + jump size_ok + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' + exit +end calminstruction + +calminstruction xlat? src* + asmcmd =x86.=parse_operand =@src,src + check @src.size > 1 + jno size_ok + asmcmd =err 'invalid operand size' + size_ok: + check @src.type = 'mem' & @src.mod = 0 & ( (x86.mode < 64 & @src.mode = 16 & @src.rm = 7) | (@src.mode > 16 & @src.rm = 3) ) + jno invalid_operand + check @src.segment_prefix = 0 | @src.segment_prefix = 3Eh | (x86.mode = 64 & @src.segment_prefix < 64h) + jyes segment_prefix_ok + asmcmd =db =@src.=segment_prefix + segment_prefix_ok: + check @src.mode = x86.mode + jyes address_prefix_ok + asmcmd =db 67h + address_prefix_ok: + assemble x86.xlatb + exit + invalid_operand: + asmcmd =err 'invalid operand' + exit +end calminstruction + +calminstruction in? dest*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + check @dest.size = 8 + jyes invalid_size + check @dest.size + jyes size_ok + asmcmd =err 'operand size not specified' + jump size_ok + invalid_size: + asmcmd =err 'invalid_operand_size' + size_ok: + check @src.type = 'reg' & @src.size = 2 & @src.rm = 2 & @dest.type = 'reg' & @dest.rm = 0 + jyes in_ax_dx + check @src.type = 'imm' & @dest.type = 'reg' & @dest.rm = 0 + jyes in_ax_imm + asmcmd =err 'invalid combination of operands' + exit + in_ax_dx: + check @dest.size > 1 + jno in_al_dx + asmcmd =x86.=store_operand_prefix =@dest.=size + asmcmd =db 0EDh + exit + in_al_dx: + asmcmd =db 0ECh + exit + in_ax_imm: + check @dest.size > 1 + jno in_al_imm + asmcmd =x86.=store_operand_prefix =@dest.=size + asmcmd =db 0E5h,=@src.=imm + exit + in_al_imm: + asmcmd =db 0E4h,=@src.=imm + exit +end calminstruction + +calminstruction out? dest*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + check @src.size = 8 + jyes invalid_size + check @src.size + jyes size_ok + asmcmd =err 'operand size not specified' + jump size_ok + invalid_size: + asmcmd =err 'invalid_operand_size' + size_ok: + check @dest.type = 'reg' & @dest.size = 2 & @dest.rm = 2 & @src.type = 'reg' & @src.rm = 0 + jyes out_dx_ax + check @dest.type = 'imm' & @src.type = 'reg' & @src.rm = 0 + jyes out_imm_ax + asmcmd =err 'invalid combination of operands' + exit + out_dx_ax: + check @src.size > 1 + jno out_dx_al + asmcmd =x86.=store_operand_prefix =@src.=size + asmcmd =db 0EFh + exit + out_dx_al: + asmcmd =db 0EEh + exit + out_imm_ax: + check @src.size > 1 + jno out_imm_al + asmcmd =x86.=store_operand_prefix =@src.=size + asmcmd =db 0E7h,=@dest.=imm + exit + out_imm_al: + asmcmd =db 0E6h,=@dest.=imm + exit +end calminstruction + +calminstruction enter? alloc*,nesting* + asmcmd =x86.=parse_operand =@src,alloc + asmcmd =x86.=parse_operand =@aux,nesting + check (@src.size and not 2) | (@aux.size and not 1) + jno size_ok + asmcmd =err 'invalid operand size' + size_ok: + check @src.type = 'imm' & @aux.type = 'imm' + jyes operand_ok + asmcmd =err 'invalid operand' + exit + operand_ok: + asmcmd =db 0C8h + asmcmd =dw =@src.=imm + asmcmd =db =@aux.=imm +end calminstruction + +calminstruction bound? dest*,src* + check x86.mode < 64 + jyes allowed + asmcmd =err 'illegal instruction' + exit + allowed: + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + check @src.type = 'mem' & @dest.type = 'reg' + jno invalid_combination_of_operands + check @src.size and not @dest.size + jno size_ok + asmcmd =err 'operand sizes do not match' + size_ok: + asmcmd =x86.=select_operand_prefix =@src,=@dest.=size + asmcmd =x86.=store_instruction 62h,=@src,=@dest.=rm + exit + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' + exit +end calminstruction + +calminstruction arpl? dest*,src* + check x86.mode < 64 + jyes allowed + asmcmd =err 'illegal instruction' + exit + allowed: + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + check @src.type = 'reg' & (@dest.type = 'mem' | @dest.type = 'reg') + jno invalid_combination_of_operands + check @src.size = 2 + jno invalid_operand_size + check @dest.size and not @src.size + jno size_ok + asmcmd =err 'operand sizes do not match' + jump size_ok + invalid_operand_size: + asmcmd =err 'invalid operand size' + size_ok: + asmcmd =x86.=store_instruction 63h,=@dest,=@src.=rm + exit + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' + exit + exit +end calminstruction + +iterate , lldt,0,2, ltr,0,3, verr,0,4, verw,0,5, lmsw,1,6 + + calminstruction instr? dest* + asmcmd =x86.=parse_operand =@dest,dest + check @dest.size and not 2 + jno size_ok + asmcmd =err 'invalid operand size' + size_ok: + check @dest.type = 'reg' | @dest.type = 'mem' + jyes operand_ok + asmcmd =err 'invalid operand' + exit + operand_ok: + asmcmd =x86.=store_instruction <0Fh,ext>,=@dest,postbyte + end calminstruction + +end iterate + +iterate , sldt,0,0, str,0,1, smsw,1,4 + + calminstruction instr? dest* + asmcmd =x86.=parse_operand =@dest,dest + check @dest.type = 'reg' + jyes select_operand_prefix + check @dest.size and not 2 + jno size_ok + asmcmd =err 'invalid operand size' + size_ok: + check @dest.type = 'mem' + jyes store_instruction + asmcmd =err 'invalid combination of operands' + exit + select_operand_prefix: + asmcmd =x86.=select_operand_prefix =@dest,=@dest.=size + store_instruction: + asmcmd =x86.=store_instruction <0Fh,ext>,=@dest,postbyte + end calminstruction + +end iterate + +iterate , lgdt,2, lidt,3, sgdt,0, sidt,1 + + calminstruction instr? dest* + asmcmd =x86.=parse_operand =@dest,dest + check @dest.type = 'mem' + jyes operand_ok + asmcmd =err 'invalid operand' + exit + operand_ok: + check x86.mode = 64 & @dest.size = 10 + jyes store_instruction + check x86.mode < 64 & @dest.size = 6 + jyes o32 + check x86.mode < 64 & @dest.size = 5 + jyes o16 + check @dest.size + jno store_instruction + asmcmd =err 'invalid operand size' + jump store_instruction + o16: + asmcmd =x86.=select_operand_prefix =@dest,2 + jump store_instruction + o32: + asmcmd =x86.=select_operand_prefix =@dest,4 + store_instruction: + asmcmd =x86.=store_instruction <0Fh,1>,=@dest,postbyte + end calminstruction + +end iterate + +iterate , lar,2, lsl,3 + + calminstruction instr? dest*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + check @dest.type = 'reg' & (@src.type = 'mem' | @src.type = 'reg') + jno invalid_combination_of_operands + check @src.size and not 2 + jno size_ok + asmcmd =err 'invalid operand size' + size_ok: + asmcmd =x86.=select_operand_prefix =@src,=@dest.=size + asmcmd =x86.=store_instruction <0Fh,ext>,=@src,=@dest.=rm + exit + invalid_combination_of_operands: + asmcmd =err 'invalid combination of operands' + end calminstruction + +end iterate + +iterate , cmpxchg,0B0h, xadd,0C0h + + calminstruction instr? dest*,src* + asmcmd =x86.=parse_operand =@dest,dest + asmcmd =x86.=parse_operand =@src,src + check @src.type = 'reg' & ( @dest.type = 'reg' | @dest.type = 'mem' ) + jyes xadd_rm_reg + asmcmd =err 'invalid combination of operands' + exit + xadd_rm_reg: + check @dest.size and not @src.size + jno size_ok + asmcmd =err 'operand sizes do not match' + size_ok: + check @src.size > 1 + jno xadd_rm_reg_8bit + asmcmd =x86.=select_operand_prefix =@dest,=@src.=size + asmcmd =x86.=store_instruction <0Fh,ext+1>,=@dest,=@src.=rm + exit + xadd_rm_reg_8bit: + asmcmd =x86.=store_instruction <0Fh,ext>,=@dest,=@src.=rm + end calminstruction + +end iterate + +calminstruction cmpxchg8b? dest* + asmcmd =x86.=parse_operand =@dest,dest + check @dest.type = 'mem' + jyes operand_ok + asmcmd =err 'invalid operand' + exit + operand_ok: + check @dest.size and not 8 + jno size_ok + asmcmd =err 'invalid operand size' + size_ok: + asmcmd =x86.=store_instruction <0Fh,0C7h>,=@dest,1 +end calminstruction + +calminstruction cmpxchg16b? dest* + asmcmd =x86.=parse_operand =@dest,dest + check @dest.type = 'mem' + jyes operand_ok + asmcmd =err 'invalid operand' + exit + operand_ok: + check @dest.size and not 16 + jno size_ok + asmcmd =err 'invalid operand size' + size_ok: + assemble x86.o64 + asmcmd =x86.=store_instruction <0Fh,0C7h>,=@dest,1 +end calminstruction + +calminstruction bswap? dest* + asmcmd =x86.=parse_operand =@dest,dest + check @dest.type = 'reg' & @dest.size > 2 + jyes operand_ok + asmcmd =err 'invalid operand' + exit + operand_ok: + asmcmd =x86.=store_operand_prefix =@dest.=size,=@dest.=rm + local opcode + compute opcode, 0C8h + @dest.rm and 111b + asmcmd =db 0Fh,opcode +end calminstruction + +calminstruction invlpg? dest* + asmcmd =x86.=parse_operand =@dest,dest + check @dest.type = 'mem' + jyes operand_ok + asmcmd =err 'invalid operand' + exit + operand_ok: + asmcmd =x86.=store_instruction <0Fh,1>,=@dest,7 +end calminstruction + +include '80387.inc' + +iterate , fcmovb,<0DAh,0C0h>, fcmove,<0DAh,0C8h>, fcmovbe,<0DAh,0D0h>, fcmovu,<0DAh,0D8h>, \ + fcmovnb,<0DBh,0C0h>, fcmovne,<0DBh,0C8h>, fcmovnbe,<0DBh,0D0h>, fcmovnu,<0DBh,0D8h> + + calminstruction instr? dest*,src* + asmcmd =x87.=parse_operand =@dest,dest + asmcmd =x87.=parse_operand =@src,src + check @dest.type = 'streg' & @dest.rm = 0 & @src.type = 'streg' + jyes ok + asmcmd =err 'invalid operand' + exit + ok: + asmcmd =db opcode + =@src.=rm + end calminstruction + +end iterate + +iterate , fucomi,0DBh,5, fucomip,0DFh,5, fcomi,0DBh,6, fcomip,0DFh,6 + + calminstruction instr? src:st1 + asmcmd =x87.=parse_operand =@src,src + check @src.type = 'streg' + jyes ok + asmcmd =err 'invalid operand' + exit + ok: + local modrm + compute modrm, 11b shl 6 + postbyte shl 3 + @src.rm + asmcmd =db opcode, modrm + end calminstruction + +end iterate + +include 'ext/sse2.inc' + +repeat 8, i:8 + element xmm#i? : SSE.reg + i +end repeat + +iterate , fxsave64,0, fxrstor64,1 + + calminstruction instr? src* + check x86.mode = 64 + jyes allowed + asmcmd =err 'instruction requires long mode' + exit + allowed: + asmcmd =x86.=parse_operand =@src,src + check @src.type = 'mem' + jno invalid_operand + check @src.size and not 512 + jno size_ok + asmcmd =err 'invalid operand size' + size_ok: + asmcmd =x86.=select_operand_prefix =@src,8 + asmcmd =x86.=store_instruction <0Fh,0AEh>,=@src,postbyte + exit + invalid_operand: + asmcmd =err 'invalid operand' + end calminstruction + +end iterate diff --git a/x86_64_sse2_x87/fasm/examples/x86/life.asm b/x86_64_sse2_x87/fasm/examples/x86/life.asm new file mode 100644 index 0000000..d6dbb9a --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/life.asm @@ -0,0 +1,209 @@ + +; Life - fasm example program + +; Controls: +; arrow keys - move cursor +; Space - switch cell +; Enter - move to next generation +; Esc - exit program + + include '80186.inc' + + org 100h + jumps + + mov di,screen_data + xor al,al + mov cx,80*50*2 + rep stosb + + mov ax,3 + int 10h ; set text mode + mov ah,1 + mov ch,20h + int 10h ; hide cursor + mov ax,1003h + xor bx,bx + int 10h ; enable background intensity + + mov ax,1100h + mov bp,DCh_pattern + mov cx,1 + mov dx,0DCh + mov bx,1000h + int 10h + + mov ax,0B800h + mov es,ax + xor di,di + mov ax,0DCh + mov cx,80*25 + rep stosw + + redraw_screen: + mov si,[cursor_y] + imul si,80 + add si,[cursor_x] + and byte [screen_data+si],8 + or byte [screen_data+si],2 + + mov si,screen_data + xor di,di + mov cx,50 + draw_screen: + push cx + mov cx,80 + draw_line: + mov ah,[si+80] + lodsb + shl al,4 + and ah,0Fh + or al,ah + inc di + stosb + loop draw_line + pop cx + add si,80 + loop draw_screen + + wait_for_key: + xor ah,ah + int 16h + cmp ah,1 + je exit + cmp ah,1Ch + je next_generation + cmp ah,39h + je switch_cell + cmp ah,4Bh + je cursor_left + cmp ah,4Dh + je cursor_right + cmp ah,48h + je cursor_up + cmp ah,50h + je cursor_down + jmp wait_for_key + + switch_cell: + mov si,[cursor_y] + imul si,80 + add si,[cursor_x] + xor byte [screen_data+si],8 + jmp redraw_screen + + cursor_left: + cmp [cursor_x],1 + jbe wait_for_key + call clear_cursor + dec [cursor_x] + jmp redraw_screen + cursor_right: + cmp [cursor_x],78 + jae wait_for_key + call clear_cursor + inc [cursor_x] + jmp redraw_screen + cursor_up: + cmp [cursor_y],1 + jbe wait_for_key + call clear_cursor + dec [cursor_y] + jmp redraw_screen + cursor_down: + cmp [cursor_y],48 + jae wait_for_key + call clear_cursor + inc [cursor_y] + jmp redraw_screen + + next_generation: + call clear_cursor + mov si,screen_data+81 + mov di,screen_data+80*50+81 + mov cx,48 + process_screen: + push cx + mov cx,78 + process_line: + xor bl,bl + mov al,[si+1] + and al,1 + add bl,al + mov al,[si-1] + and al,1 + add bl,al + mov al,[si+80] + and al,1 + add bl,al + mov al,[si-80] + and al,1 + add bl,al + mov al,[si+80+1] + and al,1 + add bl,al + mov al,[si+80-1] + and al,1 + add bl,al + mov al,[si-80+1] + and al,1 + add bl,al + mov al,[si-80-1] + and al,1 + add bl,al + mov al,byte [si] + mov byte [di],al + cmp bl,1 + jbe clear_cell + cmp bl,4 + jae clear_cell + cmp bl,2 + je cell_ok + mov byte [di],0Fh + jmp cell_ok + clear_cell: + mov byte [di],0 + cell_ok: + inc si + inc di + loop process_line + pop cx + add si,2 + add di,2 + loop process_screen + push es + push ds + pop es + mov si,screen_data+80*50 + mov di,screen_data + mov cx,80*50 + rep movsb + pop es + jmp redraw_screen + + exit: + mov ax,3 + int 10h + int 20h + + clear_cursor: + mov si,[cursor_y] + imul si,80 + add si,[cursor_x] + mov al,byte [screen_data+si] + cmp al,2 + je empty_cell + mov byte [screen_data+si],0Fh + ret + empty_cell: + mov byte [screen_data+si],0 + ret + +cursor_x dw 40 +cursor_y dw 25 + +DCh_pattern: + db 8 dup 0 + db 8 dup 0FFh + +screen_data rb 80*50*2 diff --git a/x86_64_sse2_x87/fasm/examples/x86/make.cmd b/x86_64_sse2_x87/fasm/examples/x86/make.cmd new file mode 100644 index 0000000..b26594d --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/make.cmd @@ -0,0 +1,9 @@ +set include=include +fasmg hello.asm hello.com +fasmg life.asm life.com +fasmg mandel.asm mandel.com +fasmg multiseg.asm multiseg.exe +fasmg usedpmi.asm usedpmi.exe +fasmg win32.asm win32.exe +fasmg win64.asm win64.exe +fasmg win64avx.asm win64avx.exe diff --git a/x86_64_sse2_x87/fasm/examples/x86/mandel.asm b/x86_64_sse2_x87/fasm/examples/x86/mandel.asm new file mode 100644 index 0000000..c3a6e1c --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/mandel.asm @@ -0,0 +1,94 @@ + +; Mandelbrot Set - fasm example program + +include '80186.inc' +include '8087.inc' + + org 100h + + mov ax,13h ; MCGA/VGA + int 10h + push 0A000h + pop es + + mov dx,3C8h + xor al,al + out dx,al + inc dl + mov cx,64 + vga_palette: + out dx,al + out dx,al + out dx,al + inc al + loop vga_palette + + xor di,di + xor dx,dx + finit + fld [y_top] + fstp [y] +screen: + xor bx,bx + fld [x_left] + fstp [x] + row: + finit + fldz + fldz + mov cx,63 + iteration: + fld st0 + fmul st0,st0 + fxch st1 + fmul st0,st2 + fadd st0,st0 + fxch st2 + fmul st0,st0 + fsubp st1,st0 + fxch st1 + fadd [y] + fxch st1 + fadd [x] + fld st1 + fmul st0,st0 + fld st1 + fmul st0,st0 + faddp st1,st0 + fsqrt + fistp [i] + cmp [i],2 + ja over + loop iteration + over: + mov al,cl + stosb + fld [x] + fadd [x_step] + fstp [x] + inc bx + cmp bx,320 + jb row + fld [y] + fsub [y_step] + fstp [y] + inc dx + cmp dx,200 + jb screen + + xor ah,ah + int 16h + mov ax,3 + int 10h + int 20h + +x_left dd -2.2 +y_top dd 1.25 + +x_step dd 0.009375 +y_step dd 0.0125 + +x dd ? +y dd ? + +i dw ? diff --git a/x86_64_sse2_x87/fasm/examples/x86/multiseg.asm b/x86_64_sse2_x87/fasm/examples/x86/multiseg.asm new file mode 100644 index 0000000..be9030d --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/multiseg.asm @@ -0,0 +1,29 @@ + +include '8086.inc' +include 'format/mz.inc' + +entry code:start ; program entry point +stack 100h ; stack size + +segment code + + start: + mov ax,data + mov ds,ax + + mov dx,hello + call extra:write_text + + mov ax,4C00h + int 21h + +segment data + + hello db 'Hello world!',24h + +segment extra + + write_text: + mov ah,9 + int 21h + retf diff --git a/x86_64_sse2_x87/fasm/examples/x86/usedpmi.asm b/x86_64_sse2_x87/fasm/examples/x86/usedpmi.asm new file mode 100644 index 0000000..66d12a0 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/usedpmi.asm @@ -0,0 +1,99 @@ + +; This is an example of setting up 32-bit program with flat memory model +; using DPMI. It requires 32-bit DPMI host in order to start. + +include '80386.inc' +include 'format/mz.inc' + +heap 0 ; no additional memory + +segment loader use16 + + push cs + pop ds + + mov ax,1687h + int 2Fh + or ax,ax ; DPMI installed? + jnz error + test bl,1 ; 32-bit programs supported? + jz error + mov word [mode_switch],di + mov word [mode_switch+2],es + mov bx,si ; allocate memory for DPMI data + mov ah,48h + int 21h + jc error + mov es,ax + mov ax,1 + call far [mode_switch] ; switch to protected mode + jc error + + mov cx,1 + xor ax,ax + int 31h ; allocate descriptor for code + mov si,ax + xor ax,ax + int 31h ; allocate descriptor for data + mov di,ax + mov dx,cs + lar cx,dx + shr cx,8 + or cx,0C000h + mov bx,si + mov ax,9 + int 31h ; set code descriptor access rights + mov dx,ds + lar cx,dx + shr cx,8 + or cx,0C000h + mov bx,di + int 31h ; set data descriptor access rights + mov ecx,main + shl ecx,4 + mov dx,cx + shr ecx,16 + mov ax,7 ; set descriptor base address + int 31h + mov bx,si + int 31h + mov cx,0FFFFh + mov dx,0FFFFh + mov ax,8 ; set segment limit to 4 GB + int 31h + mov bx,di + int 31h + + mov ds,di + mov es,di + mov fs,di + mov gs,di + push 0 + push si + push dword start + retfd + + error: + mov ax,4CFFh + int 21h + + mode_switch dd ? + +segment main use32 + + start: + mov esi,hello + .loop: + lodsb + or al,al + jz .done + mov dl,al + mov ah,2 + int 21h + jmp .loop + .done: + + mov ax,4C00h + int 21h + + hello db 'Hello from protected mode!',0Dh,0Ah,0 diff --git a/x86_64_sse2_x87/fasm/examples/x86/win32.asm b/x86_64_sse2_x87/fasm/examples/x86/win32.asm new file mode 100644 index 0000000..4168c97 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/win32.asm @@ -0,0 +1,46 @@ + +include 'format/format.inc' + +format PE GUI +entry start + +section '.text' code readable executable + + start: + + push 0 + push _caption + push _message + push 0 + call [MessageBoxA] + + push 0 + call [ExitProcess] + +section '.data' data readable writeable + + _caption db 'Win32 assembly program',0 + _message db 'Hello World!',0 + +section '.idata' import data readable writeable + + dd 0,0,0,RVA kernel_name,RVA kernel_table + dd 0,0,0,RVA user_name,RVA user_table + dd 0,0,0,0,0 + + kernel_table: + ExitProcess dd RVA _ExitProcess + dd 0 + user_table: + MessageBoxA dd RVA _MessageBoxA + dd 0 + + kernel_name db 'KERNEL32.DLL',0 + user_name db 'USER32.DLL',0 + + _ExitProcess dw 0 + db 'ExitProcess',0 + _MessageBoxA dw 0 + db 'MessageBoxA',0 + +section '.reloc' fixups data readable discardable ; needed for Win32s diff --git a/x86_64_sse2_x87/fasm/examples/x86/win64.asm b/x86_64_sse2_x87/fasm/examples/x86/win64.asm new file mode 100644 index 0000000..940247e --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/win64.asm @@ -0,0 +1,45 @@ + +include 'format/format.inc' + +format PE64 GUI +entry start + +section '.text' code readable executable + + start: + sub rsp,8*5 ; reserve stack for API use and make stack dqword aligned + + mov r9d,0 + lea r8,[_caption] + lea rdx,[_message] + mov rcx,0 + call [MessageBoxA] + + mov ecx,eax + call [ExitProcess] + +section '.data' data readable writeable + + _caption db 'Win64 assembly program',0 + _message db 'Hello World!',0 + +section '.idata' import data readable writeable + + dd 0,0,0,RVA kernel_name,RVA kernel_table + dd 0,0,0,RVA user_name,RVA user_table + dd 0,0,0,0,0 + + kernel_table: + ExitProcess dq RVA _ExitProcess + dq 0 + user_table: + MessageBoxA dq RVA _MessageBoxA + dq 0 + + kernel_name db 'KERNEL32.DLL',0 + user_name db 'USER32.DLL',0 + + _ExitProcess dw 0 + db 'ExitProcess',0 + _MessageBoxA dw 0 + db 'MessageBoxA',0 diff --git a/x86_64_sse2_x87/fasm/examples/x86/win64avx.asm b/x86_64_sse2_x87/fasm/examples/x86/win64avx.asm new file mode 100644 index 0000000..b7d4637 --- /dev/null +++ b/x86_64_sse2_x87/fasm/examples/x86/win64avx.asm @@ -0,0 +1,114 @@ + +include 'format/format.inc' + +format PE64 NX GUI 5.0 +entry start + +include 'ext/avx.inc' + +section '.data' data readable writeable + + _title db 'AVX playground',0 + _error db 'AVX instructions are not supported.',0 + + x dq 3.14159265389 + + vector_output: + repeat 16, i:0 + db 'ymm',`i,': %f,%f,%f,%f',13,10 + end repeat + db 0 + + buffer db 1000h dup ? + +section '.text' code readable executable + + start: + + mov eax,1 + cpuid + and ecx,18000000h + cmp ecx,18000000h + jne no_AVX + xor ecx,ecx + xgetbv + and eax,110b + cmp eax,110b + jne no_AVX + + vbroadcastsd ymm0, [x] + vsqrtpd ymm1, ymm0 + + vsubpd ymm2, ymm0, ymm1 + vsubpd ymm3, ymm1, ymm2 + + vaddpd xmm4, xmm2, xmm3 + vaddpd ymm5, ymm4, ymm0 + + vperm2f128 ymm6, ymm4, ymm5, 03h + vshufpd ymm7, ymm6, ymm5, 10010011b + + vroundpd ymm8, ymm7, 0011b + vroundpd ymm9, ymm7, 0 + + sub rsp,418h + + repeat 16, i:0 + vmovups [rsp+10h+i*32],ymm#i + end repeat + + mov r8,[rsp+10h] + mov r9,[rsp+18h] + lea rdx,[vector_output] + lea rcx,[buffer] + call [sprintf] + + xor ecx,ecx + lea rdx,[buffer] + lea r8,[_title] + xor r9d,r9d + call [MessageBoxA] + + xor ecx,ecx + call [ExitProcess] + + no_AVX: + + sub rsp,28h + + xor ecx,ecx + lea rdx,[_error] + lea r8,[_title] + mov r9d,10h + call [MessageBoxA] + + mov ecx,1 + call [ExitProcess] + +section '.idata' import data readable writeable + + dd 0,0,0,RVA kernel_name,RVA kernel_table + dd 0,0,0,RVA user_name,RVA user_table + dd 0,0,0,RVA msvcrt_name,RVA msvcrt_table + dd 0,0,0,0,0 + + kernel_table: + ExitProcess dq RVA _ExitProcess + dq 0 + user_table: + MessageBoxA dq RVA _MessageBoxA + dq 0 + msvcrt_table: + sprintf dq RVA _sprintf + dq 0 + + kernel_name db 'KERNEL32.DLL',0 + user_name db 'USER32.DLL',0 + msvcrt_name db 'MSVCRT.DLL',0 + + _ExitProcess dw 0 + db 'ExitProcess',0 + _MessageBoxA dw 0 + db 'MessageBoxA',0 + _sprintf dw 0 + db 'sprintf',0 diff --git a/x86_64_sse2_x87/fasm/fasmg b/x86_64_sse2_x87/fasm/fasmg new file mode 100644 index 0000000000000000000000000000000000000000..55be13d4c5ed0c94ee161f785e27ee6ff92fc9df GIT binary patch literal 63641 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%0Db0KrH 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 INId)fA0|xZ0{{R3 literal 0 HcwPel00001 diff --git a/x86_64_sse2_x87/fasm/fasmg.exe b/x86_64_sse2_x87/fasm/fasmg.exe new file mode 100644 index 0000000000000000000000000000000000000000..68d98fcbcc710fe3b1308aeede4bf4ccc4394248 GIT binary patch literal 64512 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
AUE-_GRGR%|npD@6$cJ@4@x% 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 WF!S&Qe8MWXV~{?Q63L72gEqrj8$+m^!5iXu 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
q5oW+aYh#<#$U&i-2*!UA8=>!FWDr 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

O?AQRj?$SDw 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 !4S>Vng5I=6cg6yIKSM3g=bPgxYApFD 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 z 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/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 literal 0 HcwPel00001 diff --git a/x86_64_sse2_x87/fasm/license.txt b/x86_64_sse2_x87/fasm/license.txt new file mode 100644 index 0000000..fd847f9 --- /dev/null +++ b/x86_64_sse2_x87/fasm/license.txt @@ -0,0 +1,24 @@ +flat assembler g +Copyright (c) 2015-2020, Tomasz Grysztar +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/x86_64_sse2_x87/fasm/readme.txt b/x86_64_sse2_x87/fasm/readme.txt new file mode 100644 index 0000000..4d43e58 --- /dev/null +++ b/x86_64_sse2_x87/fasm/readme.txt @@ -0,0 +1,20 @@ + +The name of flat assembler g (abbreviated to fasmg) is intentionally stylized +with lowercase letters. This is a nod to the history of its precedessor. + +The "source" directory contains the complete source code which +can be assembled with either fasm or fasmg except for MacOS version, +which can only be assembled with fasmg. + +The executable file for Windows is "fasmg.exe", while "fasmg" and "fasmg.x64" +are for Linux in 32-bit and 64-bit format respectively. The files for MacOS +are at "source/macos/fasmg" and "source/macos/x64/fasmg". + +The "source/libc/fasmg.asm" may be used to assemble fasmg as an ELF object +that can then be linked to a 32-bit C library with a third-party linker to +produce an executable native to a particular operating system. A similar +object file in Mach-O format can be assembled from "source/macos/fasmg.o.asm". + +When the source code is assembled with fasmg, it depends on the files from +"examples/x86/include" directory that implement the instruction sets and +output formats compatible with flat assembler 1. \ No newline at end of file diff --git a/x86_64_sse2_x87/fasm/source/assembler.inc b/x86_64_sse2_x87/fasm/source/assembler.inc new file mode 100644 index 0000000..453bac8 --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/assembler.inc @@ -0,0 +1,3352 @@ + +; general note: +; with the ability to run in various environments in mind, the entire assembler core +; does not use ebp register and never directly touches esp; the stack is managed +; with push and pop instructions, occasionally [esp] may be used to access the top value, +; but no other assumptions about the stack layout are made + +struct Workspace + memory_start dd ? + memory_end dd ? +ends + +struct SourceContext + number_of_entries dd ? + ; entries SourceEntry[] +ends + +struct SourceEntry + type db ? ; SOURCE_# + flags db ? ; SRCF_# + saved_result db ? + reserved db ? + name dd ? + name_length dd ? + text dd ? + offset dd ? + line_number dd ? + number_of_attached_lines dd ? + line_offset dd ? + local_namespace dd ? +ends + +struct RecognitionContext + base_namespace dd ? ; SymbolTree_Root + base_label dd ? ; SymbolTree_Foliage +ends + +struct LineEmbedding + previous_pointer dd ? + previous_end dd ? + recognition_context dd ? + definition dd ? + whitespace dd ? +ends + +struct LineExcerpt + data_start dd ? + data_end dd ? + recognition_context dd ? + leftover_context dd ? +ends + +SOURCE_FILE = 0 +SOURCE_MEMORY = 1 +SOURCE_MACRO = 2 +SOURCE_CALM = 3 + +SRCF_PREPROCESSED = 1 +SRCF_ALM_STATEMENT = 2 + +PMODE_RETAIN_COMMENTS = 1 +PMODE_ISOLATE_LINES = 2 + +AMODE_SKIP = 1 +AMODE_DEFINITION = 2 +AMODE_POSTPONED = 4 +AMODE_CALM_DEFINITION = 8 + +TRACE_ERROR_STACK = 1 +TRACE_DISPLAY = 2 + +assembly_init: +; in: +; al = any combination of TRACE_# flags + + mov [trace_mode],al + + xor eax,eax + mov edi,variables + mov ecx,(variables_end - variables) shr 2 + rep stosd + if (variables_end - variables) and 11b + mov cl,(variables_end - variables) and 11b + rep stosb + end if + mov edi,characters + prepare_characters: + stosb + inc al + jnz prepare_characters + mov esi,characters+'a' + mov edi,characters+'A' + mov ecx,'z'+1-'a' + rep movsb + mov edi,characters + mov esi,control_characters + mov cl,control_characters.count + mark_control_characters: + lodsb + mov byte [edi+eax],20h + loop mark_control_characters + mov esi,syntactical_characters + mov cl,syntactical_characters.count + mark_syntactical_characters: + lodsb + mov byte [edi+eax],0 + loop mark_syntactical_characters + + mov esi,include_variable + xor ecx,ecx + call get_environment_variable + mov ecx,eax + call malloc_fixed + mov [include_paths],eax + mov edi,eax + call get_environment_variable + + mov cl,10 + call create_string_map + mov [file_source_cache],ebx + + mov cl,12 + call create_string_map + mov [memory_source_cache],ebx + + mov cl,10 + call create_string_map + mov [file_data_cache],ebx + + mov cl,7 + call create_string_map + mov [auxiliary_output_areas],ebx + + mov ecx,sizeof.SymbolTree_Root + sizeof.SymbolTree_Node + call create_tree_element + mov [root_namespace],eax + + mov ecx,sizeof.SymbolTree_Root + sizeof.SymbolTree_Node + call create_tree_element + or [eax+SymbolTree_Root.flags],NAMESPACE_UNATTACHED + mov [alm_namespace],eax + + call create_parameter_namespace + mov [root_parameter_namespace],eax + + mov ecx,sizeof.SymbolTree_Leaf + call create_tree_element + mov [eax+SymbolTree_Leaf.class],SYMCLASS_INSTRUCTION + or [eax+SymbolTree_Leaf.flags],SYM_VARIABLE + mov [interceptor_symbol],eax + mov ecx,sizeof.SymbolTree_Leaf + call create_tree_element + mov [eax+SymbolTree_Leaf.class],SYMCLASS_STRUCTURE + or [eax+SymbolTree_Leaf.flags],SYM_VARIABLE + mov [label_interceptor_symbol],eax + + mov ecx,400 + call malloc_growable + mov [tree_stack_base],eax + add eax,ecx + mov [tree_stack_end],eax + + mov ecx,4000 + call malloc_growable + mov [source_context],eax + mov [source_context_maximum_length],ecx + + mov ecx,1000 + call malloc_growable + mov [line_embeddings],eax + mov [line_embeddings_maximum_length],ecx + + mov ecx,1000 + call malloc_growable + mov [display_buffer],eax + mov [display_buffer_length],ecx + + mov ecx,1000 + call malloc_growable + mov [macro_buffer],eax + mov [macro_buffer_length],ecx + + mov ecx,32*6*sizeof.ExpressionTerm + call malloc_fixed + mov [temporary_terms],eax + mov [free_temporary_terms],eax + + mov ecx,4*sizeof.FloatData + call malloc_fixed + mov [temporary_floats],eax + + mov ecx,400 + call malloc_growable + mov [output_areas_list],eax + mov edi,eax + add eax,ecx + mov [output_areas_list_end],eax + xor eax,eax + shr ecx,2 + rep stosd + mov [number_of_line_embeddings],eax + mov [virtual_area],eax + + mov ecx,1000 + call malloc_growable + mov [directives_stack_base],eax + add eax,ecx + mov [directives_stack_end],eax + + mov ecx,1000 + call malloc_growable + mov [counters_stack_base],eax + add eax,ecx + mov [counters_stack_end],eax + + mov ecx,400 + call malloc_growable + mov [operator_stack_base],eax + add eax,ecx + mov [operator_stack_end],eax + + mov ecx,400 + call malloc_growable + mov [condition_stack_base],eax + add eax,ecx + mov [condition_stack_end],eax + + mov ecx,400 + call malloc_growable + mov [assembly_stack_base],eax + add eax,ecx + mov [assembly_stack_end],eax + + mov edx,preprocessing_workspace + call initialize_workspace + + mov edx,assembly_workspace + call initialize_workspace + + mov edx,identifier_workspace + call initialize_workspace + + mov edx,auxiliary_workspace + call initialize_workspace + + mov edx,value_workspace + call initialize_workspace + + mov edx,expression_workspace + call initialize_workspace + + mov edx,calculation_workspace + call initialize_workspace + + mov edx,calm_code_buffer + call initialize_workspace + + mov edx,calm_literals_buffer + call initialize_workspace + + mov edx,calm_auxiliary_buffer + call initialize_workspace + + mov ecx,256*4 + call malloc_fixed + mov [operator_table],eax + mov edi,eax + mov ecx,256 + xor eax,eax + rep stosd + mov esi,separating_operators + register_operators: + lodsb + test al,al + jz operators_registered + movzx ebx,al + shl ebx,2 + add ebx,[operator_table] + mov ecx,sizeof.ValueDefinition + call create_tree_element + mov edx,eax + xchg [ebx],eax + mov [edx+ValueDefinition.previous],eax + inc [edx+ValueDefinition.reference_count] + lodsb + mov [edx+ValueDefinition.type],al + lodsb + mov [edx+ValueDefinition.flags],al + lodsb + mov [edx+ValueDefinition.attribute],al + lodsd + mov [edx+ValueDefinition.value],eax + jmp register_operators + operators_registered: + + xor eax,eax + mov [name_volatile],al + mov [name_token],eax + mov [name_kind],NAME_CASEINSENSITIVE + or [symbol_required],1 + or [symbol_expected],1 + mov eax,[root_namespace] + mov [current_context.base_namespace],eax + mov esi,symbols + register_internal_symbols: + lodsb + test al,al + jnz prepare_internal_symbol + lodsb + test al,al + jz internal_symbols_registered + and [current_context.base_namespace],0 + prepare_internal_symbol: + movzx ecx,al + + xor ebx,ebx + mov edx,FNV_OFFSET + hash_internal_symbol: + movzx eax,byte [esi+ebx] + xor dl,[characters+eax] + imul edx,FNV_PRIME + inc ebx + cmp ebx,ecx + jb hash_internal_symbol + + mov ebx,[current_context.base_namespace] + test ebx,ebx + jz register_internal_namespace + mov al,[esi+ecx] + mov [symbol_class],al + push esi + call scan_namespace + pop esi + add esi,ecx + mov ecx,sizeof.ValueDefinition + call create_tree_element + mov edx,eax + xchg [ebx+SymbolTree_Leaf.definition],eax + mov [edx+ValueDefinition.previous],eax + inc [edx+ValueDefinition.reference_count] + lodsb + lodsb + mov [edx+ValueDefinition.type],al + lodsb + mov [edx+ValueDefinition.flags],al + lodsb + mov [edx+ValueDefinition.attribute],al + lodsd + mov [edx+ValueDefinition.value],eax + jmp register_internal_symbols + register_internal_namespace: + mov [symbol_class],SYMCLASS_EXPRESSION + mov ebx,[root_namespace] + push esi + call scan_namespace + pop esi + add esi,ecx + mov ecx,sizeof.ValueDefinition + call create_tree_element + mov [ebx+SymbolTree_Leaf.definition],eax + inc [eax+ValueDefinition.reference_count] + mov [eax+ValueDefinition.type],VALTYPE_RESERVED + mov [eax+ValueDefinition.flags],VAL_INTERNAL + call get_symbol_namespace + mov [current_context.base_namespace],ebx + jmp register_internal_symbols + internal_symbols_registered: + + retn + +assembly_shutdown: + + call discard_errors + + mov ebx,[auxiliary_output_areas] + test ebx,ebx + jz auxiliary_output_areas_released + call destroy_string_map + auxiliary_output_areas_released: + + mov ebx,[value_definition_chain] + release_values_from_chain: + test ebx,ebx + jz values_released + cmp [ebx+ValueDefinition.block_length],0 + je release_next_value + mov eax,[ebx+ValueDefinition.value] + call mfree + release_next_value: + mov ebx,[ebx+ValueDefinition.interlink] + jmp release_values_from_chain + values_released: + + mov eax,[include_paths] + call mfree + + mov ebx,[file_source_cache] + test ebx,ebx + jz file_source_cache_released + mov edi,mfree + call iterate_through_map + mov ebx,[file_source_cache] + call destroy_string_map + file_source_cache_released: + + mov ebx,[file_data_cache] + test ebx,ebx + jz file_data_cache_released + mov edi,release_file_data + call iterate_through_map + mov ebx,[file_data_cache] + call destroy_string_map + file_data_cache_released: + + mov ebx,[memory_source_cache] + test ebx,ebx + jz memory_source_cache_released + mov edi,mfree + call iterate_through_map + mov ebx,[memory_source_cache] + call destroy_string_map + memory_source_cache_released: + + mov eax,[tree_stack_base] + call mfree + mov eax,[source_context] + call mfree + mov eax,[line_embeddings] + call mfree + mov eax,[output_areas_list] + call mfree + mov eax,[directives_stack_base] + call mfree + mov eax,[counters_stack_base] + call mfree + mov eax,[operator_stack_base] + call mfree + mov eax,[condition_stack_base] + call mfree + mov eax,[assembly_stack_base] + call mfree + + mov eax,[preprocessing_workspace.memory_start] + call mfree + mov eax,[assembly_workspace.memory_start] + call mfree + mov eax,[identifier_workspace.memory_start] + call mfree + mov eax,[value_workspace.memory_start] + call mfree + mov eax,[expression_workspace.memory_start] + call mfree + mov eax,[calculation_workspace.memory_start] + call mfree + mov eax,[auxiliary_workspace.memory_start] + call mfree + mov eax,[calm_code_buffer.memory_start] + call mfree + mov eax,[calm_literals_buffer.memory_start] + call mfree + mov eax,[calm_auxiliary_buffer.memory_start] + call mfree + + mov eax,[display_buffer] + call mfree + mov eax,[macro_buffer] + call mfree + mov eax,[temporary_terms] + call mfree + mov eax,[temporary_floats] + call mfree + mov eax,[operator_table] + call mfree + + mov eax,[tree_blocks] + call release_chained_blocks + + mov eax,[storage_blocks] + call release_chained_blocks + + retn + + release_chained_blocks: + test eax,eax + jz blocks_released + mov ebx,[eax] + call mfree + mov eax,ebx + jmp release_chained_blocks + blocks_released: + retn + + release_file_data: + test eax,eax + jz file_data_released + mov ebx,[eax+FileData.cache] + call mfree + release_file_cache: + mov eax,ebx + test eax,eax + jz file_data_released + mov ebx,[ebx+FileCache.next] + call mfree + jmp release_file_cache + file_data_released: + retn + + release_auxiliary_output: + test eax,eax + jz auxiliary_output_released + dec [eax+ValueDefinition.reference_count] + xor eax,eax + mov [edx+MapEntry.value],eax + auxiliary_output_released: + retn + +assembly_pass: +; in: +; esi - ASCIIZ string containing source text +; edx - path to source file +; out: +; cf clear if another pass is needed +; note: +; if both string and file sources are present, they are assembled as combined text + mov [source_file],edx + inc [current_pass] + call discard_errors + mov eax,[directives_stack_base] + mov [directives_stack],eax + mov eax,[root_namespace] + mov [current_context.base_namespace],eax + mov edx,[counters_stack_base] + mov [current_counter],edx + xor eax,eax + mov [edx],al + mov [preprocessing_mode],al + mov [next_pass_needed],al + mov [assembly_mode],al + mov [use_raw_values],al + mov [shift_tracking],al + mov [current_area],eax + mov [current_output_area_entry],eax + mov [initial_output_area_entry],eax + mov [predicted_shift],eax + mov [display_data_length],eax + mov [macro_end_position],eax + mov [proxy_number],eax + mov [output_extension],eax + mov [output_extension_length],eax + mov ebx,[source_context] + mov [ebx+SourceContext.number_of_entries],eax + add ebx,sizeof.SourceContext + mov edi,ebx + mov ecx,sizeof.SourceEntry shr 2 + assert sizeof.SourceEntry and 11b = 0 + rep stosd + mov [line_start],eax + mov [line_end],eax + test esi,esi + jz read_main_source_file + cmp byte [esi],0 + je read_main_source_file + push ebx + call use_source + pop ebx + mov [ebx+SourceEntry.type],SOURCE_MEMORY + jmp fill_main_source_entry + read_main_source_file: + mov esi,[source_file] + test esi,esi + jz no_source_to_assemble + cmp byte [esi],0 + je no_source_to_assemble + push ebx + call read_source + pop ebx + test eax,eax + jz main_source_file_not_found + mov [ebx+SourceEntry.type],SOURCE_FILE + fill_main_source_entry: + mov [ebx+SourceEntry.name],esi + mov [ebx+SourceEntry.text],eax + mov eax,[root_parameter_namespace] + and [eax+SymbolTree_Root.parameters],0 + mov [local_parameter_namespace],eax + mov [ebx+SourceEntry.local_namespace],eax + mov ebx,[source_context] + inc [ebx+SourceContext.number_of_entries] + mov esi,zero_value + mov ecx,4+4 + call create_output_area + mov [current_area],edx + inc [edx+ValueDefinition.reference_count] + mov ebx,[auxiliary_output_areas] + mov edi,release_auxiliary_output + call iterate_through_map +assembly_line: + xor eax,eax + mov [value_position],eax + mov [current_constituent],al + call clear_line_embeddings + get_line: + xor eax,eax + mov [symbol_class],SYMCLASS_PARAMETER + mov [symbol_expected],al + mov [symbol_required],al + mov [name_volatile],al + mov [preprocessed_context],eax + mov [alm_statement],al + mov ebx,[source_context] + mov ecx,[ebx+SourceContext.number_of_entries] + dec ecx + imul ecx,sizeof.SourceEntry + lea ebx,[ebx+sizeof.SourceContext+ecx] + xchg eax,[ebx+SourceEntry.number_of_attached_lines] + inc eax + add [ebx+SourceEntry.line_number],eax + cmp [ebx+SourceEntry.type],SOURCE_CALM + je calm_virtual_machine + mov eax,[ebx+SourceEntry.local_namespace] + mov [parameter_namespace],eax + xor edx,edx + test [eax+SymbolTree_Root.flags],NAMESPACE_UNATTACHED + jnz no_local_namespace + mov edx,eax + mov ecx,[eax+SymbolTree_Root.current_label] + test ecx,ecx + jnz local_namespace_ok + no_local_namespace: + mov eax,[current_context.base_namespace] + mov ecx,[eax+SymbolTree_Root.current_label] + local_namespace_ok: + mov [local_namespace],edx + mov [current_context.base_label],ecx + cmp [ebx+SourceEntry.type],SOURCE_MACRO + je get_line_from_macro + and [name_token],0 + mov esi,[ebx+SourceEntry.text] + mov eax,[ebx+SourceEntry.offset] + add esi,eax + mov [ebx+SourceEntry.line_offset],eax + cmp byte [esi],0 + je source_ended + mov edi,[preprocessing_workspace.memory_start] + preprocess_line_from_file: + mov ecx,14 + mov edx,preprocessing_workspace + call reserve_workspace + lodsb + cmp al,1Ah + je convert_name_symbol + cmp al,22h + je convert_quoted_string + cmp al,27h + je convert_quoted_string + test al,al + jz file_ended + cmp al,0Ah + je line_ended + cmp al,';' + jne preprocess_syntactical_character + xor edx,edx + test [preprocessing_mode],PMODE_RETAIN_COMMENTS + jz skip_comment + preprocess_syntactical_character: + stosb + cmp al,'`' + je convert_parameter + cmp al,'\' + jne preprocess_line_from_file + test [preprocessing_mode],PMODE_ISOLATE_LINES + jnz preprocess_line_from_file + mov edx,esi + detect_line_concatenation: + mov al,[edx] + inc edx + cmp al,0Ah + je concatenate_line + cmp al,';' + je concatenation_comment + cmp al,20h + jne preprocess_line_from_file + jmp detect_line_concatenation + concatenate_line: + mov byte [edi-1],20h + inc esi + inc [ebx+SourceEntry.number_of_attached_lines] + jmp preprocess_line_from_file + concatenation_comment: + test [preprocessing_mode],PMODE_RETAIN_COMMENTS + jnz preprocess_line_from_file + mov byte [edi-1],20h + mov esi,edx + inc [ebx+SourceEntry.number_of_attached_lines] + skip_comment: + lodsb + test al,al + jz file_ended + cmp al,0Ah + je comment_ended + cmp al,22h + je skip_quoted_string + cmp al,27h + je skip_quoted_string + cmp al,1Ah + jne skip_comment + lodsd + lea esi,[esi+eax+12] + jmp skip_comment + comment_ended: + test edx,edx + jnz preprocess_line_from_file + jmp line_ended + skip_quoted_string: + lodsd + add esi,eax + jmp skip_comment + convert_quoted_string: + stosb + mov eax,esi + stosd + lodsd + add esi,eax + jmp preprocess_line_from_file + convert_name_symbol: + mov ecx,[esi] + lea eax,[esi+4+ecx+12] + push eax ebx + mov eax,[eax-4] + test eax,eax + jz name_symbol_not_cached + mov esi,eax + name_symbol_not_cached: + call preprocess_symbol + name_symbol_converted: + pop ebx esi + jmp preprocess_line_from_file + convert_parameter: + cmp byte [esi],1Ah + jne preprocess_line_from_file + lodsb + mov ecx,[esi] + lea eax,[esi+4+ecx+12] + push eax ebx + mov eax,[eax-4] + test eax,eax + jz parameter_to_convert_not_cached + mov esi,eax + parameter_to_convert_not_cached: + call preprocess_symbol + jc name_symbol_converted + call convert_parameter_to_string + jmp name_symbol_converted + file_ended: + dec esi + line_ended: + sub esi,[ebx+SourceEntry.text] + mov [ebx+SourceEntry.offset],esi + jmp line_preprocessed + source_ended: + mov ebx,[source_context] + dec [ebx+SourceContext.number_of_entries] + jnz get_line + cmp [ebx+sizeof.SourceContext+SourceEntry.type],SOURCE_MEMORY + jne no_more_lines + mov esi,[source_file] + test esi,esi + jz no_more_lines + cmp byte [esi],0 + je no_more_lines + lea edi,[ebx+sizeof.SourceContext] + mov ecx,sizeof.SourceEntry shr 2 + assert sizeof.SourceEntry and 11b = 0 + xor eax,eax + rep stosd + call read_source + test eax,eax + jz main_source_file_not_found + mov ebx,[source_context] + inc [ebx+SourceContext.number_of_entries] + add ebx,sizeof.SourceContext + mov [ebx+SourceEntry.type],SOURCE_FILE + mov [ebx+SourceEntry.name],esi + mov [ebx+SourceEntry.text],eax + mov eax,[parameter_namespace] + mov [ebx+SourceEntry.local_namespace],eax + jmp get_line + no_more_lines: + jmp pass_done + get_line_from_macro: + mov edx,[ebx+SourceEntry.text] + mov esi,[edx+ValueDefinition.value] + mov ecx,[edx+ValueDefinition.value_length] + mov eax,[ebx+SourceEntry.offset] + test [ebx+SourceEntry.flags],SRCF_PREPROCESSED + jnz use_preprocessed_line + add ecx,esi + mov [source_end],ecx + add esi,eax + cmp esi,[source_end] + je macro_ended + mov [ebx+SourceEntry.line_offset],eax + mov edi,[preprocessing_workspace.memory_start] + preprocess_line_from_macro: + cmp esi,[source_end] + je macro_line_ended + mov ecx,14 + mov edx,preprocessing_workspace + call reserve_workspace + lodsb + cmp al,1Ah + je reproduce_name_symbol + test al,al + jz macro_line_ended + stosb + cmp al,22h + je reproduce_quoted_string + cmp al,27h + je reproduce_quoted_string + cmp al,30h + je reproduce_internal_token + cmp al,40h + je reproduce_context_token + cmp al,'`' + je conversion_operator_in_macro + jmp preprocess_line_from_macro + reproduce_quoted_string: + movsd + jmp preprocess_line_from_macro + reproduce_internal_token: + mov ecx,[esi] + add ecx,4 + mov edx,preprocessing_workspace + call reserve_workspace + lodsd + stosd + mov ecx,eax + shr ecx,2 + rep movsd + mov cl,al + and cl,11b + rep movsb + jmp preprocess_line_from_macro + reproduce_context_token: + mov [preprocessed_context],esi + assert sizeof.RecognitionContext and 11b = 0 + mov ecx,sizeof.RecognitionContext shr 2 + rep movsd + cmp esi,[source_end] + je macro_line_ended + cmp byte [esi],40h + jne preprocess_line_from_macro + inc esi + sub edi,sizeof.RecognitionContext + jmp reproduce_context_token + reproduce_name_symbol: + mov [name_token],esi + lodsd + push esi ebx + mov esi,eax + call preprocess_symbol + name_symbol_reproduced: + pop ebx esi + jmp preprocess_line_from_macro + conversion_operator_in_macro: + cmp esi,[source_end] + je macro_ended + cmp byte [esi],1Ah + jne preprocess_line_from_macro + inc esi + mov [name_token],esi + lodsd + push esi ebx + mov esi,eax + call preprocess_symbol + jc name_symbol_reproduced + call convert_parameter_to_string + jmp name_symbol_reproduced + convert_parameter_to_string: + call convert_symbolic_value_to_string + mov ebx,[memory_source_cache] + xor eax,eax + call put_into_map + mov edi,[symbol_value_start] + dec edi + mov al,22h + stosb + mov eax,esi + stosd + retn + macro_ended: + mov edx,[ebx+SourceEntry.text] + and [edx+ValueDefinition.flags],not VAL_IN_USE + dec [edx+ValueDefinition.reference_count] + mov ebx,[source_context] + dec [ebx+SourceContext.number_of_entries] + jnz get_line + jmp pass_done + use_preprocessed_line: + test eax,eax + jnz macro_ended + dec eax + mov [ebx+SourceEntry.offset],eax + add ecx,esi + mov [line_start],esi + mov [line_end],ecx + jmp got_line + macro_line_ended: + mov edx,[ebx+SourceEntry.text] + sub esi,[edx+ValueDefinition.value] + mov [ebx+SourceEntry.offset],esi + line_preprocessed: + mov [line_end],edi + mov esi,[preprocessing_workspace.memory_start] + mov [line_start],esi + got_line: + and [line_context],0 +assemble_instruction: + mov ebx,[interceptor_symbol] + call get_available_value + jc no_interceptor + test [edx+ValueDefinition.flags],VAL_UNCONDITIONAL + jz weak_interceptor + jmp execute_instruction + no_interceptor: + xor edx,edx + weak_interceptor: + mov [interceptor],edx + xor eax,eax + mov [label_interceptor],eax + test [assembly_mode],AMODE_CALM_DEFINITION + jnz assemble_alm_instruction + mov [symbol_definition],al + mov [instruction_branch],eax + mov dl,SYMCLASS_INSTRUCTION + call identify_symbol + jc empty_line + mov [label_branch],edx + mov al,[symbol_solid] + mov [label_solid],al + test ebx,ebx + jz unrecognized_instruction + cmp [ebx+SymbolTree_Leaf.class],SYMCLASS_INSTRUCTION + jne labeled_instruction + call get_available_value + jc unrecognized_instruction + test [edx+ValueDefinition.flags],VAL_UNCONDITIONAL + jnz execute_instruction + cmp [interceptor],0 + jne unrecognized_instruction + test [assembly_mode],AMODE_SKIP or AMODE_DEFINITION + jnz unrecognized_instruction + execute_instruction: + mov al,[edx+ValueDefinition.type] + cmp al,VALTYPE_CALM + je launch_calm + cmp al,VALTYPE_NATIVE_COMMAND + je execute_native_instruction + cmp al,VALTYPE_SYMBOLIC + je use_macro + cmp al,VALTYPE_RESERVED + jne unrecognized_instruction + mov edx,_symbolic_self_reference + call register_error + unrecognized_instruction: + test [assembly_mode],AMODE_SKIP + jnz assembly_line + test [assembly_mode],AMODE_DEFINITION + jnz add_line_to_macro + cmp [interceptor],0 + jne execute_interceptor + cmp [label_interceptor],0 + jne execute_label_interceptor + mov edx,_illegal_instruction + call register_error + mov ebx,[label_branch] + test ebx,ebx + jz assembly_line + mov [symbol_class],SYMCLASS_INSTRUCTION + or [symbol_required],1 + or [symbol_expected],1 + call scan_symbol_branch + xor edx,edx + call mark_symbol_as_used + mov ebx,[instruction_branch] + test ebx,ebx + jz assembly_line + mov [symbol_class],SYMCLASS_STRUCTURE + or [symbol_required],1 + or [symbol_expected],1 + call scan_symbol_branch + xor edx,edx + call mark_symbol_as_used + jmp assembly_line + labeled_instruction: + mov [label_leaf],ebx + mov [label_instruction_start],esi + mov ebx,[label_interceptor_symbol] + call get_available_value + jc no_label_interceptor + test [edx+ValueDefinition.flags],VAL_UNCONDITIONAL + jnz execute_labeled_instruction + mov [label_interceptor],edx + mov eax,[embedded_context] + mov [label_instruction_context],eax + jmp identify_structure_instruction + no_label_interceptor: + call move_to_next_symbol + jc unrecognized_instruction ; orphan label + cmp al,':' + je define_label + cmp al,'=' + je define_numeric_symbol + identify_structure_instruction: + mov dl,SYMCLASS_STRUCTURE + call identify_symbol + mov [instruction_branch],edx + test ebx,ebx + jz unrecognized_instruction + cmp [ebx+SymbolTree_Leaf.class],SYMCLASS_STRUCTURE + jne unrecognized_instruction + call get_available_value + jc unrecognized_instruction + test [edx+ValueDefinition.flags],VAL_UNCONDITIONAL + jnz execute_labeled_instruction + test [assembly_mode],AMODE_SKIP or AMODE_DEFINITION + jnz unrecognized_instruction + cmp [interceptor],0 + jne execute_interceptor + cmp [label_interceptor],0 + jne execute_label_interceptor + execute_labeled_instruction: + mov al,[edx+ValueDefinition.type] + cmp al,VALTYPE_CALM + je launch_calm + cmp al,VALTYPE_SYMBOLIC + je use_struc + cmp al,VALTYPE_NATIVE_COMMAND + jne unrecognized_instruction + execute_native_instruction: + mov eax,[current_pass] + mov [ebx+SymbolTree_Leaf.last_use_pass],eax + jmp [edx+ValueDefinition.value] + empty_line: + mov al,[assembly_mode] + and al,AMODE_SKIP or AMODE_DEFINITION + cmp al,AMODE_DEFINITION + je add_line_to_macro + jmp assembly_line + execute_interceptor: + mov ebx,[interceptor_symbol] + mov edx,[interceptor] + xor eax,eax + mov [embedded_context],eax + mov esi,[line_start] + jmp execute_instruction + execute_label_interceptor: + mov ebx,[label_interceptor_symbol] + mov edx,[label_interceptor] + mov eax,[label_instruction_context] + mov [embedded_context],eax + mov esi,[label_instruction_start] + cmp [edx+ValueDefinition.type],VALTYPE_CALM + je launch_calm + jmp use_struc +instruction_assembled: + cmp [current_constituent],0 + jne extra_characters_on_line + call warp_to_next_symbol + jc assembly_line + extra_characters_on_line: + mov edx,_extra_characters_on_line + call register_error + jmp assembly_line + pass_done: + mov dl,DBLOCK_CONTROL + call find_directive_block + jc assemble_postponed_block + mov esi,edi + sub esi,[edi+DirectiveBlock.length_of_data] + mov edx,_missing_end_directive + call register_delayed_error + call close_control_directive_block + jmp pass_done + assemble_postponed_block: + mov dl,DBLOCK_POSTPONED + call find_directive_block + jc no_postponed_blocks + mov ebx,edi + mov esi,edi + sub esi,[edi+DirectiveBlock.length_of_data] + mov edi,[source_context] + call clone_source_context + mov edi,ebx + call close_directive_block + or [assembly_mode],AMODE_POSTPONED + jmp assembly_line + no_postponed_blocks: + mov ebx,[root_namespace] + call detect_mispredictions + assemble_suspended_block: + mov dl,DBLOCK_SUSPENDED + call find_directive_block + jc no_suspended_blocks + cmp [next_pass_needed],0 + jne ignore_suspended_block + mov ebx,edi + mov esi,edi + sub esi,[edi+DirectiveBlock.length_of_data] + mov edi,[source_context] + call clone_source_context + mov edi,ebx + call close_directive_block + or [assembly_mode],AMODE_POSTPONED + jmp assembly_line + ignore_suspended_block: + call close_directive_block + jmp assemble_suspended_block + no_suspended_blocks: + mov esi,[directives_stack] + signal_unclosed_blocks: + cmp esi,[directives_stack_base] + je unclosed_blocks_signalled + sub esi,sizeof.DirectiveBlock + mov eax,[esi+DirectiveBlock.length_of_data] + sub esi,eax + mov edx,_missing_end_directive + call register_delayed_error + jmp signal_unclosed_blocks + unclosed_blocks_signalled: + xor eax,eax + xchg eax,[current_area] + dec [eax+ValueDefinition.reference_count] + mov al,[next_pass_needed] + sub al,1 + retn + main_source_file_not_found: + mov ebx,esi + mov edx,_source_file_not_found + call register_error + no_source_to_assemble: + mov esi,zero_value + mov ecx,4+4 + call create_output_area + mov [current_area],edx + inc [edx+ValueDefinition.reference_count] + stc + retn + +initialize_workspace: +; in: +; edx - Workspace +; preserves: ebx, edx, esi, edi + push edx + mov ecx,1000h + call malloc_growable + pop edx + mov [edx+Workspace.memory_start],eax + add eax,ecx + mov [edx+Workspace.memory_end],eax + retn + +reserve_workspace: +; in: +; edx - Workspace +; edi - top of used workspace area +; ecx = size of required reserve +; out: +; cf set if workspace had to be expanded +; edi - top of used workspace area (possibly relocated when cf is set) +; preserves: ebx, edx, esi + mov eax,[edx+Workspace.memory_end] + sub eax,ecx + jc not_enough_workspace + cmp edi,eax + ja not_enough_workspace + clc + retn + not_enough_workspace: + add ecx,edi + jc allocation_overflow + sub ecx,[edx+Workspace.memory_start] + push ecx + bsr eax,ecx + xchg ecx,eax + dec cl + shr eax,cl + inc eax + shl eax,cl + mov ecx,eax + pop eax + cmp ecx,eax + jbe allocation_overflow + cmp edi,[edx+Workspace.memory_start] + je reestablish_workspace + expand_workspace: + mov eax,[edx+Workspace.memory_start] + sub edi,eax + push edx + call realloc + pop edx + update_workspace: + mov [edx+Workspace.memory_start],eax + add edi,eax + add eax,ecx + mov [edx+Workspace.memory_end],eax + stc + retn + reestablish_workspace: + push edx ecx + xor eax,eax + xchg eax,[edx+Workspace.memory_start] + call mfree + pop ecx + call malloc_growable + pop edx + xor edi,edi + jmp update_workspace + allocation_overflow: + jmp out_of_memory + +grow_stack: +; in: +; eax = base address of memory block containing stack data +; ecx = required minimum size of memory block +; out: +; eax = new base address of memory block containing stack data +; ecx = new size of memory block +; preserves: ebx, esi, edi + push ecx + bsr edx,ecx + xchg ecx,edx + sub cl,2 + shr edx,cl + inc edx + shl edx,cl + mov ecx,edx + pop edx + cmp ecx,edx + jbe allocation_overflow + call realloc + retn + +create_source_entry: +; out: +; cf set when the maximum number of entries in context has been exceeded +; when cf = 0: +; ebx - new SourceEntry in the main SourceContext +; preserves: edx, esi + mov ebx,[source_context] + mov eax,[ebx+SourceContext.number_of_entries] + cmp eax,[maximum_depth_of_stack] + jae source_context_full + inc [ebx+SourceContext.number_of_entries] + imul eax,sizeof.SourceEntry + add eax,sizeof.SourceContext + mov edi,eax + add eax,sizeof.SourceEntry + cmp eax,[source_context_maximum_length] + jbe source_context_length_ok + mov ecx,eax + mov eax,ebx + mov ebx,edx + call realloc + mov [source_context],eax + mov [source_context_maximum_length],ecx + mov edx,ebx + mov ebx,eax + source_context_length_ok: + add ebx,edi + mov edi,ebx + mov ecx,sizeof.SourceEntry shr 2 + assert sizeof.SourceEntry and 11b = 0 + xor eax,eax + rep stosd + cmp [alm_statement],0 + je source_entry_created + mov [edi-sizeof.SourceEntry+SourceEntry.flags],SRCF_ALM_STATEMENT + source_entry_created: + clc + retn + source_context_full: + stc + retn + +create_parameter_namespace: +; out: +; eax - SymbolTree_Root +; preserves: ebx, edx, esi, edi + mov ecx,sizeof.SymbolTree_Root + sizeof.SymbolTree_LocalNode + call create_tree_element + or [eax+SymbolTree_Root.attributes],SYMTREE_LOCAL + or [eax+SymbolTree_Root.flags],NAMESPACE_UNATTACHED + retn + +clone_source_context: +; in: +; esi - SourceContext +; edi - buffer +; out: +; edi = pointer advanced past the stored data +; preserves: ebx + cmp edi,[source_context] + sete [source_context_affected] + mov eax,[esi+SourceContext.number_of_entries] + assert sizeof.SourceContext and 11b = 0 + mov ecx,sizeof.SourceContext shr 2 + rep movsd + test eax,eax + jnz clone_source_entry + retn + clone_source_entry: + cmp [esi+SourceEntry.type],SOURCE_MACRO + jne copy_source_entry + mov edx,[esi+SourceEntry.text] + inc [edx+ValueDefinition.reference_count] + cmp [source_context_affected],0 + je copy_source_entry + or [edx+ValueDefinition.flags],VAL_IN_USE + copy_source_entry: + assert sizeof.SourceEntry and 11b = 0 + mov ecx,sizeof.SourceEntry shr 2 + rep movsd + dec eax + jnz clone_source_entry + retn + +release_source_context: +; in: +; eax - SourceContext +; preserves: eax, ebx, esi, edi + cmp eax,[source_context] + sete [source_context_affected] + push eax + mov ecx,[eax+SourceContext.number_of_entries] + add eax,sizeof.SourceContext + test ecx,ecx + jnz release_source_entry + pop eax + retn + release_source_entry: + cmp [eax+SourceEntry.type],SOURCE_MACRO + jne source_entry_released + mov edx,[eax+SourceEntry.text] + dec [edx+ValueDefinition.reference_count] + cmp [source_context_affected],0 + je source_entry_released + and [edx+ValueDefinition.flags],not VAL_IN_USE + source_entry_released: + add eax,sizeof.SourceEntry + loop release_source_entry + pop eax + retn + +get_file_source_entry: +; out: +; ebx - SourceEntry in the main SourceContext +; preserves: edx, esi, edi + mov ebx,[source_context] + mov ecx,[ebx+SourceContext.number_of_entries] + mov eax,ecx + imul eax,sizeof.SourceEntry + lea ebx,[ebx+sizeof.SourceContext+eax] + find_file_source_entry: + sub ebx,sizeof.SourceEntry + cmp [ebx+SourceEntry.type],SOURCE_FILE + loopne find_file_source_entry + retn + +preprocess_symbol: +; in: +; esi - contents of the name token (32-bit length and name followed by two hashes) +; edi - pointer into preprocessing_workspace where the preprocessed text should be stored +; out: +; edi - just after the preprocessed text +; cf set when symbol was used as-is (was not recognized as a parameter) +; when cf = 0: +; [symbol_value_start] - start of the preprocessed text +; [symbol_value_end] - end of the preprocessed text (the same as edi) + mov eax,edi + sub eax,[preprocessing_workspace.memory_start] + mov [symbol_value_start],eax + mov [symbol_data],esi + lodsd + mov ecx,eax + mov eax,[esi+ecx+4] + mov [case_insensitive_hash],eax + mov edx,[esi+ecx] + mov [name_kind],NAME_CASESENSITIVE + mov ebx,[parameter_namespace] + call scan_namespace + jnc parameter_found + mov [name_kind],NAME_CASEINSENSITIVE + mov ebx,[parameter_namespace] + test [ebx+SymbolTree_Root.attributes],SYMTREE_WITH_CASEINSENSITIVE_PARAMETERS + jz no_local_parameter_recognized + mov edx,[case_insensitive_hash] + call scan_namespace + jnc parameter_found + no_local_parameter_recognized: + cmp byte [esi],'%' + jne no_parameter_recognized + cmp ecx,2 + ja no_parameter_recognized + jb current_counter_value + cmp byte [esi+1],'%' + je current_limit_value + no_parameter_recognized: + mov edi,[preprocessing_workspace.memory_start] + add edi,[symbol_value_start] + mov al,1Ah + stosb + mov eax,[symbol_data] + stosd + stc + retn + parameter_found: + mov edi,[preprocessing_workspace.memory_start] + add edi,[symbol_value_start] + mov edx,[ebx+SymbolTree_Leaf.definition] + mov al,[edx+ValueDefinition.type] + cmp al,VALTYPE_SYMBOLIC + je simple_parameter_value + cmp al,VALTYPE_SYMBOLIC_SEQUENCE + je iterator_value + cmp al,VALTYPE_NUMERIC_SEQUENCE + je named_counter_value + cmp al,VALTYPE_NATIVE_COMMAND + jne no_parameter_recognized + jmp [edx+ValueDefinition.value] + current_counter_value: + mov ebx,[parameter_namespace] + test [ebx+SymbolTree_Root.parameters],SPECPARM_COUNTER + jz no_parameter_recognized + mov esi,[current_counter] + mov dl,DBLOCK_CONTROL + call find_directive_block + find_breakable_block: + jc no_parameter_recognized + test [edi+DirectiveBlock.flags],CTRLF_BREAKABLE + jz look_deeper_for_breakable_block + mov eax,[parameter_namespace] + cmp eax,[edi+DirectiveBlock.parameter_namespace] + je found_breakable_block + look_deeper_for_breakable_block: + mov esi,[edi+DirectiveBlock.prior_counter_position] + add esi,[counters_stack_base] + call find_next_directive_block + jmp find_breakable_block + found_breakable_block: + cmp byte [esi],0 + je no_parameter_recognized + mov edi,[preprocessing_workspace.memory_start] + add edi,[symbol_value_start] + movzx ecx,byte [esi] + add ecx,6 + mov edx,preprocessing_workspace + call reserve_workspace + mov al,30h + stosb + mov edx,edi + xor eax,eax + lodsb + stosd + mov ecx,eax + rep movsb + test byte [edi-1],80h + jz parameter_replaced + xor al,al + stosb + inc dword [edx] + jmp parameter_replaced + current_limit_value: + mov ebx,[parameter_namespace] + test [ebx+SymbolTree_Root.parameters],SPECPARM_LIMIT + jz no_parameter_recognized + mov dl,DBLOCK_CONTROL + call find_directive_block + find_block_with_limit: + jc no_parameter_recognized + test [edi+DirectiveBlock.flags],CTRLF_BREAKABLE + jz look_deeper_for_block_with_limit + test [edi+DirectiveBlock.flags],CTRLF_HAS_REPEAT_DATA + jz look_deeper_for_block_with_limit + mov eax,[parameter_namespace] + cmp eax,[edi+DirectiveBlock.parameter_namespace] + je found_block_with_limit + look_deeper_for_block_with_limit: + call find_next_directive_block + jmp find_block_with_limit + found_block_with_limit: + mov ebx,edi + sub ebx,sizeof.RepeatData + mov edi,[preprocessing_workspace.memory_start] + add edi,[symbol_value_start] + mov ecx,[ebx+RepeatData.limit_length] + add ecx,6 + mov edx,preprocessing_workspace + call reserve_workspace + mov al,30h + stosb + mov edx,edi + mov eax,[ebx+RepeatData.limit_length] + stosd + sub ebx,eax + mov esi,ebx + mov ecx,eax + rep movsb + test byte [edi-1],80h + jz parameter_replaced + xor al,al + stosb + inc dword [edx] + jmp parameter_replaced + named_counter_value: + mov edx,[edx+ValueDefinition.value] + mov ebx,[edx] + add ebx,[counters_stack_base] + cmp byte [ebx],0 + je no_parameter_recognized + add edx,4 + movzx ecx,byte [ebx] + cmp ecx,[edx] + jae estimate_counter_length + mov ecx,[edx] + estimate_counter_length: + add ecx,6 + mov esi,edx + mov edx,preprocessing_workspace + call reserve_workspace + mov al,30h + stosb + xor eax,eax + stosd + push edi + movzx ecx,byte [ebx] + inc ebx + lodsd + sub eax,ecx + jnc counter_base_selected + add ecx,eax + neg eax + xchg ebx,esi + counter_base_selected: + mov edx,eax + jecxz counter_added_to_base + xor ah,ah + add_counter_to_base: + lodsb + add al,ah + setc ah + add al,[ebx] + adc ah,0 + inc ebx + add al,-1 + adc ah,0 + stosb + loop add_counter_to_base + counter_added_to_base: + mov ecx,edx + jecxz counter_carried + carry_counter: + lodsb + add al,ah + setc ah + add al,-1 + adc ah,0 + stosb + loop carry_counter + counter_carried: + pop edx + mov al,ah + dec al + jnz extend_counter_value + cmp edx,edi + je counter_value_finished + test byte [edi-1],80h + jz counter_value_finished + extend_counter_value: + stosb + counter_value_finished: + mov ecx,edi + sub ecx,edx + mov [edx-4],ecx + jmp parameter_replaced + iterator_value: + mov edx,[edx+ValueDefinition.value] + mov ebx,[edx] + add ebx,[counters_stack_base] + movzx ecx,byte [ebx] + test ecx,ecx + jz no_parameter_recognized + push edi + mov edi,value_index + xor eax,eax + mov [edi],eax + mov esi,ebx + inc esi + rep movsb + mov eax,[value_index] + pop edi + shl eax,2 + mov esi,[edx+eax] + mov ecx,[edx+eax+4] + sub ecx,esi + add esi,edx + jmp copy_parameter_value + simple_parameter_value: + mov esi,[edx+ValueDefinition.value] + mov ecx,[edx+ValueDefinition.value_length] + copy_parameter_value: + push ecx + mov edx,preprocessing_workspace + call reserve_workspace + pop ecx + cmp [preprocessed_context],0 + jne copy_with_preprocessed_context + mov al,cl + shr ecx,2 + rep movsd + mov cl,al + and cl,11b + rep movsb + jmp parameter_replaced + copy_token_pointer: + movsd + sub ecx,4 + copy_with_preprocessed_context: + test ecx,ecx + jz parameter_replaced + lodsb + stosb + dec ecx + cmp al,1Ah + je copy_token_pointer + cmp al,22h + je copy_token_pointer + cmp al,27h + je copy_token_pointer + cmp al,30h + je copy_token_data + cmp al,40h + jne copy_with_preprocessed_context + mov eax,ecx + cmp dword [esi],0 + jne copy_parameter_context + mov edx,esi + mov esi,[preprocessed_context] + assert sizeof.RecognitionContext and 11b = 0 + mov ecx,sizeof.RecognitionContext shr 2 + rep movsd + lea esi,[edx+sizeof.RecognitionContext] + mov ecx,eax + sub ecx,sizeof.RecognitionContext + jmp copy_with_preprocessed_context + copy_parameter_context: + assert sizeof.RecognitionContext and 11b = 0 + mov ecx,sizeof.RecognitionContext shr 2 + rep movsd + mov ecx,eax + sub ecx,sizeof.RecognitionContext + jmp copy_with_preprocessed_context + copy_token_data: + lodsd + stosd + sub ecx,4 + sub ecx,eax + xchg ecx,eax + rep movsb + xchg ecx,eax + jmp copy_with_preprocessed_context + local_symbol_name: + mov ecx,1+sizeof.RecognitionContext+1+4+1+sizeof.RecognitionContext + mov edx,preprocessing_workspace + call reserve_workspace + mov al,40h + stosb + mov eax,[local_namespace] + mov [edi+RecognitionContext.base_namespace],eax + xor eax,eax + mov [edi+RecognitionContext.base_label],eax + add edi,sizeof.RecognitionContext + mov al,1Ah + stosb + mov eax,[symbol_data] + stosd + mov al,40h + stosb + mov esi,[preprocessed_context] + assert sizeof.RecognitionContext and 11b = 0 + mov ecx,sizeof.RecognitionContext shr 2 + test esi,esi + jz reset_context + rep movsd + stc + retn + reset_context: + xor eax,eax + rep stosd + stc + retn + parameter_replaced: + mov [symbol_value_end],edi + mov eax,[preprocessing_workspace.memory_start] + add [symbol_value_start],eax + clc + retn + +convert_symbolic_value_to_string: +; in: +; [symbol_value_start] - start of the preprocessed text to convert +; [symbol_value_end] - end of the preprocessed text to convert +; out: +; esi - 32-bit length followed by string data +; ecx = total length of string data (including the length prefix) + mov esi,[symbol_value_start] + mov edx,assembly_workspace + mov edi,[edx+Workspace.memory_start] + add edi,4 + convert_token_to_text: + mov ecx,[symbol_value_end] + sub ecx,esi + jbe finish_conversion + mov edx,assembly_workspace + call reserve_workspace + lodsb + cmp al,1Ah + je convert_name_token_to_text + cmp al,22h + je convert_string_token_to_text + cmp al,27h + je convert_string_token_to_text + cmp al,30h + je convert_internal_number_to_text + cmp al,40h + je ignore_context_token + stosb + jmp convert_token_to_text + ignore_context_token: + add esi,sizeof.RecognitionContext + jmp convert_token_to_text + convert_name_token_to_text: + lodsd + mov ebx,esi + mov esi,eax + mov ecx,[esi] + mov edx,assembly_workspace + call reserve_workspace + lodsd + mov ecx,eax + rep movsb + mov esi,ebx + jmp convert_token_to_text + convert_string_token_to_text: + lodsd + mov ebx,esi + mov esi,eax + call enclose_string + mov esi,ebx + cmp byte [esi-4-1],27h + jne convert_token_to_text + dec edi + jmp convert_token_to_text + enclose_string: + mov ecx,[esi] + inc ecx + shl ecx,1 + mov edx,assembly_workspace + call reserve_workspace + lodsd + mov ecx,eax + mov al,27h + stosb + copy_string_characters: + jecxz string_characters_copied + lodsb + stosb + dec ecx + cmp al,27h + jne copy_string_characters + stosb + jmp copy_string_characters + string_characters_copied: + mov al,27h + stosb + retn + convert_internal_number_to_text: + mov edx,esi + lodsd + add esi,eax + push esi edi + call convert_number_back + pop edi + mov esi,edx + mov ecx,[esi] + mov edx,assembly_workspace + call reserve_workspace + lodsd + mov ecx,eax + rep movsb + pop esi + jmp convert_token_to_text + finish_conversion: + mov esi,[assembly_workspace.memory_start] + mov ecx,edi + sub ecx,esi + lea eax,[ecx-4] + mov [esi],eax + retn + +compare_symbolic_values: +; in: +; esi - first symbolic value +; edi - second symbolic value +; ecx = length to compare +; out: +; ecx = zero when values equal, or a number of bytes following the point of difference +; esi - the point of difference in first value +; edi - the point of difference in second value + mov al,[esi] + cmp al,[edi] + jne symbolic_values_compared + inc esi + inc edi + cmp al,1Ah + je compare_tokens_with_data + cmp al,22h + je compare_tokens_with_data + cmp al,27h + je compare_tokens_with_data + cmp al,30h + je compare_internal_tokens + cmp al,40h + je compare_context_tokens + loop compare_symbolic_values + symbolic_values_compared: + retn + compare_tokens_with_data: + dec ecx + mov eax,[esi] + mov edx,[edi] + cmp eax,edx + jne compare_token_data + add esi,4 + add edi,4 + sub ecx,4 + jnz compare_symbolic_values + retn + compare_token_data: + mov ebx,[eax] + cmp ebx,[edx] + jne symbolic_values_compared + add eax,ebx + add edx,ebx + xchg esi,eax + xchg edi,edx + xchg ecx,ebx + dec ecx + shr ecx,2 + inc ecx + cmp byte [eax-1],1Ah + jne compare_token_dwords + inc ecx + add esi,4 + add edi,4 + compare_token_dwords: + std + repe cmpsd + cld + jne token_data_content_differs + lea esi,[eax+4] + lea edi,[edx+4] + mov ecx,ebx + sub ecx,4 + jnz compare_symbolic_values + retn + token_data_content_differs: + mov esi,eax + mov edi,edx + mov ecx,ebx + retn + compare_internal_tokens: + mov eax,[esi] + cmp eax,[edi] + jne symbolic_values_compared + add esi,4 + add edi,4 + sub ecx,1+4 + sub ecx,eax + xchg ecx,eax + repe cmpsb + je internal_tokens_equal + inc ecx + dec esi + dec edi + add ecx,eax + retn + internal_tokens_equal: + add ecx,eax + jnz compare_symbolic_values + retn + compare_context_tokens: + dec ecx + assert sizeof.RecognitionContext and 11b = 0 + mov edx,sizeof.RecognitionContext shr 2 + compare_contexts: + mov eax,[esi] + cmp eax,[edi] + jne symbolic_values_compared + add esi,4 + add edi,4 + sub ecx,4 + jz symbolic_values_compared + dec edx + jnz compare_contexts + jmp compare_symbolic_values + +move_to_next_symbol: +; in: +; esi = pointer into preprocessed line or current embedded value +; zeroed ecx is recommended for whitespace detection +; out: +; esi = pointer advanced past the whitespace and context tokens +; cf set when reached end of line or embedded value +; ecx increased when there was whitespace on the way, preserved otherwise +; when cf = 0: +; esi - next symbol +; al = initial byte of the next symbol +; preserves: ebx, edx, edi +; note: +; [embedded_context] is updated to point to a RecognitionContext that should be in force at the new position (null means current namespace context); + cmp esi,[line_end] + jb next_token_available + stc + retn + next_token_available: + mov al,[esi] + cmp al,40h + je set_embedded_context + cmp al,20h + je pass_whitespace + clc + retn + pass_whitespace: + inc esi + inc ecx + jmp move_to_next_symbol + set_embedded_context: + inc esi + cmp [esi+RecognitionContext.base_namespace],0 + je restore_embedded_context + mov [embedded_context],esi + add esi,sizeof.RecognitionContext + jmp move_to_next_symbol + restore_embedded_context: + add esi,sizeof.RecognitionContext + mov eax,[number_of_line_embeddings] + test eax,eax + jz clear_embedded_context + dec eax + imul eax,sizeof.LineEmbedding + add eax,[line_embeddings] + mov eax,[eax+LineEmbedding.recognition_context] + mov [embedded_context],eax + jmp move_to_next_symbol + clear_embedded_context: + and [embedded_context],0 + jmp move_to_next_symbol + +warp_to_next_symbol: +; in: +; esi = pointer into preprocessed line or current embedded value +; zeroed ecx is recommended for whitespace detection +; out: +; esi - next symbol +; cf set when end of line reached +; ecx increased when there was whitespace on the way, preserved otherwise +; when cf = 0: +; al = initial byte of the next symbol +; preserves: ebx, edx, edi +; note: +; [embedded_context] is updated to point to a RecognitionContext that should be in force at the new position (null means current namespace context); + call move_to_next_symbol + jc warp_through_embedding_boundary + retn + warp_through_embedding_boundary: + mov eax,[number_of_line_embeddings] + sub eax,1 + jc reached_end_of_line + mov [number_of_line_embeddings],eax + imul eax,sizeof.LineEmbedding + add eax,[line_embeddings] + mov esi,eax + push edx + add ecx,[esi+LineEmbedding.whitespace] + mov edx,[esi+LineEmbedding.recognition_context] + mov [embedded_context],edx + mov edx,[esi+LineEmbedding.definition] + dec [edx+ValueDefinition.reference_count] + and [edx+ValueDefinition.flags],not VAL_IN_USE + mov edx,[esi+LineEmbedding.previous_end] + mov [line_end],edx + mov esi,[esi+LineEmbedding.previous_pointer] + pop edx + jmp warp_to_next_symbol + reached_end_of_line: + ; stc + retn + +cut_piece_of_line: +; in: +; esi = pointer into preprocessed line or current embedded value +; edi - LineExcerpt to be filled with information about cut piece of line +; dl = initial byte of symbol that should end the cut value, zero to cut up to the end of line (or current embedded value) +; dh = initial byte of symbol that would open the nested piece, zero for no nesting +; [breakpoint_token] = initial byte of symbol that should unconditionally end the cut value if it is met +; out: +; esi = pointer advanced past the cut piece +; al = initial byte of symbol at pointer, zero when no more symbols there +; preserves: ebx, edx, edi +; note: when [breakpoint_token] has the same value as either dl or dh, it has no effect + mov [number_of_enclosings],1 + call move_to_next_symbol + mov [edi+LineExcerpt.data_start],esi + mov [edi+LineExcerpt.data_end],esi + jc last_piece_in_line + mov ecx,[embedded_context] + mov [edi+LineExcerpt.recognition_context],ecx + cut_piece: + cmp al,dh + je nested_piece + cmp al,dl + je close_nested_piece + cmp al,[breakpoint_token] + jne cut_token + retn + nested_piece: + inc [number_of_enclosings] + jmp cut_token + close_nested_piece: + dec [number_of_enclosings] + jz end_of_piece + cut_token: + cmp al,1Ah + je cut_token_with_data + cmp al,22h + je cut_token_with_data + cmp al,27h + je cut_token_with_data + cmp al,30h + je cut_internal_token + inc esi + cut_next_token: + mov [edi+LineExcerpt.data_end],esi + call move_to_next_symbol + jnc cut_piece + last_piece_in_line: + xor al,al + end_of_piece: + mov ecx,[embedded_context] + mov [edi+LineExcerpt.leftover_context],ecx + retn + cut_token_with_data: + add esi,1+4 + jmp cut_next_token + cut_internal_token: + inc esi + lodsd + add esi,eax + jmp cut_next_token + +extract_piece_of_line: +; in: +; esi = pointer into preprocessed line (not an embedded value) +; edi - buffer for token sequence, must be large enough to hold all the remaining tokens in line and an additional context token +; dl = initial byte of symbol that should end the cut value, zero to cut up to the end of line +; dh = initial byte of symbol that would open the nested piece, zero for no nesting +; [breakpoint_token] = initial byte of symbol that should unconditionally end the cut value if it is met +; out: +; esi = pointer advanced past the processed piece +; edi - end of the extracted sequence of tokens in provided buffer +; al = initial byte of symbol at pointer, zero when no more symbols there +; [context_boundary] = equal to edi if the extracted sequence ends with a context token +; preserves: ebx, edx +; note: +; when [breakpoint_token] has the same value as either dl or dh, it has no effect +; when AMODE_DEFINITION is active, current context is not added to the extracted value + and [context_boundary],0 + call move_to_next_symbol + jc no_piece_to_extract + mov [number_of_enclosings],1 + mov [whitespace_boundary],edi + mov al,40h + stosb + mov eax,[embedded_context] + test eax,eax + jz extract_current_context + xchg esi,eax + assert sizeof.RecognitionContext and 11b = 0 + mov ecx,sizeof.RecognitionContext shr 2 + rep movsd + mov esi,eax + mov [context_boundary],edi + jmp extract_piece + extract_contextless: + dec edi + jmp extract_piece + extract_current_context: + test [assembly_mode],AMODE_DEFINITION + jnz extract_contextless + call store_current_context + extract_piece: + lodsb + cmp al,dh + je nested_piece_to_extract + cmp al,dl + je close_extracted_nested_piece + cmp al,[breakpoint_token] + je extraction_breakpoint + extract_token: + cmp al,40h + je extract_context_token + cmp al,20h + je extract_whitespace + and [whitespace_boundary],0 + stosb + cmp al,1Ah + je extract_token_with_data + cmp al,22h + je extract_token_with_data + cmp al,27h + je extract_token_with_data + cmp al,30h + je extract_internal_token + extract_next_token: + cmp esi,[line_end] + jne extract_piece + xor al,al + cmp [whitespace_boundary],0 + jne drop_final_whitespace + retn + nested_piece_to_extract: + inc [number_of_enclosings] + jmp extract_token + close_extracted_nested_piece: + dec [number_of_enclosings] + jnz extract_token + extraction_breakpoint: + dec esi + cmp [whitespace_boundary],0 + jne drop_final_whitespace + retn + drop_final_whitespace: + mov edi,[whitespace_boundary] + retn + no_piece_to_extract: + xor al,al + retn + extract_whitespace: + cmp [whitespace_boundary],0 + jne whitespace_boundary_ok + mov [whitespace_boundary],edi + whitespace_boundary_ok: + stosb + jmp extract_next_token + extract_token_with_data: + movsd + jmp extract_next_token + extract_internal_token: + lodsd + stosd + mov ecx,eax + rep movsb + jmp extract_next_token + extract_context_token: + cmp [whitespace_boundary],0 + jne context_whitespace_boundary_ok + mov [whitespace_boundary],edi + context_whitespace_boundary_ok: + mov [embedded_context],esi + cmp dword [esi],0 + jne embedded_context_for_extraction_ok + and [embedded_context],0 + embedded_context_for_extraction_ok: + call make_recognition_context_token + jmp extract_next_token + +make_recognition_context_token: +; in: +; al = 40h +; esi - RecognitionContext (contents of the context token) +; edi - buffer for the context token +; [context_boundary] = equal to edi if it is at the end of another context token +; out: +; esi - past the processed RecognitionContext +; edi - past the extracted context token +; [context_boundary] - past the extracted context token +; preserves: ebx, edx + cmp edi,[context_boundary] + je reuse_recognition_context + stosb + jmp store_recognition_context + reuse_recognition_context: + sub edi,sizeof.RecognitionContext + store_recognition_context: + ; in: + ; esi - RecognitionContext to read (may have base namespace zeroed to indicate current context) + ; edi - RecognitionContext to fill + ; out: + ; esi - past the processed RecognitionContext + ; edi - past the filled RecognitionContext + ; [context_boundary] - past the filled RecognitionContext + ; preserves: ebx, edx + test [assembly_mode],AMODE_DEFINITION + jnz copy_recognition_context + cmp [esi+RecognitionContext.base_namespace],0 + je capture_context_reset + copy_recognition_context: + assert sizeof.RecognitionContext and 11b = 0 + mov ecx,sizeof.RecognitionContext shr 2 + rep movsd + mov [context_boundary],edi + retn + capture_context_reset: + add esi,sizeof.RecognitionContext + store_current_context: + ; in: + ; edi - RecognitionContext to fill + ; out: + ; edi - past the filled RecognitionContext + ; [context_boundary] - past the filled RecognitionContext + ; preserves: ebx, edx, esi + mov eax,[current_context.base_namespace] + mov ecx,[current_context.base_label] + mov [edi+RecognitionContext.base_namespace],eax + mov [edi+RecognitionContext.base_label],ecx + add edi,sizeof.RecognitionContext + mov [context_boundary],edi + retn + +embed_symbolic_value: +; in: +; ebx - SymbolTree_Leaf +; edx - ValueDefinition +; esi = pointer into preprocessed line or current embedded value +; ecx = number of whitespace tokens to imply after emdedding +; out: +; esi = pointer into embedded value +; preserves: ebx, edx + mov eax,[number_of_line_embeddings] + inc eax + mov [number_of_line_embeddings],eax + imul eax,sizeof.LineEmbedding + cmp eax,[line_embeddings_maximum_length] + jbe line_embedding_allocated + push eax ecx edx + mov ecx,sizeof.LineEmbedding + add ecx,[line_embeddings_maximum_length] + mov eax,[line_embeddings] + call grow_stack + mov [line_embeddings_maximum_length],ecx + mov [line_embeddings],eax + pop edx ecx eax + line_embedding_allocated: + sub eax,sizeof.LineEmbedding + add eax,[line_embeddings] + mov [eax+LineEmbedding.whitespace],ecx + mov edi,[line_end] + mov [eax+LineEmbedding.previous_pointer],esi + mov [eax+LineEmbedding.previous_end],edi + mov ecx,[embedded_context] + mov [eax+LineEmbedding.recognition_context],ecx + and [embedded_context],0 + mov esi,[edx+ValueDefinition.value] + mov ecx,[edx+ValueDefinition.value_length] + add ecx,esi + mov [line_end],ecx + mov [eax+LineEmbedding.definition],edx + inc [edx+ValueDefinition.reference_count] + or [edx+ValueDefinition.flags],VAL_IN_USE + retn + +clear_line_embeddings: +; preserves: ebx, ecx, edx, esi, edi +; note: +; when esi is said to point into preprocessed line or current embedded value, it must be between [line_start] and [line_end]; +; these two pointers change upon entering a symbolic value of evaluated identifier (with embed_symbolic_value) +; and are restored when warp_to_next_symbol goes past the end of that embedded value; +; this function needs to be called before setting up a new line for the assembly +; and it discards the stack of stored [line_start] and [line_end] boundaries + xor eax,eax + mov [line_start],eax + mov [line_end],eax + mov [embedded_context],eax + cmp eax,[number_of_line_embeddings] + je embedded_values_ok + push ecx + discard_line_embedding: + mov ecx,eax + imul ecx,sizeof.LineEmbedding + add ecx,[line_embeddings] + mov ecx,[ecx+LineEmbedding.definition] + dec [ecx+ValueDefinition.reference_count] + and [ecx+ValueDefinition.flags],not VAL_IN_USE + inc eax + cmp eax,[number_of_line_embeddings] + jne discard_line_embedding + and [number_of_line_embeddings],0 + pop ecx + embedded_values_ok: + retn + +identify_symbol_in_namespace: +; in: +; ebx - SymbolTree_Root +; esi = pointer into preprocessed line or current embedded value +; dl = SYMCLASS_# +; [symbol_definition] = non-zero when symbol needs to be identified for the purpose of definition +; out: +; cf set when there were no more symbols in preprocessed line or current embedded value +; esi = pointer advanced past the processed identifier and following whitespace or to the first token of a different symbol +; ecx = number of whitespace tokens encountered immediately before the new position +; when cf = 0: +; ebx - SymbolTree_Leaf, null when no symbol was identified +; edx - SymbolTree_Foliage, may be null if either ebx or edi is null +; edi - SymbolTree_Root, null when there was no identifier at all or when identified symbol is not in a namespace +; [symbol_start] - first token of identified symbol +; [symbol_solid] = non-zero when initial component of identifier was a name and not a dot +; note: +; when ebx is null but edi is not, the identifier was malformed +; when edi is null but ebx is not, a special symbol was identified; this is possible only for SYMCLASS_INSTRUCTION and SYMCLASS_STRUCTURE + mov [expected_class],dl + xor ecx,ecx + call move_to_next_symbol + jnc namespaces_ok + retn +identify_symbol: +; in: +; esi = pointer into preprocessed line or current embedded value +; dl = SYMCLASS_# +; [symbol_definition] = non-zero when symbol needs to be identified for the purpose of definition +; out: +; cf set when there were no more symbols in preprocessed line or current embedded value +; esi = pointer advanced past the processed identifier and following whitespace or to the first token of a different symbol +; ecx = number of whitespace tokens encountered immediately before the new position +; [recognition_context] = applied recognition context +; when cf = 0: +; ebx - SymbolTree_Leaf, null when no symbol was identified +; edx - SymbolTree_Foliage, may be null if either ebx or edi is null +; edi - SymbolTree_Root, null when there was no identifier at all or when identified symbol is not in a namespace +; [symbol_start] - first token of identified symbol (within current embedded value or preprocessed line) +; [symbol_solid] = non-zero when initial component of identifier was a name and not a dot +; note: +; when ebx is null but edi is not, the identifier was malformed +; when edi is null but ebx is not, a special symbol was identified; this is possible only for SYMCLASS_INSTRUCTION and SYMCLASS_STRUCTURE + mov [expected_class],dl + xor ecx,ecx + call move_to_next_symbol + jc identification_finished + xor ebx,ebx + mov edx,[embedded_context] + test edx,edx + jnz use_namespace + mov edx,current_context + use_namespace: + mov eax,[edx+RecognitionContext.base_namespace] + mov [recognition_context.base_namespace],eax + mov eax,[edx+RecognitionContext.base_label] + mov [recognition_context.base_label],eax + namespaces_ok: + mov [symbol_start],esi + and [symbol_solid],0 + cmp [symbol_definition],0 + setnz al + shl al,bsf RECOGNIZE_DEFINITION + mov [recognizer_setting],al + xor edx,edx + xor edi,edi + mov al,[esi] + cmp al,1Ah + je starting_with_name + cmp al,'.' + je starting_dot + cmp al,'#' + je starting_with_concatenation + cmp al,'?' + je starting_question_mark + return_no_identifier: + xor ebx,ebx + return_no_namespace: + xor edx,edx + xor edi,edi + clc + retn + starting_with_name: + call detect_numeric_symbol + jc return_no_identifier + valid_starting_name: + or [symbol_solid],1 + valid_name: + inc esi + mov [name_token],esi + lodsd + mov edi,eax + and [name_volatile],0 + xor ecx,ecx + identify_name: + mov dh,[recognizer_setting] + call move_to_next_symbol + jc run_recognizer + cmp al,'#' + jne name_complete + call check_concatenation + jnc name_concatenation + xor al,al + name_complete: + test ecx,ecx + jnz run_recognizer + cmp al,'?' + jne run_recognizer + or dh,RECOGNIZE_CASE_INSENSITIVE + inc esi + call move_to_next_symbol + jc run_recognizer + cmp al,'#' + jne name_modifiers_complete + call check_concatenation + jc run_recognizer + name_modifiers_complete: + test ecx,ecx + jnz run_recognizer + cmp al,1Ah + je malformed_identifier + run_recognizer: + push esi ecx + cmp esi,[line_end] + je recognize_final_symbol + test ecx,ecx + jnz recognize_final_symbol + cmp byte [esi],'.' + jne recognize_final_symbol + mov esi,edi + lodsd + mov ecx,eax + mov dl,SYMCLASS_EXPRESSION + and dh,not RECOGNIZE_DEFINITION + call recognize_symbol + pop ecx esi + dot_operator: + inc esi + xor ecx,ecx + call move_to_next_symbol + jc ending_with_dot + cmp al,'#' + je name_concatenation_after_dot + test ecx,ecx + jnz ending_with_dot + cmp al,'.' + je multiple_dot_operator + identify_name_after_dot: + cmp al,30h + je number_after_dot + cmp al,1Ah + jne ending_with_dot + call get_symbol_namespace + xor edi,edi + jmp valid_name + name_concatenation_after_dot: + call check_concatenation + jc ending_with_dot + cmp al,'.' + je multiple_dot_operator + number_after_dot: + call get_symbol_namespace + mov al,[esi] + xor edi,edi + jmp name_concatenation + recognize_final_symbol: + mov dl,[expected_class] + mov esi,edi + lodsd + mov ecx,eax + call recognize_symbol + pop ecx esi + symbol_identified: + clc + identification_finished: + retn + ending_with_dot: + mov al,[expected_class] + cmp al,SYMCLASS_EXPRESSION + jne find_expected_class + clc + retn + starting_question_mark: + inc esi + xor ecx,ecx + call move_to_next_symbol + jc alone_question_mark + cmp al,'#' + jne symbol_after_question_mark + call check_concatenation + jc alone_question_mark + symbol_after_question_mark: + test ecx,ecx + jnz alone_question_mark + cmp [symbol_definition],0 + jne no_forced_definition + mov [expected_class],SYMCLASS_EXPRESSION + or [recognizer_setting],RECOGNIZE_DEFINITION + no_forced_definition: + cmp al,'.' + je starting_dot + cmp al,1Ah + je valid_starting_name + cmp al,30h + je concatenation_with_internal_number + alone_question_mark: + mov dl,[expected_class] + cmp dl,SYMCLASS_INSTRUCTION + je return_interceptor_symbol + cmp dl,SYMCLASS_STRUCTURE + je return_label_interceptor_symbol + mov eax,[symbol_start] + cmp byte [eax],'?' + jne malformed_identifier + mov esi,eax + jmp return_no_identifier + return_label_interceptor_symbol: + mov ebx,[label_interceptor_symbol] + jmp return_no_namespace + return_interceptor_symbol: + mov ebx,[interceptor_symbol] + jmp return_no_namespace + multiple_dot_operator: + mov ebx,edi + xor edi,edi + jmp starting_dot + additional_dot: + inc edi + starting_dot: + inc esi + xor ecx,ecx + call move_to_next_symbol + jc alone_dot + cmp al,'#' + jne symbol_after_starting_dot + call check_concatenation + jc alone_dot + symbol_after_starting_dot: + test ecx,ecx + jnz alone_dot + cmp al,'.' + je additional_dot + cmp al,30h + je name_after_starting_dot + cmp al,1Ah + jne alone_dot + name_after_starting_dot: + test ebx,ebx + jnz name_after_multiple_dot_operator + call get_base_label + mov edi,ebx + mov al,[esi] + jmp identify_name_after_dot + name_after_multiple_dot_operator: + test edx,edx + jz parent_namespace_ready + call get_symbol_namespace + parent_namespace_ready: + call synthesize_dot_label + mov edi,ebx + mov al,[esi] + jmp identify_name_after_dot + alone_dot: + test ebx,ebx + jz identify_current_label + test edx,edx + jz malformed_identifier + push ecx + call get_symbol_namespace + pop ecx + call synthesize_dot_label + jmp get_label_symbol + malformed_identifier: + xor ebx,ebx + xor edx,edx + mov edi,[recognition_context.base_namespace] + jmp symbol_identified + identify_current_label: + call get_base_label + get_label_symbol: + mov edi,ebx + mov al,[expected_class] + find_expected_class: + mov [symbol_class],al + mov al,[symbol_definition] + mov [symbol_required],al + mov [symbol_expected],al + mov ebx,edx + call scan_symbol_branch + jnc current_label_identified + mov [symbol_class],SYMCLASS_EXPRESSION + or [symbol_required],1 + or [symbol_expected],1 + mov ebx,edx + call scan_symbol_branch + current_label_identified: + clc + retn + get_base_label: + mov edx,[recognition_context.base_label] + test edi,edi + jnz synthesize_dot_label + test edx,edx + jnz current_label_ready + synthesize_dot_label: + push ecx esi + mov esi,[identifier_workspace.memory_start] + mov eax,edi + mov [esi],eax + mov edx,FNV_OFFSET + xor ecx,ecx + hash_dot_label: + test eax,eax + jz dot_label_synthesised + xor dl,al + imul edx,FNV_PRIME + inc ecx + shr eax,8 + jmp hash_dot_label + dot_label_synthesised: + or [name_volatile],1 + and [name_token],0 + mov [name_kind],NAME_NUMERIC + mov [symbol_class],SYMCLASS_EXPRESSION + or [symbol_required],1 + or [symbol_expected],1 + test ebx,ebx + jnz identify_dot_label + mov ebx,[recognition_context.base_namespace] + identify_dot_label: + call scan_namespace + pop esi ecx + current_label_ready: + mov ebx,[edx+SymbolTree_Foliage.root] + retn + starting_with_concatenation: + xor ecx,ecx + call check_concatenation + jc empty_concatenation + name_concatenation: + cmp al,30h + je concatenation_with_internal_number + cmp al,1Ah + jne empty_concatenation + test edi,edi + jz starting_with_name + inc esi + lodsd + push ebx esi + mov ebx,eax + attach_to_name: + mov esi,edi + mov edi,[identifier_workspace.memory_start] + cmp esi,edi + jne initial_concatenation + lodsd + add esi,eax + mov edi,esi + jmp get_precalculated_hashes + initial_concatenation: + test esi,esi + jz initial_conversion + mov ecx,[esi] + add ecx,4+8 + mov edx,identifier_workspace + call reserve_workspace + lodsd + stosd + mov ecx,eax + rep movsb + get_precalculated_hashes: + xchg ebx,esi + mov ecx,[esi] + mov eax,[identifier_workspace.memory_start] + add [eax],ecx + add ecx,8 + mov edx,identifier_workspace + call reserve_workspace + mov ecx,[ebx] + mov edx,[ebx+4] + lodsd + lea ebx,[esi+eax] + xor eax,eax + concatenation_hash: + lodsb + xor cl,al + xor dl,[characters+eax] + imul ecx,FNV_PRIME + imul edx,FNV_PRIME + stosb + cmp esi,ebx + jne concatenation_hash + mov eax,ecx + stosd + mov eax,edx + stosd + pop esi ebx + mov edi,[identifier_workspace.memory_start] + or [name_volatile],1 + and [name_token],0 + xor ecx,ecx + jmp identify_name + initial_conversion: + xor eax,eax + stosd + mov esi,edi + mov eax,FNV_OFFSET + mov [esi],eax + mov [esi+4],eax + jmp get_precalculated_hashes + concatenation_with_internal_number: + inc esi + mov edx,esi + lodsd + add esi,eax + push ebx esi + push edi + call convert_number_back + pop edi + mov ebx,edx + jmp attach_to_name + empty_concatenation: + test edi,edi + jnz identify_name_with_empty_concatenation + call move_to_next_symbol + jc malformed_identifier + cmp al,'.' + je starting_dot + cmp al,'?' + je starting_question_mark + jmp malformed_identifier + identify_name_with_empty_concatenation: + mov dh,[recognizer_setting] + call move_to_next_symbol + jc run_recognizer + jmp name_complete + +detect_numeric_symbol: +; in: +; esi - name token in preprocessed line or in current embedded value +; out: +; cf set when symbol starting with this token should be considered numeric +; preserves: ebx, ecx, edx, esi, edi + mov eax,[esi+1] + mov al,[eax+4] + cmp al,'$' + je detect_pascal_hexadecimal + sub al,'0' + cmp al,10 + jb numeric_symbol_detected + not_a_numeric_symbol: + clc + retn + detect_pascal_hexadecimal: + mov eax,[esi+1] + cmp dword [eax],2 + jb not_a_numeric_symbol + movzx eax,byte [eax+4+1] + mov al,[characters+eax] + sub al,'0' + cmp al,10 + jb numeric_symbol_detected + sub al,'a'-'0' + jc not_a_numeric_symbol + cmp al,16-10 + jae not_a_numeric_symbol + numeric_symbol_detected: + stc + retn + +check_concatenation: +; in: +; esi - concatenation character in preprocessed line or in current embedded value +; ecx = number of whitespace tokens encountered immediately before current position +; out: +; cf set when there is no concatenation applicable to previous symbol +; esi = pointer advanced past the processed operator and the whitespace that follows it +; ecx = number of whitespace tokens encountered immediately before the new position +; when cf = 0: +; esi - symbol that follows concatenation operator +; al = initial byte of symbol following concatenation operator +; preserves: ebx, edx, edi + test ecx,ecx + jnz no_concatenation + inc esi + call move_to_next_symbol + jc no_concatenation + cmp al,'#' + je check_concatenation + test ecx,ecx + jnz no_concatenation + ; clc + retn + no_concatenation: + stc + retn + +get_literal: +; in: +; esi - token in preprocessed line or in current embedded value +; out: +; al = type of value +; esi = pointer advanced past the processed literal and the whitespace that follows it +; ecx = number of whitespace tokens encountered immediately before the new position +; when al = 22h: +; edx - 32-bit length followed by string data +; when al = 30h: +; edx - 32-bit length followed by numeric data, null when invalid number +; when al = 2Eh: +; edx - FloatData, null when invalid number +; when al is any other value, it is a simple special character, and edx is zero + xor ecx,ecx + mov al,[esi] + cmp al,1Ah + je get_literal_number + cmp al,22h + je get_literal_string + cmp al,27h + je missing_end_quote + cmp al,30h + je get_internal_number + inc esi + mov dl,al + call move_to_next_symbol + mov al,dl + xor edx,edx + retn + get_literal_number: + mov edx,[esi+1] + add esi,5 + check_for_more_parts_of_number: + call move_to_next_symbol + jc number_ready_for_conversion + test ecx,ecx + jnz check_for_number_concatenation + cmp al,'.' + je get_literal_float + check_for_number_concatenation: + cmp al,'#' + jne number_ready_for_conversion + call check_concatenation + jc number_ready_for_conversion + number_concatenation: + cmp al,30h + je attach_internal_number_to_number + cmp al,'.' + je get_literal_float + cmp al,1Ah + jne number_ready_for_conversion + attach_name_to_number: + mov ebx,[esi+1] + add esi,5 + push esi + attach_to_number: + mov esi,edx + mov edi,[identifier_workspace.memory_start] + cmp esi,edi + jne initial_concatenation_of_number + lodsd + add esi,eax + mov edi,esi + jmp attach_segment_of_number + initial_concatenation_of_number: + mov ecx,[esi] + add ecx,4 + mov edx,identifier_workspace + call reserve_workspace + lodsd + stosd + mov ecx,eax + rep movsb + attach_segment_of_number: + mov esi,ebx + mov ecx,[esi] + mov eax,[identifier_workspace.memory_start] + add [eax],ecx + mov edx,identifier_workspace + call reserve_workspace + lodsd + mov ecx,eax + rep movsb + pop esi + mov edx,[identifier_workspace.memory_start] + jmp check_for_more_parts_of_number + attach_internal_number_to_number: + mov ebx,edx + inc esi + mov edx,esi + lodsd + add esi,eax + push esi + push ebx + call convert_number_back + mov ebx,edx + pop edx + jmp attach_to_number + number_ready_for_conversion: + push ecx + call convert_number + pop ecx + jc get_literal_float + mov al,30h + retn + missing_end_quote: + mov edx,_missing_end_quote + call register_error + get_literal_string: + mov edx,[esi+1] + add esi,5 + call move_to_next_symbol + mov al,22h + retn + get_internal_number: + lea edx,[esi+1] + mov eax,[edx] + lea esi,[esi+1+4+eax] + call move_to_next_symbol + jc internal_number_ready + test ecx,ecx + jnz check_for_internal_number_concatenation + cmp al,'.' + je convert_internal_number_back + check_for_internal_number_concatenation: + cmp al,'#' + jne internal_number_ready + call check_concatenation + jc internal_number_ready + cmp al,1Ah + je convert_internal_number_back + cmp al,30h + jne internal_number_ready + convert_internal_number_back: + push esi + call convert_number_back + mov esi,edx + mov ecx,[esi] + add ecx,4 + mov edi,[identifier_workspace.memory_start] + mov edx,identifier_workspace + call reserve_workspace + lodsd + stosd + mov ecx,eax + rep movsb + pop esi + mov edx,[identifier_workspace.memory_start] + mov al,[esi] + jmp number_concatenation + internal_number_ready: + mov al,30h + retn + get_literal_float: + xor eax,eax + mov [zero_digits],eax + mov [decimal_places],eax + mov [literal_exponent],eax + mov [literal_exponent_sign],al + mov [literal_fractional_part],al + mov [waiting_for_digit],al + mov [float_literal_status],al + push esi ecx + mov ebx,edx + call start_decimal_converter + lea esi,[ebx+4] + mov ecx,[ebx] + get_float_digit: + lodsb + cmp al,27h + je skip_float_digit + cmp al,'_' + je skip_float_digit + cmp al,'9' + ja float_nondigit + sub al,'0' + jz float_digit_zero + jc float_nondigit + test [float_literal_status],1 + jnz reject_float_digit + inc [decimal_places] + push esi ecx + xor ecx,ecx + xchg ecx,[zero_digits] + call convert_decimal_digit + pop ecx esi + and [waiting_for_digit],0 + skip_float_digit: + loop get_float_digit + jmp float_digits_ok + float_digit_zero: + test [float_literal_status],1 + jnz reject_float_digit + inc [decimal_places] + inc [zero_digits] + and [waiting_for_digit],0 + loop get_float_digit + float_digits_ok: + pop ecx esi + call move_to_next_symbol + jc no_more_tokens_in_float + cmp al,'.' + je decimal_point + cmp al,'#' + jne no_more_tokens_in_float + call check_concatenation + jc no_more_tokens_in_float + jmp next_token_in_float + decimal_point: + test ecx,ecx + jnz no_more_tokens_in_float + mov al,[literal_fractional_part] + or al,[literal_exponent_sign] + jz decimal_point_allowed + or [float_literal_status],-1 + decimal_point_allowed: + or [literal_fractional_part],1 + or [waiting_for_digit],1 + and [decimal_places],0 + get_following_digits: + inc esi + xor ecx,ecx + call move_to_adjacent_symbol + jc invalid_float_value + next_token_in_float: + cmp al,1Ah + je next_digits_section + cmp al,30h + je internal_number_as_digits + no_more_tokens_in_float: + cmp [waiting_for_digit],0 + jne invalid_float_value + cmp [float_literal_status],-1 + je invalid_float_value + call finish_decimal_conversion + push esi ecx + mov esi,edi + xor ecx,ecx + cmp [literal_fractional_part],0 + je float_decimal_places_ok + mov ecx,[decimal_places] + float_decimal_places_ok: + sub ecx,[zero_digits] + neg ecx + add ecx,[literal_exponent] + jo float_conversion_failed + call multiply_float_by_power_of_ten + test [edi+FloatData.attributes],FLOAT_INFINITE or FLOAT_INDETERMINATE or FLOAT_UNDERFLOW + jnz float_conversion_failed + mov edx,edi + pop ecx esi + mov al,2Eh + retn + float_conversion_failed: + pop ecx esi + invalid_float_value: + xor edx,edx + mov al,2Eh + retn + next_digits_section: + inc esi + lodsd + xor ecx,ecx + push esi ecx + mov esi,eax + lodsd + mov ecx,eax + cmp [literal_exponent_sign],0 + jne get_exponent_digit + jmp get_float_digit + internal_number_as_digits: + inc esi + mov edx,esi + lodsd + add esi,eax + xor ecx,ecx + push esi ecx + call convert_number_back + mov esi,edx + lodsd + mov ecx,eax + cmp [literal_exponent_sign],0 + jne get_exponent_digit + jmp get_float_digit + get_literal_exponent: + mov al,1 + xchg al,[literal_exponent_sign] + test al,al + jnz reject_exponent_digit + or [waiting_for_digit],1 + loop get_exponent_digit + pop ecx esi + call move_to_adjacent_symbol + jc invalid_float_value + cmp al,1Ah + je next_digits_section + cmp al,30h + je internal_number_as_digits + cmp al,'+' + je get_following_digits + cmp al,'-' + jne invalid_float_value + neg [literal_exponent_sign] + jmp get_following_digits + get_exponent_digit: + xor eax,eax + lodsb + cmp al,27h + je skip_exponent_digit + cmp al,'_' + je skip_exponent_digit + sub al,'0' + jc reject_exponent_digit + cmp al,10 + jae reject_exponent_digit + test [float_literal_status],1 + jnz reject_exponent_digit + mov edx,[literal_exponent] + imul edx,10 + jo reject_exponent_digit + cmp [literal_exponent_sign],0 + jnl exponent_digit_ok + neg eax + exponent_digit_ok: + add edx,eax + jo reject_exponent_digit + mov [literal_exponent],edx + and [waiting_for_digit],0 + skip_exponent_digit: + loop get_exponent_digit + jmp float_digits_ok + reject_exponent_digit: + or [float_literal_status],-1 + jmp skip_exponent_digit + float_nondigit: + cmp [waiting_for_digit],0 + jne reject_float_digit + movzx eax,byte [esi-1] + mov al,[characters+eax] + cmp al,'e' + je get_literal_exponent + cmp al,'f' + jne reject_float_digit + or [float_literal_status],1 + jmp skip_float_digit + reject_float_digit: + or [float_literal_status],-1 + jmp skip_float_digit + move_to_adjacent_symbol: + call move_to_next_symbol + jc adjacent_symbol_ok + cmp al,'#' + je check_concatenation + neg ecx + adjacent_symbol_ok: + retn + +skip_literal: +; in: +; esi - token in preprocessed line or in current embedded value +; out: +; esi = pointer advanced past the processed literal and the whitespace that follows it +; ecx = number of whitespace tokens encountered immediately before the new position + xor ecx,ecx + mov al,[esi] + cmp al,1Ah + je skip_literal_number + cmp al,22h + je skip_literal_string + cmp al,27h + je skip_literal_string + cmp al,30h + je skip_internal_number + inc esi + call move_to_next_symbol + retn + skip_literal_string: + add esi,5 + call move_to_next_symbol + retn + skip_literal_number: + xor dh,dh + inc esi + lodsd + mov dl,[eax+4] + cmp dl,'0' + jb skip_number_segments + cmp dl,'9' + ja skip_number_segments + or dh,1 + check_for_possible_exponent: + mov ecx,[eax] + mov dl,[eax+4+ecx-1] + cmp dl,'e' + je possible_exponent + cmp dl,'E' + jne skip_number_segments + possible_exponent: + or dh,2 + jmp skip_number_segments + skip_internal_number: + inc esi + lodsd + add esi,eax + mov dh,1 + skip_number_segments: + xor ecx,ecx + call move_to_next_symbol + jc literal_symbol_skipped + cmp al,'#' + je check_literal_concatenation + test ecx,ecx + jnz literal_symbol_skipped + cmp al,'.' + jne check_for_exponent + skip_decimal_point: + inc esi + xor ecx,ecx + call move_to_next_symbol + jc literal_symbol_skipped + cmp al,'#' + je check_literal_concatenation + test ecx,ecx + jnz literal_symbol_skipped + skip_attached_segment: + cmp al,'.' + je skip_decimal_point + cmp al,30h + je skip_attached_internal_number + cmp al,1Ah + jne check_for_exponent + skip_attached_number: + inc esi + lodsd + test dh,1 + jnz check_for_possible_exponent + jmp skip_number_segments + skip_attached_internal_number: + inc esi + lodsd + add esi,eax + and dh,not 2 + jmp skip_number_segments + check_for_exponent: + cmp dh,1+2 + jne literal_symbol_skipped + cmp al,'+' + je skip_exponent + cmp al,'-' + jne literal_symbol_skipped + skip_exponent: + xor dh,dh + jmp skip_decimal_point + check_literal_concatenation: + call check_concatenation + jnc skip_attached_segment + literal_symbol_skipped: + retn + +get_processed_value: +; in: +; esi = pointer into preprocessed line or current embedded value +; out: +; cf set when there were no more symbols on the line +; when cf = 0: +; al = type of value +; esi = pointer advanced past the processed element and the whitespace that follows it within the boundaries of current embedded value or preprocessed line +; ecx = number of whitespace tokens encountered immediately before the new position +; when al = 1Ah: +; ebx - SymbolTree_Leaf, null when malformed identifier +; edx - ValueDefinition, null when symbol with no valid value +; additional variables set as by identify_symbol +; when al = 22h: +; edx - 32-bit length followed by string data +; when al = 30h: +; edx - 32-bit length followed by numeric data, null when invalid number +; when al = 2Eh: +; edx - FloatData, null when invalid number +; when al is any other value, it is a simple special character, and edx is zero +; note: +; to detect whitespace between two consecutive symbols, warp_to_next_symbol should be called after this function, +; since it may further increase ecx when there is additional whitespace outside of current embedded value +; when [use_raw_values] flag is set, this function is identical to get_raw_value + call get_raw_value + jc no_more_values + cmp al,1Ah + jne value_ok + test edx,edx + jz value_ok + cmp [edx+ValueDefinition.type],VALTYPE_SYMBOLIC + je symbolic_value + value_ok: + clc + retn + symbolic_value: + cmp [use_raw_values],0 + jne value_ok + mov eax,[current_pass] + mov [ebx+SymbolTree_Leaf.last_use_pass],eax + test [ebx+SymbolTree_Leaf.flags],SYM_VARIABLE + jnz no_infinite_regress + test [edx+ValueDefinition.flags],VAL_IN_USE + jz no_infinite_regress + push edx + mov edx,_symbolic_self_reference + call register_error + pop edx + retn + no_infinite_regress: + call embed_symbolic_value + jmp get_processed_value + +get_raw_value: +; same as get_processed_value, but it does not expand symbolic value and returns it just like any other + call warp_to_next_symbol + jc no_more_values + and [symbol_definition],0 + mov dl,SYMCLASS_EXPRESSION + call identify_symbol + jc get_raw_value + test edi,edi + jz literal_value + mov edi,edx + test ebx,ebx + jz invalid_symbol_value + call get_available_value + jc invalid_symbol_value + mov al,1Ah + clc + retn + invalid_symbol_value: + xor edx,edx + mov al,1Ah + clc + retn + literal_value: + call get_literal + clc + retn + no_more_values: + ; stc + retn diff --git a/x86_64_sse2_x87/fasm/source/calm.inc b/x86_64_sse2_x87/fasm/source/calm.inc new file mode 100644 index 0000000..0436a24 --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/calm.inc @@ -0,0 +1,2604 @@ + +struct CompiledMacroHeader + shared_namespace dd ? + literals_offset dd ? + label_argument_leaf dd ? +ends + +struct CompiledMacroArgument + symbol_leaf dd ? + default_value_length dd ? + default_value_offset dd ? +ends + +struct UnresolvedJump + offset dd ? + entry_length dd ? + ; source_context SourceContext +ends + +struct MatchedExcerpt + data_start dd ? + data_end dd ? + recognition_context dd ? + symbol_leaf dd ? +ends + +BOOL_NEG = -1 +BOOL_AND = -2 +BOOL_OR = -3 + +define_calm_instruction: + mov dl,DBLOCK_CALMINSTRUCTION + mov ecx,sizeof.MacroData + call add_directive_block + mov edx,[ebx+SymbolTree_Leaf.branch] + call get_symbol_namespace + mov [alm_namespace],ebx + or [assembly_mode],AMODE_CALM_DEFINITION + mov [calm_definition_active],0 + test [assembly_mode],AMODE_SKIP + jnz assembly_line + test [assembly_mode],AMODE_DEFINITION + jnz add_line_to_macro + mov eax,[calm_code_buffer.memory_start] + mov [calm_code_cursor],eax + mov eax,[calm_literals_buffer.memory_start] + mov [calm_literals_cursor],eax + mov eax,[calm_auxiliary_buffer.memory_start] + mov [calm_auxiliary_cursor],eax + call move_to_next_symbol + jc missing_argument + cmp al,'(' + je calm_labeled_instruction + and [argument_start],0 + mov dl,SYMCLASS_INSTRUCTION + get_calm_instruction_identifier: + call get_macro_definition_symbol + jc invalid_identifier + push esi + mov eax,ebx + mov ecx,[alm_namespace] + call get_local_anchor + call get_symbol_namespace + or [ebx+SymbolTree_Root.flags],NAMESPACE_CALM + mov [new_local_namespace],ebx + pop esi + mov edi,[calm_code_buffer.memory_start] + mov [edi+CompiledMacroHeader.shared_namespace],ebx + or [symbol_definition],1 + mov ebx,[argument_start] + test ebx,ebx + jz calm_instruction_label_argument_ok + push esi edi + mov esi,ebx + mov dl,SYMCLASS_EXPRESSION + mov ebx,[new_local_namespace] + call identify_symbol_in_namespace + mov eax,esi + pop edi esi + jc invalid_argument + test ebx,ebx + jz invalid_argument + cmp byte [eax],')' + jne invalid_argument + call update_value_definition + push esi edi + mov [value_type],VALTYPE_RESERVED + xor ecx,ecx + call assign_value + pop edi esi + calm_instruction_label_argument_ok: + mov [edi+CompiledMacroHeader.label_argument_leaf],ebx + add edi,sizeof.CompiledMacroHeader + calm_instruction_argument_declaration: + call move_to_next_symbol + jc calm_instruction_arguments_declared + mov dl,SYMCLASS_EXPRESSION + push edi + mov ebx,[new_local_namespace] + call identify_symbol_in_namespace + pop edi + jc invalid_argument + test ebx,ebx + jz invalid_argument + mov edx,calm_code_buffer + mov ecx,sizeof.CompiledMacroArgument+4 + call reserve_workspace + mov [edi+CompiledMacroArgument.symbol_leaf],ebx + call update_value_definition + push esi edi + mov [value_type],VALTYPE_RESERVED + xor ecx,ecx + call assign_value + pop edi esi + xor edx,edx + call move_to_next_symbol + jc calm_instruction_argument_ready + cmp al,':' + je calm_instruction_argument_default_value + cmp al,'=' + je calm_instruction_argument_default_value + cmp al,'*' + jne calm_instruction_argument_ready + dec edx + inc esi + calm_instruction_argument_ready: + mov [edi+CompiledMacroArgument.default_value_length],edx + add edi,sizeof.CompiledMacroArgument + call move_to_next_symbol + jc calm_instruction_arguments_declared + inc esi + cmp al,'&' + je calm_instruction_greedy_argument + cmp al,',' + jne invalid_argument + call move_to_next_symbol + jc invalid_argument + jmp calm_instruction_argument_declaration + calm_instruction_argument_default_value: + inc esi + mov [calm_code_cursor],edi + mov edx,calm_literals_buffer + mov edi,[calm_literals_cursor] + mov ecx,[line_end] + sub ecx,esi + add ecx,1+sizeof.RecognitionContext + call reserve_workspace + mov [calm_literals_cursor],edi + call move_to_next_symbol + jc calm_plain_default_value + cmp al,'<' + je calm_enclosed_default_value + calm_plain_default_value: + mov dl,',' + xor dh,dh + mov [breakpoint_token],'&' + call extract_piece_of_line + jmp calm_default_value_ready + calm_enclosed_default_value: + inc esi + mov dl,'>' + mov dh,'<' + mov [breakpoint_token],0 + call extract_piece_of_line + cmp al,'>' + jne invalid_argument + inc esi + calm_default_value_ready: + mov edx,edi + xchg edi,[calm_literals_cursor] + mov eax,edi + sub edx,edi + sub eax,[calm_literals_buffer.memory_start] + mov edi,[calm_code_cursor] + mov [edi+CompiledMacroArgument.default_value_offset],eax + jmp calm_instruction_argument_ready + calm_instruction_greedy_argument: + or eax,-1 + stosd + jmp call_instruction_begin_code + calm_labeled_instruction: + inc esi + call move_to_next_symbol + jc missing_argument + mov [argument_start],esi + mov edi,[expression_workspace.memory_start] + mov dl,')' + xor dh,dh + mov [breakpoint_token],dh + call cut_piece_of_line + cmp al,')' + jne invalid_argument + inc esi + mov dl,SYMCLASS_STRUCTURE + jmp get_calm_instruction_identifier + calm_instruction_arguments_declared: + xor eax,eax + stosd + call_instruction_begin_code: + mov [calm_code_cursor],edi + mov eax,[new_local_namespace] + mov [current_context.base_namespace],eax + and [calm_line_number],0 + mov [calm_definition_active],1 + jmp instruction_assembled + +assemble_alm_instruction: + or [alm_statement],1 + mov ebx,[source_context] + mov eax,[ebx+SourceContext.number_of_entries] + dec eax + imul eax,sizeof.SourceEntry + test [ebx+sizeof.SourceContext+eax+SourceEntry.flags],SRCF_ALM_STATEMENT + jnz identify_alm_instruction + inc [calm_line_number] + identify_alm_instruction: + call move_to_next_symbol + jc empty_line + call get_alm_identifier + jc unrecognized_alm_instruction + mov dl,SYMCLASS_INSTRUCTION + call identify_alm_symbol + jc alm_label + call get_available_value + jc alm_label + test [edx+ValueDefinition.flags],VAL_UNCONDITIONAL + jnz execute_alm_instruction + test [assembly_mode],AMODE_SKIP + jnz assembly_line + test [assembly_mode],AMODE_DEFINITION + jnz add_line_to_macro + execute_alm_instruction: + cmp [edx+ValueDefinition.type],VALTYPE_CALM + je launch_calm + cmp [edx+ValueDefinition.type],VALTYPE_SYMBOLIC + je use_macro + cmp [edx+ValueDefinition.type],VALTYPE_NATIVE_COMMAND + jne unrecognized_alm_instruction + jmp [edx+ValueDefinition.value] + alm_label: + call move_to_next_symbol + jc unrecognized_alm_instruction + cmp al,':' + jne unrecognized_alm_instruction + inc esi + test [assembly_mode],AMODE_SKIP + jnz identify_alm_instruction + test [assembly_mode],AMODE_DEFINITION + jnz add_label_to_macro + call identify_calm_location + jc unrecognized_alm_instruction + call create_constant_value_definition + test edx,edx + jz assembly_line + mov [edx+ValueDefinition.type],VALTYPE_PLAIN + mov eax,[calm_code_cursor] + sub eax,[calm_code_buffer.memory_start] + mov [edx+ValueDefinition.value],eax + mov eax,[current_pass] + mov [edx+ValueDefinition.pass],eax + jmp identify_alm_instruction + unrecognized_alm_instruction: + test [assembly_mode],AMODE_SKIP + jnz assembly_line + test [assembly_mode],AMODE_DEFINITION + jnz add_line_to_macro + mov edx,_unexpected_instruction + call register_error + jmp assembly_line + get_alm_identifier: + xor ecx,ecx + call move_to_next_symbol + jc malformed_alm_identifier + cmp al,1Ah + je alm_plain_name + cmp al,30h + je alm_numeric_name + cmp al,'#' + jne malformed_alm_identifier + call check_concatenation + jnc get_alm_identifier + malformed_alm_identifier: + stc + retn + alm_plain_name: + inc esi + lodsd + mov ebx,eax + mov ecx,[ebx] + mov eax,[ebx+4+ecx] + mov [case_sensitive_hash],eax + mov eax,[ebx+4+ecx+4] + mov [case_insensitive_hash],eax + alm_check_for_concatenation: + xor ecx,ecx + call move_to_next_symbol + jc alm_identifier_ok + cmp al,'#' + jne alm_identifier_ok + call check_concatenation + jc alm_identifier_ok + call move_to_next_symbol + jc alm_identifier_ok + cmp al,1Ah + je alm_concatenation + cmp al,30h + je alm_concatenation + alm_identifier_ok: + mov [symbol_data],ebx + cmp ebx,[assembly_workspace.memory_start] + je alm_identifier_volatile + mov [name_volatile],0 + clc + retn + alm_identifier_volatile: + sub edi,ebx + sub edi,4 + mov [ebx],edi + mov [name_volatile],1 + clc + retn + alm_concatenation: + mov edx,assembly_workspace + cmp ebx,[edx+Workspace.memory_start] + je alm_name_segment + mov edi,[edx+Workspace.memory_start] + add edi,4 + mov ecx,[ebx] + call reserve_workspace + xchg ebx,esi + lodsd + mov ecx,eax + rep movsb + mov esi,ebx + mov ebx,[edx+Workspace.memory_start] + alm_name_segment: + lodsb + cmp al,30h + je alm_attach_numeric_segment + lodsd + push ebx esi + alm_append_name: + mov esi,eax + mov ecx,[esi] + mov edx,assembly_workspace + call reserve_workspace + lodsd + lea ebx,[esi+eax] + mov ecx,[case_sensitive_hash] + mov edx,[case_insensitive_hash] + xor eax,eax + alm_name_hash: + lodsb + xor cl,al + xor dl,[characters+eax] + imul ecx,FNV_PRIME + imul edx,FNV_PRIME + stosb + cmp esi,ebx + jne alm_name_hash + mov [case_sensitive_hash],ecx + mov [case_insensitive_hash],edx + pop esi ebx + jmp alm_check_for_concatenation + alm_numeric_name: + inc esi + mov ebx,[assembly_workspace.memory_start] + lea edi,[ebx+4] + mov eax,FNV_OFFSET + mov [case_sensitive_hash],eax + mov [case_insensitive_hash],eax + alm_attach_numeric_segment: + mov edx,esi + mov ecx,[edx] + lea esi,[esi+4+ecx] + push ebx esi + push edi + call convert_number_back + pop edi + mov eax,edx + jmp alm_append_name + identify_alm_symbol: + mov [symbol_class],dl + push esi + mov esi,[symbol_data] + lodsd + mov ecx,eax + mov edx,[case_insensitive_hash] + mov ebx,[alm_namespace] + mov [name_kind],NAME_CASEINSENSITIVE + and [name_token],0 + and [symbol_required],0 + and [symbol_expected],0 + call scan_namespace + pop esi + retn + identify_calm_location: + mov [symbol_class],SYMCLASS_CALM_LOCATION + push esi + mov esi,[symbol_data] + lodsd + mov ecx,eax + mov edx,[case_sensitive_hash] + mov ebx,[current_context.base_namespace] + and [name_kind],0 + and [name_token],0 + or [symbol_required],1 + or [symbol_expected],1 + call scan_namespace + pop esi + retn + +alm_jyes: + mov [value],calm_jyes + jmp assemble_alm_jump +alm_jno: + mov [value],calm_jno + jmp assemble_alm_jump +alm_jump: + mov [value],calm_jump + assemble_alm_jump: + test [assembly_mode],AMODE_CALM_DEFINITION + jz unexpected_instruction + call move_to_next_symbol + jc missing_argument + call get_alm_identifier + jc invalid_argument + call identify_calm_location + jc invalid_argument + mov edx,[ebx+SymbolTree_Leaf.definition] + test edx,edx + jz alm_unresolved_jump + mov eax,[edx+ValueDefinition.pass] + cmp eax,[current_pass] + jne alm_unresolved_jump + mov ebx,[edx+ValueDefinition.value] + emit_calm_jump: + mov edi,[calm_code_cursor] + mov edx,calm_code_buffer + mov ecx,8+4 + call reserve_workspace + mov eax,[calm_line_number] + stosd + mov eax,[value] + stosd + mov eax,ebx + stosd + mov [calm_code_cursor],edi + jmp instruction_assembled + alm_unresolved_jump: + push esi + mov edx,calm_auxiliary_buffer + mov edi,[calm_auxiliary_cursor] + mov esi,[source_context] + mov ecx,[esi+SourceContext.number_of_entries] + imul ecx,sizeof.SourceEntry + add ecx,sizeof.SourceContext + add ecx,sizeof.UnresolvedJump + call reserve_workspace + mov [calm_auxiliary_cursor],edi + mov eax,[calm_code_cursor] + add eax,8 + sub eax,[calm_code_buffer.memory_start] + mov [edi+UnresolvedJump.offset],eax + add edi,sizeof.UnresolvedJump + call clone_source_context + mov ecx,edi + xchg edi,[calm_auxiliary_cursor] + sub ecx,edi + mov [edi+UnresolvedJump.entry_length],ecx + pop esi + jmp emit_calm_jump + +alm_end: + test [assembly_mode],AMODE_CALM_DEFINITION + jz unexpected_unconditional_alm_instruction + call move_to_next_symbol + jc finish_calm_definition + call get_alm_identifier + jc invalid_argument + mov dl,SYMCLASS_STRUCTURE + call identify_alm_symbol + jc invalid_argument + mov edx,[ebx+SymbolTree_Leaf.definition] + cmp [edx+ValueDefinition.type],VALTYPE_NATIVE_COMMAND + jne invalid_argument + cmp [edx+ValueDefinition.value],alm_end + jne invalid_argument + finish_calm_definition: + mov dl,DBLOCK_CALMINSTRUCTION + call find_directive_block + jc unexpected_unconditional_alm_instruction + call close_directive_block + and [assembly_mode],not AMODE_CALM_DEFINITION + test [assembly_mode],AMODE_DEFINITION + jnz add_line_to_macro + cmp [calm_definition_active],0 + je instruction_assembled + push esi + mov esi,[calm_auxiliary_buffer.memory_start] + resolve_calm_jumps: + cmp esi,[calm_auxiliary_cursor] + je calm_jumps_resolved + mov eax,[esi+UnresolvedJump.offset] + add eax,[calm_code_buffer.memory_start] + mov ebx,[eax] + mov edx,[ebx+SymbolTree_Leaf.definition] + test edx,edx + jz unresolvable_calm_jump + mov ecx,[edx+ValueDefinition.pass] + cmp ecx,[current_pass] + jne unresolvable_calm_jump + mov ebx,[edx+ValueDefinition.value] + mov [eax],ebx + resolve_next_calm_jump: + add esi,[esi+UnresolvedJump.entry_length] + jmp resolve_calm_jumps + unresolvable_calm_jump: + mov edx,[calm_code_cursor] + sub edx,[calm_code_buffer.memory_start] + mov [eax],edx + add esi,sizeof.UnresolvedJump + mov edx,_undefined_jump_target + call register_delayed_error + sub esi,sizeof.UnresolvedJump + jmp resolve_next_calm_jump + calm_jumps_resolved: + mov ebx,[current_context.base_namespace] + mov edx,[ebx+SymbolTree_Root.parent_branch] + mov eax,[edx+SymbolTree_Foliage.root] + mov [current_context.base_namespace],eax + mov edi,[calm_code_cursor] + mov edx,calm_code_buffer + mov ecx,[calm_literals_cursor] + mov esi,[calm_literals_buffer.memory_start] + sub ecx,esi + add ecx,8 + call reserve_workspace + mov eax,[calm_line_number] + stosd + mov eax,calm_end + stosd + mov ebx,[calm_code_buffer.memory_start] + mov eax,edi + sub eax,ebx + mov [ebx+CompiledMacroHeader.literals_offset],eax + mov ecx,[calm_literals_cursor] + sub ecx,esi + rep movsb + mov ebx,[macro_leaf] + test ebx,ebx + jz calm_definition_done + call create_value_definition + test edx,edx + jz calm_definition_done + mov esi,[calm_code_buffer.memory_start] + mov ecx,edi + sub ecx,esi + mov [value_type],VALTYPE_CALM + call assign_value + mov al,[macro_flags] + or [edx+ValueDefinition.flags],al + calm_definition_done: + pop esi + jmp instruction_assembled + unexpected_unconditional_alm_instruction: + test [assembly_mode],AMODE_SKIP + jnz assembly_line + test [assembly_mode],AMODE_DEFINITION + jnz add_line_to_macro + jmp unexpected_instruction + +alm_local: + test [assembly_mode],AMODE_CALM_DEFINITION + jz unexpected_instruction + call move_to_next_symbol + jc missing_argument + or [symbol_definition],1 + mov dl,SYMCLASS_EXPRESSION + mov ebx,[current_context.base_namespace] + call identify_symbol_in_namespace + jc invalid_argument + test ebx,ebx + jz invalid_argument + call update_value_definition + test edx,edx + jz alm_next_local + push esi + mov [value_type],VALTYPE_RESERVED + xor ecx,ecx + call assign_value + pop esi + alm_next_local: + call move_to_next_symbol + jc instruction_assembled + cmp al,',' + jne invalid_argument + inc esi + jmp alm_local + +alm_assemble: + mov [value],calm_assemble + jmp assemble_alm_operation_on_variable +alm_stringify: + mov [value],calm_stringify + assemble_alm_operation_on_variable: + test [assembly_mode],AMODE_CALM_DEFINITION + jz unexpected_instruction + and [symbol_definition],0 + mov dl,SYMCLASS_EXPRESSION + call identify_symbol + jc invalid_identifier + test ebx,ebx + jz invalid_identifier + mov edi,[calm_code_cursor] + mov edx,calm_code_buffer + mov ecx,8+4 + call reserve_workspace + mov eax,[calm_line_number] + stosd + mov eax,[value] + stosd + mov eax,ebx + stosd + mov [calm_code_cursor],edi + jmp instruction_assembled + +alm_arrange: + test [assembly_mode],AMODE_CALM_DEFINITION + jz unexpected_instruction + and [symbol_definition],0 + mov dl,SYMCLASS_EXPRESSION + call identify_symbol + jc invalid_identifier + test ebx,ebx + jz invalid_identifier + call move_to_next_symbol + jc missing_argument + cmp al,',' + jne instruction_assembled + inc esi + call move_to_next_symbol + mov edi,[calm_code_cursor] + mov edx,calm_code_buffer + mov ecx,8+12 + call reserve_workspace + mov eax,[calm_line_number] + stosd + mov eax,calm_arrange + stosd + mov eax,ebx + stosd + mov edx,calm_literals_buffer + mov ebx,[calm_literals_cursor] + mov eax,ebx + sub eax,[edx+Workspace.memory_start] + stosd + mov [calm_code_cursor],edi + mov edi,ebx + mov ecx,[line_end] + sub ecx,esi + shl ecx,1 + call reserve_workspace + mov [breakpoint_token],0 + call parse_pattern + mov [calm_literals_cursor],edi + mov eax,edi + mov edi,[calm_code_cursor] + sub eax,[calm_literals_buffer.memory_start] + stosd + mov [calm_code_cursor],edi + jmp instruction_assembled + parse_pattern: + ; in: + ; esi - first token of the pattern in the preprocessed line + ; edi - buffer with enough space to hold twice the length of the preprocessed text left in line + ; [breakpoint_token] = initial byte of symbol that should end the pattern + ; out: + ; esi - after the processed portion of line (might be at [line_end]) + ; edi - in the buffer after the stored pattern + cmp esi,[line_end] + je pattern_parsed + lodsb + cmp al,[breakpoint_token] + je pattern_breakpoint + token_in_pattern: + cmp al,'=' + je parse_pattern_literal + cmp al,1Ah + je parse_reference_in_pattern + cmp al,40h + je ignore_context_in_pattern + cmp al,20h + je whitespace_in_pattern + stosb + literal_token_in_pattern: + cmp al,22h + je copy_pattern_data + cmp al,27h + je copy_pattern_data + cmp al,30h + jne parse_pattern + lodsd + mov ecx,eax + stosd + rep movsb + jmp parse_pattern + whitespace_in_pattern: + cmp esi,[line_end] + je pattern_parsed + lodsb + cmp al,20h + je whitespace_in_pattern + cmp al,[breakpoint_token] + je pattern_breakpoint + cmp al,40h + jne store_whitespace_in_pattern + call pass_context + jmp whitespace_in_pattern + store_whitespace_in_pattern: + mov byte [edi],20h + inc edi + jmp token_in_pattern + pattern_breakpoint: + dec esi + pattern_parsed: + retn + pass_context: + mov eax,esi + add esi,sizeof.RecognitionContext + cmp dword [eax],0 + je zero_context + mov [embedded_context],eax + retn + zero_context: + and [embedded_context],0 + retn + copy_pattern_data: + movsd + jmp parse_pattern + ignore_context_in_pattern: + call pass_context + jmp parse_pattern + ignore_context_in_pattern_literal: + call pass_context + parse_pattern_literal: + cmp esi,[line_end] + je error_in_parsed_pattern + lodsb + cmp al,40h + je ignore_context_in_pattern_literal + cmp al,20h + je parse_hard_space_in_pattern + stosb + cmp al,1Ah + je copy_pattern_data + jmp literal_token_in_pattern + parse_hard_space_in_pattern: + mov al,0A0h + stosb + jmp parse_pattern + parse_reference_in_pattern: + dec esi + call detect_numeric_symbol + jc numeric_name_in_pattern + inc esi + mov [name_token],esi + lodsd + mov dx,SYMCLASS_EXPRESSION + mov [name_volatile],0 + push esi edi + mov esi,eax + lodsd + mov ecx,eax + xor ebx,ebx + mov eax,[embedded_context] + test eax,eax + jz use_current_namespace_in_pattern + mov eax,[eax+RecognitionContext.base_namespace] + jmp namespace_in_pattern_ok + use_current_namespace_in_pattern: + mov eax,[current_context.base_namespace] + namespace_in_pattern_ok: + mov [recognition_context.base_namespace],eax + call recognize_symbol + pop edi esi + mov al,1Bh + stosb + mov eax,[name_token] + mov eax,[eax] + stosd + mov eax,ebx + stosd + jmp parse_pattern + numeric_name_in_pattern: + movsb + movsd + jmp parse_pattern + error_in_parsed_pattern: + mov edx,_invalid_argument + call register_error + jmp parse_pattern + +alm_match: + test [assembly_mode],AMODE_CALM_DEFINITION + jz unexpected_instruction + call move_to_next_symbol + jc missing_argument + mov edx,calm_literals_buffer + mov edi,[calm_literals_cursor] + mov ecx,[line_end] + sub ecx,esi + shl ecx,1 + call reserve_workspace + mov [calm_literals_cursor],edi + mov [breakpoint_token],',' + call parse_pattern + xchg edi,[calm_literals_cursor] + mov [pattern_start],edi + cmp esi,[line_end] + je missing_argument + lodsb + cmp al,',' + jne invalid_argument + and [symbol_definition],0 + mov dl,SYMCLASS_EXPRESSION + call identify_symbol + jc invalid_identifier + test ebx,ebx + jz invalid_identifier + and [brackets],0 + call move_to_next_symbol + jc no_brackets + cmp al,',' + jne no_brackets + inc esi + call move_to_next_symbol + jc invalid_argument + mov dl,al + lea edi,[esi+1] + call skip_token + cmp esi,edi + jne invalid_argument + call move_to_next_symbol + jc invalid_argument + mov dh,al + lea edi,[esi+1] + call skip_token + cmp esi,edi + jne invalid_argument + mov esi,[pattern_start] + ensure_no_brackets_in_pattern: + cmp esi,[calm_literals_cursor] + je brackets_ok + mov al,[esi] + cmp al,dl + je invalid_argument + cmp al,dh + je invalid_argument + cmp al,1Bh + je skip_reference_in_pattern + call skip_token + jmp ensure_no_brackets_in_pattern + skip_reference_in_pattern: + add esi,1+8 + jmp ensure_no_brackets_in_pattern + brackets_ok: + mov esi,edi + mov word [brackets],dx + no_brackets: + mov edi,[calm_code_cursor] + mov edx,calm_code_buffer + mov ecx,8+16 + call reserve_workspace + mov eax,[calm_line_number] + stosd + mov eax,calm_match + stosd + mov eax,ebx + stosd + mov ebx,[calm_literals_buffer.memory_start] + mov eax,[pattern_start] + sub eax,ebx + stosd + mov eax,[calm_literals_cursor] + sub eax,ebx + stosd + mov eax,[brackets] + stosd + mov [calm_code_cursor],edi + jmp instruction_assembled + +alm_compute: + test [assembly_mode],AMODE_CALM_DEFINITION + jz unexpected_instruction + and [symbol_definition],0 + mov dl,SYMCLASS_EXPRESSION + call identify_symbol + jc invalid_identifier + test ebx,ebx + jz invalid_identifier + call move_to_next_symbol + jc missing_argument + cmp al,',' + jne instruction_assembled + inc esi + call move_to_next_symbol + mov edi,[calm_code_cursor] + mov edx,calm_code_buffer + mov ecx,8+8 + call reserve_workspace + mov eax,[calm_line_number] + stosd + mov eax,calm_compute + stosd + mov eax,ebx + stosd + xor eax,eax + stosd + mov [calm_code_cursor],edi + mov edi,[expression_workspace.memory_start] + and [leave_opening_parentheses],0 + or [use_raw_values],1 + call parse_expression + and [use_raw_values],0 + push esi + mov esi,[expression_workspace.memory_start] + mov ecx,edi + sub ecx,esi + mov edi,[calm_code_cursor] + mov edx,calm_code_buffer + call reserve_workspace + mov [calm_code_cursor],edi + call convert_parsed_expression + pop esi + jc no_computable_expression + mov ecx,edi + xchg edi,[calm_code_cursor] + sub ecx,edi + mov [edi-4],ecx + jmp instruction_assembled + no_computable_expression: + sub [calm_code_cursor],8+8 + jmp assembly_line + convert_parsed_expression: + lodsd + test eax,eax + jz parsed_expression_converted + cmp al,EXPR_SYMBOL_VALUE + je copy_parsed_symbol + cmp al,EXPR_OPERATOR + je copy_parsed_operator + cmp al,EXPR_NUMBER + je copy_parsed_value + cmp al,EXPR_STRING + je copy_parsed_value + cmp al,EXPR_FLOAT + je copy_parsed_value + cmp al,EXPR_MISSING_PARENTHESIS + je expression_missing_parenthesis + jmp invalid_expression + copy_parsed_value: + or eax,EXPRF_CALM_LITERAL + mov ebx,[esi] + add esi,4 + test eax,EXPRF_VALUE_IN_WORKSPACE + jnz parsed_value_in_workspace + test ebx,ebx + jnz parsed_value_pointer_ok + jmp invalid_expression + parsed_value_in_workspace: + add ebx,[value_workspace.memory_start] + and eax,not EXPRF_VALUE_IN_WORKSPACE + parsed_value_pointer_ok: + cmp al,EXPR_FLOAT + je parsed_float_to_copy + mov ecx,[ebx] + add ecx,4 + jmp parsed_value_length_ok + parsed_float_to_copy: + mov ecx,sizeof.FloatData + parsed_value_length_ok: + stosd + mov edx,[calm_literals_cursor] + mov eax,edx + sub eax,[calm_literals_buffer.memory_start] + stosd + push edi + push ecx + mov edi,edx + mov edx,calm_literals_buffer + call reserve_workspace + pop ecx + xchg esi,ebx + rep movsb + mov [calm_literals_cursor],edi + mov esi,ebx + pop edi + jmp convert_parsed_expression + copy_parsed_symbol: + mov al,EXPR_SYMBOL + stosd + lodsd + test eax,eax + jz invalid_expression + stosd + add esi,4 + jmp convert_parsed_expression + copy_parsed_operator: + stosd + movsd + jmp convert_parsed_expression + parsed_expression_converted: + stosd + clc + retn + +alm_check: + test [assembly_mode],AMODE_CALM_DEFINITION + jz unexpected_instruction + mov edi,[calm_code_cursor] + mov edx,calm_code_buffer + mov eax,edi + sub eax,[edx+Workspace.memory_start] + mov [calm_rollback_offset],eax + mov ecx,8 + call reserve_workspace + mov eax,[calm_line_number] + stosd + mov eax,calm_check + stosd + mov [calm_code_cursor],edi + or [use_raw_values],1 + mov ebx,[condition_stack_base] + and dword [ebx],0 + add ebx,4 + mov [condition_stack],ebx + parse_alm_condition: + call peek_at_constituent_value + jc calm_condition_parsed + cmp al,'~' + jne parse_alm_logical_value + and [current_constituent],0 + mov ecx,BOOL_NEG + call push_to_condition_stack + jmp parse_alm_condition + alm_logical_value_empty: + test ecx,ecx + jz calm_condition_parsed + call push_to_condition_stack + jmp parse_alm_condition + parse_alm_logical_value: + call parse_logical_value + jc alm_logical_value_empty + jecxz convert_logical_value + call push_to_condition_stack + convert_logical_value: + mov [calm_source_pointer],esi + mov esi,[expression_workspace.memory_start] + mov ecx,edi + sub ecx,esi + add ecx,8 + add ecx,[condition_stack] + sub ecx,[condition_stack_base] + mov edi,[calm_code_cursor] + mov edx,calm_code_buffer + call reserve_workspace + xor eax,eax + stosd + mov [calm_code_cursor],edi + mov eax,[comparator] + stosd + convert_argument_sequences: + call convert_parsed_expression + jc condition_malformed + cmp esi,[expression_end] + jb convert_argument_sequences + mov ecx,edi + xchg edi,[calm_code_cursor] + sub ecx,edi + mov [edi-4],ecx + mov esi,[calm_source_pointer] + parse_alm_logical_operator: + call get_constituent_value + jc calm_condition_parsed + cmp al,')' + je close_subconditions + cmp al,'|' + je parse_or + cmp al,'&' + jne condition_malformed + parse_and: + mov ecx,BOOL_AND + jmp prepare_stack_for_logical_operator + parse_or: + mov ecx,BOOL_OR + prepare_stack_for_logical_operator: + mov ebx,[condition_stack] + sub ebx,4 + cmp dword [ebx],0 + jge push_logical_operator + mov [condition_stack],ebx + mov edi,[calm_code_cursor] + mov eax,[ebx] + stosd + mov [calm_code_cursor],edi + jmp prepare_stack_for_logical_operator + push_logical_operator: + call push_to_condition_stack + jmp parse_alm_condition + close_subconditions: + mov ebx,[condition_stack] + sub ebx,4 + cmp dword [ebx],0 + jl store_logical_operator + je condition_malformed + dec dword [ebx] + jnz parse_alm_logical_operator + mov [condition_stack],ebx + jmp parse_alm_logical_operator + store_logical_operator: + mov edi,[calm_code_cursor] + mov eax,[ebx] + mov [condition_stack],ebx + stosd + mov [calm_code_cursor],edi + jmp close_subconditions + push_to_condition_stack: + mov ebx,[condition_stack] + mov [ebx],ecx + add ebx,4 + cmp [condition_stack_end],ebx + je grow_condition_stack + mov [condition_stack],ebx + retn + grow_condition_stack: + mov eax,[condition_stack_base] + sub ebx,eax + lea ecx,[ebx+4] + call grow_stack + mov [condition_stack_base],eax + add ebx,eax + mov [condition_stack],ebx + add ecx,eax + mov [condition_stack_end],ecx + retn + calm_condition_parsed: + mov edi,[calm_code_cursor] + mov ebx,[condition_stack] + mov ecx,ebx + sub ecx,[condition_stack_base] + mov edx,calm_code_buffer + call reserve_workspace + finish_calm_condition: + sub ebx,4 + mov eax,[ebx] + cmp eax,0 + jg condition_malformed + stosd + jne finish_calm_condition + mov [calm_code_cursor],edi + and [use_raw_values],0 + jmp instruction_assembled + condition_malformed: + and [use_raw_values],0 + mov edi,[calm_rollback_offset] + add edi,[calm_code_buffer.memory_start] + mov [calm_code_cursor],edi + mov edx,_invalid_expression + call register_error + jmp assembly_line + +alm_publish: + test [assembly_mode],AMODE_CALM_DEFINITION + jz unexpected_instruction + mov [value],calm_publish_variable + call move_to_next_symbol + jc missing_argument + cmp al,':' + jne get_identifier_variable + inc esi + mov [value],calm_publish_stack + get_identifier_variable: + and [symbol_definition],0 + mov dl,SYMCLASS_EXPRESSION + call identify_symbol + jc invalid_identifier + test ebx,ebx + jz invalid_identifier + mov [label_leaf],ebx + call move_to_next_symbol + jc missing_argument + cmp al,':' + jne get_value_variable + mov eax,calm_publish_constant + xchg [value],eax + cmp eax,calm_publish_variable + jne invalid_argument + inc esi + call move_to_next_symbol + jc missing_argument + get_value_variable: + cmp al,',' + jne missing_argument + inc esi + mov dl,SYMCLASS_EXPRESSION + call identify_symbol + jc invalid_identifier + test ebx,ebx + jz invalid_identifier + mov edi,[calm_code_cursor] + mov edx,calm_code_buffer + mov ecx,8+8 + call reserve_workspace + mov eax,[calm_line_number] + stosd + mov eax,[value] + stosd + mov eax,[label_leaf] + stosd + mov eax,ebx + stosd + mov [calm_code_cursor],edi + jmp instruction_assembled + +alm_transform: + test [assembly_mode],AMODE_CALM_DEFINITION + jz unexpected_instruction + call move_to_next_symbol + jc missing_argument + and [symbol_definition],0 + mov dl,SYMCLASS_EXPRESSION + call identify_symbol + jc invalid_identifier + test ebx,ebx + jz invalid_identifier + mov [label_leaf],ebx + xor ebx,ebx + call move_to_next_symbol + jc alm_transform_arguments_ready + cmp al,',' + jne alm_transform_arguments_ready + inc esi + mov dl,SYMCLASS_EXPRESSION + call identify_symbol + jc invalid_identifier + test edx,edx + jz invalid_identifier + call get_symbol_namespace + alm_transform_arguments_ready: + mov edi,[calm_code_cursor] + mov edx,calm_code_buffer + mov ecx,8+8 + call reserve_workspace + mov eax,[calm_line_number] + stosd + mov eax,calm_transform + stosd + mov eax,[label_leaf] + stosd + mov eax,ebx + stosd + mov [calm_code_cursor],edi + jmp instruction_assembled + +alm_take: + test [assembly_mode],AMODE_CALM_DEFINITION + jz unexpected_instruction + call move_to_next_symbol + jc missing_argument + xor ebx,ebx + and [symbol_definition],0 + cmp al,',' + je alm_take_destination_ready + mov dl,SYMCLASS_EXPRESSION + call identify_symbol + jc invalid_identifier + test ebx,ebx + jz invalid_identifier + call move_to_next_symbol + jc missing_argument + cmp al,',' + jne missing_argument + alm_take_destination_ready: + mov [label_leaf],ebx + inc esi + mov dl,SYMCLASS_EXPRESSION + call identify_symbol + jc invalid_identifier + test ebx,ebx + jz invalid_identifier + mov edi,[calm_code_cursor] + mov edx,calm_code_buffer + mov ecx,8+8 + call reserve_workspace + mov eax,[calm_line_number] + stosd + mov eax,calm_take + stosd + mov eax,[label_leaf] + stosd + mov eax,ebx + stosd + mov [calm_code_cursor],edi + jmp instruction_assembled + +alm_call: + test [assembly_mode],AMODE_CALM_DEFINITION + jz unexpected_instruction + and [symbol_definition],0 + mov ebx,[current_context.base_namespace] + mov edx,[ebx+SymbolTree_Root.parent_branch] + test edx,edx + jz alm_call_context_ready + mov eax,[edx+SymbolTree_Foliage.root] + mov [current_context.base_namespace],eax + alm_call_context_ready: + mov dl,SYMCLASS_INSTRUCTION + push ebx + call identify_symbol + pop [current_context.base_namespace] + jc invalid_identifier + test ebx,ebx + jz invalid_identifier + cmp [ebx+SymbolTree_Leaf.class],SYMCLASS_INSTRUCTION + je alm_call_instruction_ok + mov edx,[ebx+SymbolTree_Leaf.branch] + mov eax,[edx+SymbolTree_Foliage.root] + test [eax+SymbolTree_Root.flags],NAMESPACE_CALM + jz alm_call_predicted_instruction + mov ebx,[ebx+SymbolTree_Leaf.fallback_parent] + test ebx,ebx + jz invalid_identifier + alm_call_predicted_instruction: + mov ebx,edx + mov [symbol_class],SYMCLASS_INSTRUCTION + or [symbol_required],1 + or [symbol_expected],1 + call scan_symbol_branch + jc invalid_identifier + mov edi,ebx + mark_instruction_fallbacks: + mov edx,[edi+SymbolTree_Leaf.fallback_neighbour] + test edx,edx + jz instruction_fallback_neighbour_ok + or [edx+SymbolTree_Leaf.extra_flags],SYMX_INSTRUCTION_PREDICTED + instruction_fallback_neighbour_ok: + mov edi,[edi+SymbolTree_Leaf.fallback_parent] + test edi,edi + jz alm_call_instruction_ok + or [edi+SymbolTree_Leaf.extra_flags],SYMX_INSTRUCTION_PREDICTED + jmp mark_instruction_fallbacks + alm_call_instruction_ok: + mov edi,[calm_code_cursor] + mov edx,calm_code_buffer + mov ecx,8+8 + call reserve_workspace + mov eax,[calm_line_number] + stosd + mov eax,calm_call + stosd + mov eax,ebx + stosd + alm_call_argument: + call move_to_next_symbol + jc alm_call_arguments_ready + cmp al,',' + jne alm_call_invalid_argument + inc esi + mov edx,calm_code_buffer + mov ecx,8 + call reserve_workspace + call move_to_next_symbol + jc alm_call_empty_argument + cmp al,',' + je alm_call_empty_argument + push edi + mov dl,SYMCLASS_EXPRESSION + call identify_symbol + pop edi + jc alm_call_invalid_argument + test ebx,ebx + jz alm_call_invalid_argument + mov eax,ebx + stosd + jmp alm_call_argument + alm_call_empty_argument: + or eax,-1 + stosd + jmp alm_call_argument + alm_call_invalid_argument: + mov edx,_invalid_argument + call register_error + alm_call_arguments_ready: + xor eax,eax + stosd + mov [calm_code_cursor],edi + jmp instruction_assembled + +alm_exit: + test [assembly_mode],AMODE_CALM_DEFINITION + jz unexpected_instruction + mov edi,[calm_code_cursor] + mov edx,calm_code_buffer + mov ecx,8 + call reserve_workspace + mov eax,[calm_line_number] + stosd + mov eax,calm_end + stosd + mov [calm_code_cursor],edi + jmp instruction_assembled + +launch_calm: + mov eax,[current_pass] + mov [ebx+SymbolTree_Leaf.last_use_pass],eax + mov eax,[ebx+SymbolTree_Leaf.branch] + mov [instruction_branch],eax + mov [calm_value],edx + mov [calm_source_pointer],esi + mov esi,[edx+ValueDefinition.value] + mov eax,[esi+CompiledMacroHeader.literals_offset] + add eax,esi + mov [calm_literals],eax + mov [breakpoint_token],0 + mov ebx,[esi+CompiledMacroHeader.label_argument_leaf] + test ebx,ebx + jz calm_label_argument_ok + call update_value_definition + test edx,edx + jz calm_label_argument_ok + push esi + push edx + push [line_end] + push [embedded_context] + mov eax,[line_context] + mov [embedded_context],eax + mov esi,[line_start] + mov ecx,[label_instruction_start] + mov [line_end],ecx + sub ecx,esi + add ecx,1+sizeof.RecognitionContext + mov edx,assembly_workspace + mov edi,[edx+Workspace.memory_start] + call reserve_workspace + xor edx,edx + mov [breakpoint_token],dl + call extract_piece_of_line + pop [embedded_context] + pop [line_end] + pop edx + mov [value_type],VALTYPE_SYMBOLIC + mov esi,[assembly_workspace.memory_start] + mov ecx,edi + sub ecx,esi + call assign_value + pop esi + calm_label_argument_ok: + add esi,sizeof.CompiledMacroHeader + mov eax,[esi] + inc eax + cmp eax,1 + jbe calm_arguments_defined + get_calm_argument: + mov edx,assembly_workspace + mov edi,[edx+Workspace.memory_start] + mov ecx,[line_end] + sub ecx,[calm_source_pointer] + add ecx,1+sizeof.RecognitionContext + call reserve_workspace + mov eax,[esi+sizeof.CompiledMacroArgument] + xchg esi,[calm_source_pointer] + inc eax + jz get_calm_greedy_argument + call extract_argument_value + jmp calm_argument_value_cut + get_calm_greedy_argument: + xor edx,edx + call extract_piece_of_line + calm_argument_value_cut: + xchg esi,[calm_source_pointer] + mov ebx,[esi+CompiledMacroArgument.symbol_leaf] + call update_value_definition + test edx,edx + jz calm_argument_ok + mov ecx,edi + sub ecx,[assembly_workspace.memory_start] + test ecx,ecx + jnz calm_argument_value_ready + or ecx,[esi+CompiledMacroArgument.default_value_length] + jz calm_argument_value_ready + cmp ecx,-1 + je missing_argument + push esi + mov esi,[esi+CompiledMacroArgument.default_value_offset] + add esi,[calm_literals] + jmp calm_assign_argument_value + calm_argument_value_ready: + push esi + mov esi,[assembly_workspace.memory_start] + calm_assign_argument_value: + mov [value_type],VALTYPE_SYMBOLIC + call assign_value + pop esi + calm_argument_ok: + add esi,sizeof.CompiledMacroArgument + mov eax,[esi] + inc eax + cmp eax,1 + jbe calm_arguments_defined + xchg esi,[calm_source_pointer] + call move_to_next_symbol + jc calm_next_argument + cmp al,',' + jne invalid_argument + inc esi + calm_next_argument: + xchg esi,[calm_source_pointer] + jmp get_calm_argument + calm_arguments_defined: + add esi,4 + and [calm_instruction_number],0 + call create_source_entry + jc calm_exceeded_stack_limit + mov edx,[calm_value] + mov [ebx+SourceEntry.type],SOURCE_CALM + mov [ebx+SourceEntry.text],edx + sub esi,[edx+ValueDefinition.value] + mov [ebx+SourceEntry.offset],esi + or [edx+ValueDefinition.flags],VAL_IN_USE + inc [edx+ValueDefinition.reference_count] + mov eax,[parameter_namespace] + mov [ebx+SourceEntry.local_namespace],eax + mov esi,[calm_source_pointer] + mov ecx,[instruction_branch] + test ecx,ecx + jz instruction_assembled + mov [ebx+SourceEntry.name],ecx + or [ebx+SourceEntry.name_length],-1 + jmp instruction_assembled + calm_exceeded_stack_limit: + mov edx,_stack_limit_exceeded + call register_error + jmp assembly_line + +calm_virtual_machine: + mov al,[ebx+SourceEntry.saved_result] + mov [calm_result],al + mov edx,[ebx+SourceEntry.text] + mov [calm_value],edx + mov esi,[edx+ValueDefinition.value] + mov eax,[esi+CompiledMacroHeader.literals_offset] + add eax,esi + mov [calm_literals],eax + add esi,[ebx+SourceEntry.offset] + calm_execution_unit: + lodsd + mov [calm_instruction_number],eax + lodsd + jmp eax + +calm_end: + mov ebx,[source_context] + mov ecx,[ebx+SourceContext.number_of_entries] + dec ecx + ; jz internal_error + mov [ebx+SourceContext.number_of_entries],ecx + imul ecx,sizeof.SourceEntry + mov edx,[ebx+sizeof.SourceContext+ecx+SourceEntry.text] + and [edx+ValueDefinition.flags],not VAL_IN_USE + dec [edx+ValueDefinition.reference_count] + jmp assembly_line + +calm_arrange: + lodsd + mov [label_leaf],eax + lodsd + mov ecx,eax + lodsd + push esi + mov esi,[calm_literals] + add eax,esi + add esi,ecx + mov [pattern_end],eax + mov edi,[assembly_workspace.memory_start] + arrange_by_pattern: + mov edx,assembly_workspace + mov ecx,[pattern_end] + sub ecx,esi + call reserve_workspace + arrange_token: + cmp esi,[pattern_end] + je assign_arranged_value + lodsb + cmp al,1Bh + je arrange_by_reference + cmp al,0A0h + je downgrade_hard_space + stosb + cmp al,1Ah + je copy_arranged_token_data + cmp al,22h + je copy_arranged_token_data + cmp al,27h + je copy_arranged_token_data + cmp al,30h + jne arrange_token + lodsd + stosd + mov ecx,eax + rep movsb + jmp arrange_token + copy_arranged_token_data: + movsd + jmp arrange_token + downgrade_hard_space: + mov al,20h + stosb + jmp arrange_token + arrange_by_reference: + mov ebx,[esi+4] + call use_available_value + jc arrange_missing_value + mov al,[edx+ValueDefinition.type] + cmp al,VALTYPE_NUMERIC + je arrange_numeric_value + cmp al,VALTYPE_SYMBOLIC + jne arrange_unsupported_value + mov ecx,[edx+ValueDefinition.value_length] + jecxz arrange_empty_value + add ecx,1+sizeof.RecognitionContext + mov ebx,edx + mov edx,assembly_workspace + call reserve_workspace + mov edx,ebx + mov ebx,esi + mov esi,[edx+ValueDefinition.value] + mov ecx,[edx+ValueDefinition.value_length] + rep movsb + lea esi,[ebx+8] + cmp esi,[pattern_end] + je assign_arranged_value + mov al,40h + stosb + xor eax,eax + assert sizeof.RecognitionContext and 11b = 0 + mov ecx,sizeof.RecognitionContext shr 2 + rep stosd + jmp arrange_by_pattern + arrange_numeric_value: + mov eax,[edx+ValueDefinition.value] + mov ecx,[eax] + cmp dword [eax+4+ecx],0 + jne arrange_unsupported_value + test byte [eax+4+ecx-1],80h + jnz arrange_unsupported_value + mov ebx,esi + mov esi,eax + mov edx,assembly_workspace + add ecx,1+4 + call reserve_workspace + mov al,30h + stosb + lodsd + stosd + mov ecx,eax + rep movsb + lea esi,[ebx+8] + jmp arrange_by_pattern + arrange_empty_value: + add esi,8 + jmp arrange_token + arrange_unsupported_value: + mov edx,_invalid_symbol_value + jmp arrange_failed_reference + arrange_missing_value: + mov edx,_undefined_symbol + arrange_failed_reference: + call register_error + mov al,1Ah + stosb + movsd + lodsd + mov eax,[eax+SymbolTree_Leaf.branch] + cmp [eax+SymbolTree_Foliage.name_kind],NAME_CASEINSENSITIVE + jne arrange_token + mov al,'?' + stosb + jmp arrange_token + assign_arranged_value: + mov ebx,[label_leaf] + call update_value_definition + test edx,edx + jz arranged_value_assigned + mov [value_type],VALTYPE_SYMBOLIC + mov ecx,edi + mov esi,[assembly_workspace.memory_start] + sub ecx,esi + call assign_value + arranged_value_assigned: + pop esi + jmp calm_execution_unit + +calm_match: + and [calm_result],0 + lodsd + mov ebx,eax + lodsd + mov ecx,eax + lodsd + mov edi,[calm_literals] + add eax,edi + add edi,ecx + mov [pattern_end],eax + lodsd + mov [brackets],eax + call use_available_value + jc calm_undefined_symbol + mov al,[edx+ValueDefinition.type] + cmp al,VALTYPE_RESERVED + je calm_undefined_symbol + cmp al,VALTYPE_SYMBOLIC + jne calm_invalid_value + push esi + mov [value],edx + inc [edx+ValueDefinition.reference_count] + mov esi,[edx+ValueDefinition.value] + mov ecx,[edx+ValueDefinition.value_length] + add ecx,esi + mov [line_end],ecx + mov ebx,[expression_workspace.memory_start] + and [matched_context],0 + and [stored_position],0 + calm_match_with_pattern: + cmp edi,[pattern_end] + je calm_end_of_pattern + mov ah,[edi] + cmp ah,1Bh + jne calm_exact_match + calm_wildcard_match: + and [stored_position],0 + add edi,1+8 + cmp edi,[pattern_end] + je calm_required_wildcard_match + cmp byte [edi],'?' + je calm_optional_wildcard_match + calm_required_wildcard_match: + cmp esi,[line_end] + je calm_match_done + call calm_consume_whitespace + jz calm_match_done + xchg edi,ebx + mov edx,expression_workspace + mov ecx,sizeof.MatchedExcerpt + call reserve_workspace + xchg edi,ebx + mov eax,[edi-4] + mov ecx,[matched_context] + mov [ebx+MatchedExcerpt.symbol_leaf],eax + mov [ebx+MatchedExcerpt.recognition_context],ecx + mov [ebx+MatchedExcerpt.data_start],esi + mov al,[esi] + call calm_consume_token + jc calm_match_done + mov [ebx+MatchedExcerpt.data_end],esi + add ebx,sizeof.MatchedExcerpt + jmp calm_match_with_pattern + calm_optional_wildcard_match: + xchg edi,ebx + mov edx,expression_workspace + mov ecx,sizeof.MatchedExcerpt + call reserve_workspace + xchg edi,ebx + mov eax,[edi-4] + inc edi + mov ecx,[matched_context] + mov [ebx+MatchedExcerpt.symbol_leaf],eax + mov [ebx+MatchedExcerpt.recognition_context],ecx + mov [ebx+MatchedExcerpt.data_start],esi + mov [ebx+MatchedExcerpt.data_end],esi + add ebx,sizeof.MatchedExcerpt + jmp calm_match_with_pattern + calm_exact_match: + cmp esi,[line_end] + je calm_end_of_text + mov al,[esi] + cmp al,40h + jne found_token_to_match + inc esi + mov [matched_context],esi + add esi,sizeof.RecognitionContext + jmp calm_exact_match + found_token_to_match: + cmp [stored_position],0 + jne calm_match_position_stored + mov [stored_position],esi + mov ecx,[matched_context] + mov [stored_context],ecx + mov [stored_pattern],edi + calm_match_position_stored: + cmp al,20h + je calm_match_whitespace + cmp ah,20h + je calm_skip_pattern_whitespace + cmpsb + jne calm_token_mismatch + cmp ah,1Ah + je calm_match_name_tokens + cmp ah,22h + je calm_match_string_tokens + cmp ah,27h + je calm_match_string_tokens + cmp ah,30h + jne calm_match_with_pattern + mov ecx,esi + mov edx,edi + lodsd + add esi,eax + mov eax,[edi] + lea edi,[edi+4+eax] + cmp byte [edi],'?' + jne calm_compare_token_contents + inc edi + jmp calm_compare_token_contents + calm_match_string_tokens: + lodsd + mov ecx,eax + mov edx,[edi] + add edi,4 + calm_compare_token_contents: + cmp ecx,edx + je calm_match_with_pattern + push esi edi + mov esi,ecx + mov edi,edx + mov ecx,[esi] + add ecx,4 + repe cmpsb + pop edi esi + jne calm_retract_match + jmp calm_match_with_pattern + calm_match_name_tokens: + lodsd + mov ecx,eax + mov edx,[edi] + add edi,4 + cmp byte [edi],'?' + je calm_case_insensitive_match + cmp ecx,edx + je calm_match_with_pattern + push esi edi + mov esi,ecx + mov edi,edx + lodsd + scasd + jne calm_name_mismatch + mov ecx,eax + mov eax,[esi+ecx] + cmp eax,[edi+ecx] + jne calm_name_mismatch + repe cmpsb + jne calm_name_mismatch + pop edi esi + jmp calm_match_with_pattern + calm_name_mismatch: + pop edi esi + jmp calm_retract_match + calm_case_insensitive_match: + inc edi + cmp ecx,edx + je calm_match_with_pattern + push esi edi + mov esi,ecx + mov edi,edx + lodsd + scasd + jne calm_name_mismatch + mov ecx,eax + mov eax,[esi+ecx+4] + cmp eax,[edi+ecx+4] + jne calm_name_mismatch + xor eax,eax + calm_compare_case_insensitively: + lodsb + mov dl,[characters+eax] + mov al,[edi] + inc edi + cmp dl,[characters+eax] + jne name_mismatch + loop calm_compare_case_insensitively + pop edi esi + jmp calm_match_with_pattern + calm_match_converted_number: + call convert_number_to_match + jmp calm_compare_token_contents + calm_match_with_converted_number: + xchg esi,edi + call convert_number_to_match + xchg esi,edi + jmp calm_compare_token_contents + calm_match_whitespace: + cmp ah,20h + je calm_optional_whitespace + cmp ah,0A0h + je calm_required_whitespace + cmp [stored_pattern],edi + jne calm_retract_match + call calm_consume_whitespace + jz calm_match_done + and [stored_position],0 + jmp calm_match_with_pattern + calm_optional_whitespace: + inc edi + cmp edi,[pattern_end] + je calm_whitespace_matched + cmp byte [edi],0A0h + jne calm_whitespace_matched + calm_required_whitespace: + inc edi + cmp edi,[pattern_end] + je calm_whitespace_matched + cmp byte [edi],20h + jne calm_whitespace_matched + inc edi + calm_whitespace_matched: + call calm_consume_whitespace + jz calm_end_of_text + jmp calm_match_with_pattern + calm_skip_pattern_whitespace: + inc edi + jmp calm_match_with_pattern + calm_token_mismatch: + cmp ax,1A30h + je calm_match_converted_number + cmp ax,301Ah + je calm_match_with_converted_number + calm_retract_match: + xor esi,esi + xchg esi,[stored_position] + mov ecx,[stored_context] + mov [matched_context],ecx + mov edi,[stored_pattern] + call calm_consume_whitespace + jz calm_match_done + calm_expand_wildcard_match: + cmp ebx,[expression_workspace.memory_start] + je calm_match_done + call calm_consume_token + jc calm_match_done + mov [ebx-sizeof.MatchedExcerpt+MatchedExcerpt.data_end],esi + jmp calm_match_with_pattern + calm_consume_whitespace: + mov al,[esi] + cmp al,40h + je calm_consume_context_token + cmp al,20h + jne calm_whitespace_consumed + inc esi + cmp esi,[line_end] + jne calm_consume_whitespace + calm_whitespace_consumed: + retn + calm_consume_context_token: + inc esi + mov [matched_context],esi + add esi,sizeof.RecognitionContext + cmp esi,[line_end] + jne calm_consume_whitespace + retn + calm_consume_token: + xor ecx,ecx + mov edx,[brackets] + inc esi + consume_token_content: + cmp al,dh + je consume_closing + cmp al,dl + je consume_opening + cmp al,1Ah + je consume_token_with_data + cmp al,22h + je consume_token_with_data + cmp al,27h + je consume_token_with_data + cmp al,30h + je consume_internal_token + cmp al,40h + je consume_context_token + test ecx,ecx + jnz consume_more + retn + consume_token_with_data: + add esi,4 + test ecx,ecx + jnz consume_more + retn + consume_internal_token: + lodsd + add esi,eax + test ecx,ecx + jnz consume_more + retn + consume_context_token: + add esi,sizeof.RecognitionContext + test ecx,ecx + jnz consume_more + retn + consume_closing: + sub ecx,1 + jg consume_more + retn + consume_opening: + inc ecx + consume_more: + cmp esi,[line_end] + je consume_unsatisfied + lodsb + jmp consume_token_content + consume_unsatisfied: + stc + retn + calm_end_of_pattern: + cmp esi,[line_end] + je calm_match_found + lodsb + cmp al,20h + je calm_end_of_pattern + cmp al,40h + jne calm_another_token_to_match + add esi,sizeof.RecognitionContext + jmp calm_end_of_pattern + calm_another_token_to_match: + dec esi + cmp [stored_position],0 + je calm_expand_wildcard_match + jmp calm_retract_match + calm_end_of_text: + cmp edi,[pattern_end] + je calm_match_found + mov ah,[edi] + cmp ah,1Bh + je calm_wildcard_match + cmp ah,20h + jne calm_match_done + inc edi + jmp calm_end_of_text + calm_match_found: + or [calm_result],1 + mov esi,ebx + mov [value_type],VALTYPE_SYMBOLIC + calm_assign_matched_values: + cmp esi,[expression_workspace.memory_start] + je calm_match_done + sub esi,sizeof.MatchedExcerpt + mov ebx,[esi+MatchedExcerpt.symbol_leaf] + call update_value_definition + test edx,edx + jz calm_assign_matched_values + push esi + mov eax,[esi+MatchedExcerpt.recognition_context] + mov ecx,[esi+MatchedExcerpt.data_end] + mov esi,[esi+MatchedExcerpt.data_start] + sub ecx,esi + jz calm_matched_value_ready + test eax,eax + jz calm_matched_value_ready + cmp dword [eax],0 + je calm_matched_value_ready + push esi ecx edx + mov esi,eax + mov edx,assembly_workspace + mov edi,[edx+Workspace.memory_start] + add ecx,1+sizeof.RecognitionContext + call reserve_workspace + mov al,40h + stosb + assert sizeof.RecognitionContext and 11b = 0 + mov ecx,sizeof.RecognitionContext shr 2 + rep movsd + pop edx ecx esi + rep movsb + mov ecx,edi + mov esi,[assembly_workspace.memory_start] + sub ecx,esi + calm_matched_value_ready: + call assign_value + pop esi + jmp calm_assign_matched_values + calm_match_done: + mov edx,[value] + dec [edx+ValueDefinition.reference_count] + pop esi + jmp calm_execution_unit + +calm_compute: + lodsd + mov [label_leaf],eax + lodsd + add eax,esi + push eax + mov edi,[calculation_workspace.memory_start] + and [value_position],0 + call calculate_parsed_expression + call pop_terms + jc calm_compute_done + mov al,byte [edi+ExpressionTerm.attributes] + cmp al,EXPR_STRING + je calm_computed_string + cmp al,EXPR_FLOAT + je calm_computed_float + call convert_terms_to_numeric_value + mov [value_type],VALTYPE_NUMERIC + calm_computed_value_ready: + mov edi,ecx + mov ebx,[label_leaf] + call update_value_definition + test edx,edx + jz calm_compute_done + mov ecx,edi + call assign_value + calm_compute_done: + pop esi + jmp calm_execution_unit + calm_computed_string: + call get_term_value + mov esi,edx + mov ecx,[esi] + add ecx,4 + mov [value_type],VALTYPE_STRING + jmp calm_computed_value_ready + calm_computed_float: + call get_term_value + mov esi,edx + mov ecx,sizeof.FloatData + mov [value_type],VALTYPE_FLOAT + jmp calm_computed_value_ready + +calm_check: + lodsd + test eax,eax + jz calm_execution_unit + js calm_check + calm_evaluate_logical_value: + call evaluate_stored_logical_value + mov [calm_result],al + add esi,[esi-4] + find_next_logical_value: + lodsd + test eax,eax + jz calm_execution_unit + jns next_logical_value_found + cmp eax,BOOL_NEG + jne find_next_logical_value + xor [calm_result],1 + jmp find_next_logical_value + next_logical_value_found: + mov ebx,esi + xor ecx,ecx + skip_next_logical_value: + inc ecx + add esi,eax + skip_logical_operator: + lodsd + assert BOOL_NEG = -1 + cmp eax,BOOL_NEG + jg skip_next_logical_value + je skip_logical_operator + dec ecx + jnz skip_logical_operator + cmp eax,BOOL_AND + je calm_and + calm_or: + cmp [calm_result],0 + jne find_next_logical_value + mov esi,ebx + jmp calm_evaluate_logical_value + calm_and: + cmp [calm_result],0 + je find_next_logical_value + mov esi,ebx + jmp calm_evaluate_logical_value + +calm_jyes: + cmp [calm_result],0 + jne calm_jump + add esi,4 + jmp calm_execution_unit +calm_jno: + cmp [calm_result],0 + je calm_jump + add esi,4 + jmp calm_execution_unit +calm_jump: + lodsd + mov edx,[calm_value] + add eax,[edx+ValueDefinition.value] + mov esi,eax + jmp calm_execution_unit + +calm_assemble: + lodsd + mov ebx,eax + mov eax,[current_pass] + mov [ebx+SymbolTree_Leaf.last_use_pass],eax + mov eax,[ebx+SymbolTree_Leaf.branch] + mov [instruction_branch],eax + call use_available_value + jc calm_undefined_symbol + mov al,[edx+ValueDefinition.type] + cmp al,VALTYPE_SYMBOLIC + jne calm_invalid_value + call create_source_entry + jc calm_assemble_exceeded_stack_limit + mov [ebx+SourceEntry.type],SOURCE_MACRO + or [ebx+SourceEntry.flags],SRCF_PREPROCESSED + mov [ebx+SourceEntry.text],edx + or [edx+ValueDefinition.flags],VAL_IN_USE + inc [edx+ValueDefinition.reference_count] + mov eax,[calm_instruction_number] + mov [ebx-sizeof.SourceEntry+SourceEntry.line_number],eax + mov edx,[ebx-sizeof.SourceEntry+SourceEntry.text] + mov ecx,[edx+ValueDefinition.value] + sub esi,ecx + mov [ebx-sizeof.SourceEntry+SourceEntry.offset],esi + mov eax,[ebx-sizeof.SourceEntry+SourceEntry.local_namespace] + mov [ebx+SourceEntry.local_namespace],eax + mov al,[calm_result] + mov [ebx-sizeof.SourceEntry+SourceEntry.saved_result],al + mov ecx,[instruction_branch] + test ecx,ecx + jz assembly_line + mov [ebx+SourceEntry.name],ecx + or [ebx+SourceEntry.name_length],-1 + jmp assembly_line + calm_undefined_symbol: + mov edx,_undefined_symbol + jmp calm_execution_error + calm_invalid_value: + mov edx,_invalid_symbol_value + jmp calm_execution_error + calm_assemble_exceeded_stack_limit: + mov edx,_stack_limit_exceeded + calm_execution_error: + call register_error + jmp calm_execution_unit + +calm_transform: + mov [calm_result],0 + lodsd + mov [label_leaf],eax + mov ebx,eax + lodsd + mov [transforming_namespace],eax + call get_available_value + jc calm_undefined_symbol + cmp [edx+ValueDefinition.type],VALTYPE_SYMBOLIC + jne calm_invalid_value + push esi + call clear_line_embeddings + mov esi,[edx+ValueDefinition.value] + mov [line_start],esi + xor eax,eax + mov [embedded_context],eax + mov [line_context],eax + mov ecx,[edx+ValueDefinition.value_length] + add ecx,esi + mov [line_end],ecx + mov edi,[assembly_workspace.memory_start] + mov [hidden_context],0 + transform_symbolic_value: + and [symbol_definition],0 + mov dl,SYMCLASS_EXPRESSION + push edi + mov eax,[embedded_context] + mov [line_context],eax + mov ebx,[transforming_namespace] + test ebx,ebx + jz transform_identify + call identify_symbol_in_namespace + jmp transform_identification_done + transform_identify: + call identify_symbol + transform_identification_done: + jc symbolic_value_transformed + test edi,edi + jnz ready_to_transform + call skip_literal + xor ebx,ebx + ready_to_transform: + pop edi + test ebx,ebx + jz untransformed_literal + mov [further_whitespace],ecx + call get_available_value + jc untransformed_literal + cmp [edx+ValueDefinition.type],VALTYPE_SYMBOLIC + jne untransformed_literal + mov eax,[current_pass] + mov [ebx+SymbolTree_Leaf.last_use_pass],eax + mov [calm_result],1 + mov [line_start],esi + mov esi,[edx+ValueDefinition.value] + mov ecx,[edx+ValueDefinition.value_length] + mov ebx,ecx + add ecx,[further_whitespace] + add ecx,1+sizeof.RecognitionContext + mov edx,assembly_workspace + call reserve_workspace + test ebx,ebx + jz symbol_transformed + mov al,1 + xchg al,[hidden_context] + test al,al + jnz reset_hidden_context + cmp [line_context],0 + je copy_transformed_value + reset_hidden_context: + xor edx,edx + call reset_transformed_context + copy_transformed_value: + mov ecx,ebx + rep movsb + symbol_transformed: + mov ecx,[further_whitespace] + mov al,20h + rep stosb + mov esi,[line_start] + jmp transform_symbolic_value + untransformed_literal: + mov ecx,esi + xchg esi,[line_start] + sub ecx,esi + mov ebx,ecx + add ecx,1+sizeof.RecognitionContext + mov edx,assembly_workspace + call reserve_workspace + xor al,al + xchg al,[hidden_context] + test al,al + jz copy_untransformed_literal + mov edx,[line_context] + call reset_transformed_context + copy_untransformed_literal: + mov ecx,ebx + rep movsb + jmp transform_symbolic_value + reset_transformed_context: + mov al,40h + cmp [esi],al + je transformed_context_ok + stosb + assert sizeof.RecognitionContext and 11b = 0 + mov ecx,sizeof.RecognitionContext shr 2 + test edx,edx + jz clear_transformed_context + xchg esi,edx + rep movsd + mov esi,edx + transformed_context_ok: + retn + clear_transformed_context: + xor eax,eax + rep stosd + retn + symbolic_value_transformed: + pop edi + cmp [calm_result],0 + je calm_transform_done + mov ebx,[label_leaf] + call update_value_definition + test edx,edx + jz calm_transform_done + mov esi,[assembly_workspace.memory_start] + mov ecx,edi + sub ecx,esi + mov [value_type],VALTYPE_SYMBOLIC + call assign_value + calm_transform_done: + pop esi + jmp calm_execution_unit + +calm_stringify: + lodsd + mov [label_leaf],eax + mov ebx,eax + call get_available_value + jc calm_undefined_symbol + mov al,[edx+ValueDefinition.type] + cmp al,VALTYPE_SYMBOLIC + jne calm_invalid_value + mov eax,[edx+ValueDefinition.value] + mov [symbol_value_start],eax + mov ecx,[edx+ValueDefinition.value_length] + add ecx,eax + mov [symbol_value_end],ecx + push esi + call convert_symbolic_value_to_string + mov edi,ecx + mov ebx,[label_leaf] + call update_value_definition + test edx,edx + jz calm_stringify_done + mov ecx,edi + mov [value_type],VALTYPE_STRING + call assign_value + calm_stringify_done: + pop esi + jmp calm_execution_unit + +calm_publish_constant: + mov edi,create_constant_value_definition + jmp calm_publish +calm_publish_stack: + mov edi,create_value_definition + jmp calm_publish +calm_publish_variable: + mov edi,update_value_definition + calm_publish: + lodsd + mov ebx,eax + lodsd + mov [label_leaf],eax + call use_available_value + jc calm_undefined_symbol + mov al,[edx+ValueDefinition.type] + cmp al,VALTYPE_SYMBOLIC + jne calm_invalid_value + push esi edi + call clear_line_embeddings + mov esi,[edx+ValueDefinition.value] + mov [line_start],esi + mov ecx,[edx+ValueDefinition.value_length] + add ecx,esi + mov [line_end],ecx + and [symbol_definition],0 + mov dl,SYMCLASS_EXPRESSION + call identify_symbol + mov eax,esi + pop edi esi + jc calm_invalid_identifier + test ebx,ebx + jz calm_invalid_identifier + cmp eax,[line_end] + jne calm_invalid_identifier + xchg ebx,[label_leaf] + call use_available_value + jc calm_undefined_symbol + mov al,[edx+ValueDefinition.type] + cmp al,VALTYPE_SYMBOLIC + jb calm_invalid_value + cmp al,VALTYPE_AREA + ja calm_invalid_value + mov [value_type],al + mov [value],edx + mov ebx,[label_leaf] + call edi + test edx,edx + jz calm_execution_unit + push esi + mov eax,[value] + mov esi,[eax+ValueDefinition.value] + mov ecx,[eax+ValueDefinition.value_length] + call assign_value + pop esi + jmp calm_execution_unit + calm_invalid_identifier: + mov edx,_invalid_identifier + call register_error + jmp calm_execution_unit + +calm_take: + lodsd + mov edi,eax + lodsd + mov ebx,eax + xor edx,edx + call remove_value_definition + setnc [calm_result] + jmp calm_execution_unit + +calm_call: + lodsd + mov ebx,eax + mov eax,[current_pass] + mov [ebx+SymbolTree_Leaf.last_use_pass],eax + mov eax,[ebx+SymbolTree_Leaf.branch] + mov [instruction_branch],eax + call use_available_value + jc calm_call_undefined + mov al,[edx+ValueDefinition.type] + cmp al,VALTYPE_CALM + jne calm_call_invalid + call create_source_entry + jc calm_call_exceeded_stack_limit + mov eax,[parameter_namespace] + mov [ebx+SourceEntry.local_namespace],eax + mov ecx,[instruction_branch] + test ecx,ecx + jz called_name_ok + mov [ebx+SourceEntry.name],ecx + or [ebx+SourceEntry.name_length],-1 + called_name_ok: + mov [ebx+SourceEntry.type],SOURCE_CALM + mov [ebx+SourceEntry.text],edx + or [edx+ValueDefinition.flags],VAL_IN_USE + inc [edx+ValueDefinition.reference_count] + mov edi,esi + mov esi,[edx+ValueDefinition.value] + mov eax,[esi+CompiledMacroHeader.literals_offset] + add eax,esi + mov [calm_value],edx + mov [calm_literals],eax + add esi,sizeof.CompiledMacroHeader + push ebx + process_call_arguments: + mov eax,[esi] + inc eax + cmp eax,1 + jbe call_arguments_ready + mov ebx,[edi] + test ebx,ebx + jz omitted_call_argument + add edi,4 + cmp ebx,-1 + je omitted_call_argument + call use_available_value + jc missing_argument_value + push edx + mov ebx,[esi+CompiledMacroArgument.symbol_leaf] + call update_value_definition + pop eax + push esi edi + mov esi,[eax+ValueDefinition.value] + mov ecx,[eax+ValueDefinition.value_length] + mov al,[eax+ValueDefinition.type] + assign_call_argument: + test edx,edx + jz call_argument_assigned + mov [value_type],al + call assign_value + call_argument_assigned: + pop edi esi + next_call_argument: + add esi,sizeof.CompiledMacroArgument + jmp process_call_arguments + missing_argument_value: + mov edx,_undefined_symbol + call register_error + jmp next_call_argument + omitted_call_argument: + mov ebx,[esi+CompiledMacroArgument.symbol_leaf] + call update_value_definition + push esi edi + mov ecx,[esi+CompiledMacroArgument.default_value_length] + mov esi,[esi+CompiledMacroArgument.default_value_offset] + add esi,[calm_literals] + mov al,VALTYPE_SYMBOLIC + cmp ecx,-1 + jne assign_call_argument + inc ecx + push edx + mov edx,_invalid_argument + call register_error + pop edx + jmp assign_call_argument + call_arguments_ready: + add esi,4 + mov eax,[edi] + add edi,4 + test eax,eax + jz call_arguments_ok + mov edx,_invalid_argument + call register_error + skip_excess_arguments: + mov eax,[edi] + add edi,4 + test eax,eax + jnz skip_excess_arguments + call_arguments_ok: + pop ebx + mov eax,[calm_instruction_number] + mov [ebx-sizeof.SourceEntry+SourceEntry.line_number],eax + mov eax,[ebx-sizeof.SourceEntry+SourceEntry.text] + mov ecx,[eax+ValueDefinition.value] + sub edi,ecx + mov [ebx-sizeof.SourceEntry+SourceEntry.offset],edi + mov al,[calm_result] + mov [ebx-sizeof.SourceEntry+SourceEntry.saved_result],al + jmp calm_execution_unit + calm_call_undefined: + mov edx,_undefined_symbol + jmp calm_call_error + calm_call_invalid: + mov edx,_invalid_symbol_value + jmp calm_call_error + calm_call_exceeded_stack_limit: + mov edx,_stack_limit_exceeded + calm_call_error: + call register_error + skip_call_arguments: + lodsd + test eax,eax + jnz skip_call_arguments + jmp calm_execution_unit \ No newline at end of file diff --git a/x86_64_sse2_x87/fasm/source/conditions.inc b/x86_64_sse2_x87/fasm/source/conditions.inc new file mode 100644 index 0000000..0c46786 --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/conditions.inc @@ -0,0 +1,667 @@ + +COND_NEGATED = 1 ; only with COND_EVALUATE +COND_TRUE = 1 ; only with COND_DETERMINED +COND_EVALUATE = 2 +COND_DETERMINED = 4 + +get_condition_value: +; in: +; esi = pointer into preprocessed line or the last embedded line +; out: +; esi = pointer advanced past the processed line +; al = logical value + mov ebx,[condition_stack_base] + mov [condition_stack],ebx + mov byte [ebx],COND_EVALUATE + get_logical_value: + call peek_at_constituent_value + cmp al,'~' + jne negation_registered + and [current_constituent],0 + mov ebx,[condition_stack] + mov al,[ebx] + test byte [ebx],COND_EVALUATE + jz get_logical_value + xor byte [ebx],COND_NEGATED + jmp get_logical_value + negation_registered: + call parse_logical_value + setnc dh + mov dl,al + jecxz get_logical_operator + mov ebx,[condition_stack] + lea eax,[ebx+ecx+1] + cmp eax,[condition_stack_end] + jbe condition_stack_ready + push ecx edx + mov ecx,eax + mov eax,[condition_stack_base] + sub ecx,eax + sub ebx,eax + call grow_stack + mov [condition_stack_base],eax + add ebx,eax + mov [condition_stack],ebx + add eax,ecx + mov [condition_stack_end],eax + pop edx ecx + condition_stack_ready: + xor al,al + test byte [ebx],COND_EVALUATE + jz store_opening_parentheses + or al,COND_EVALUATE + store_opening_parentheses: + inc ebx + mov [ebx],al + loop store_opening_parentheses + mov [condition_stack],ebx + cmp dx,'~' + je get_logical_value + get_logical_operator: + call peek_at_constituent_value + jc end_of_logical_expression + cmp al,')' + je close_logical_subexpression + cmp al,'|' + je logical_or + cmp al,'&' + jne end_of_logical_expression + logical_and: + and [current_constituent],0 + mov ebx,[condition_stack] + mov al,[ebx] + test al,COND_EVALUATE + jnz evaluate_logical_and + test al,COND_DETERMINED + jz get_logical_value + test al,COND_TRUE + jz determined_false + jmp continue_evaluation + evaluate_logical_and: + test al,COND_NEGATED + jnz evaluate_negated_logical_and + call evaluate_logical_value + mov ebx,[condition_stack] + test al,al + jnz continue_evaluation + determined_false: + mov byte [ebx],COND_DETERMINED + jmp get_logical_value + evaluate_negated_logical_and: + call evaluate_logical_value + mov ebx,[condition_stack] + test al,al + jnz determined_false + continue_evaluation: + mov byte [ebx],COND_EVALUATE + jmp get_logical_value + logical_or: + and [current_constituent],0 + mov ebx,[condition_stack] + mov al,[ebx] + test al,COND_EVALUATE + jnz evaluate_logical_or + test al,COND_DETERMINED + jz get_logical_value + test al,COND_TRUE + jnz determined_true + jmp continue_evaluation + evaluate_logical_or: + test al,COND_NEGATED + jnz evaluate_negated_logical_or + call evaluate_logical_value + mov ebx,[condition_stack] + test al,al + jnz determined_true + jmp continue_evaluation + evaluate_negated_logical_or: + call evaluate_logical_value + mov ebx,[condition_stack] + test al,al + jnz continue_evaluation + determined_true: + mov byte [ebx],COND_DETERMINED + COND_TRUE + jmp get_logical_value + close_logical_subexpression: + and [current_constituent],0 + mov ebx,[condition_stack] + cmp ebx,[condition_stack_base] + je excess_parenthesis + mov al,[ebx] + dec ebx + mov [condition_stack],ebx + test al,COND_DETERMINED + jnz subexpression_determined + cmp al,COND_EVALUATE + COND_NEGATED + jne get_logical_operator + test byte [ebx],COND_EVALUATE + jz get_logical_operator + xor byte [ebx],COND_NEGATED + jmp get_logical_operator + subexpression_determined: + test al,COND_TRUE + jnz subexpression_determined_true + mov [comparator],evaluated_false + jmp get_logical_operator + subexpression_determined_true: + mov [comparator],evaluated_true + jmp get_logical_operator + end_of_logical_expression: + mov ebx,[condition_stack] + cmp ebx,[condition_stack_base] + jne missing_parenthesis + mov al,[ebx] + test al,COND_DETERMINED + jnz condition_determined + test al,COND_NEGATED + jz evaluate_logical_value + call evaluate_logical_value + test al,al + setz al + retn + condition_determined: + and al,COND_TRUE + retn + excess_parenthesis: + mov edx,_excess_closing_parenthesis + call register_error + jmp unknown_condition + missing_parenthesis: + mov edx,_missing_closing_parenthesis + call register_error + unknown_condition: + xor al,al + retn + +parse_logical_value: +; in: +; esi = pointer into preprocessed line or the last embedded line +; out: +; [comparator] - evaluating routine +; [expression_workspace.memory_start] - parsed argument sequences +; [expression_end] - end of the parsed argument sequences +; esi = pointer advanced past the parsed value +; al = special character that follows parsed value, zero if no more symbols in line +; ecx = number of parentheses opened before the value that did not get closed +; cf set if value was empty + mov edi,[expression_workspace.memory_start] + xor eax,eax + mov [comparator],eax + or [leave_opening_parentheses],1 + call parse_expression + mov [initial_parentheses],ecx + mov [expression_end],edi + call peek_at_constituent_value + jc end_of_line + cmp al,'&' + je end_of_logical_value + cmp al,'|' + je end_of_logical_value + cmp al,'~' + je end_of_logical_value + cmp al,')' + je end_of_logical_value + cmp al,1Ah + je identify_comparator + cmp al,'=' + je parse_equal + cmp al,'<' + je parse_less + cmp al,'>' + je parse_greater + jmp end_of_logical_value + identify_comparator: + test edx,edx + jz end_of_logical_value + cmp [edx+ValueDefinition.type],VALTYPE_NATIVE_COMPARATOR + jne end_of_logical_value + and [current_constituent],0 + mov eax,[edx+ValueDefinition.value] + jmp set_comparator + parse_less: + and [current_constituent],0 + call warp_to_next_symbol + jc parse_less_than + test ecx,ecx + jnz parse_less_than + call peek_at_constituent_value + jc parse_less_than + cmp al,'=' + je parse_less_or_equal + cmp al,'>' + je parse_not_equal + parse_less_than: + mov eax,check_if_less + jmp set_comparator + parse_less_or_equal: + and [current_constituent],0 + mov eax,check_if_not_greater + jmp set_comparator + parse_not_equal: + and [current_constituent],0 + mov eax,check_if_not_equal + jmp set_comparator + parse_greater: + and [current_constituent],0 + call warp_to_next_symbol + jc parse_greater_than + test ecx,ecx + jnz parse_greater_than + call peek_at_constituent_value + jc parse_greater_than + cmp al,'=' + je parse_greater_or_equal + parse_greater_than: + mov eax,check_if_greater + jmp set_comparator + parse_greater_or_equal: + and [current_constituent],0 + mov eax,check_if_not_less + jmp set_comparator + parse_equal: + and [current_constituent],0 + mov eax,check_if_equal + set_comparator: + mov edi,[expression_end] + mov [comparator],eax + and [leave_opening_parentheses],0 + call parse_expression + mov [expression_end],edi + call peek_at_constituent_value + jnc end_of_logical_value + end_of_line: + xor al,al + end_of_logical_value: + mov ecx,[initial_parentheses] + cmp [comparator],0 + jnz logical_value_not_empty + mov [comparator],check_if_not_zero + mov ebx,[expression_workspace.memory_start] + cmp dword [ebx],0 + jne logical_value_not_empty + stc + retn + logical_value_not_empty: + clc + retn + +evaluate_logical_value: +; in: +; [comparator] - evaluating routine +; [expression_workspace.memory_start] - parsed argument sequences +; out: al = logical value +; note: evaluates value prepared by previous call to parse_logical_value +; preserves: esi + push esi + mov esi,[expression_workspace.memory_start] + jmp [comparator] + evaluated_false: + pop esi + xor al,al + retn + evaluated_true: + pop esi + mov al,1 + retn + +evaluate_stored_logical_value: +; in: esi - pointer to evaluating routine followed by parsed argument sequences +; out: al = logical value +; preserves: esi + push esi + lodsd + jmp eax + +invalid_logical_value: + mov edx,_invalid_expression + call register_error + jmp evaluated_false + +check_if_equal: + call get_difference_signum + test al,al + jz evaluated_true + jmp evaluated_false +check_if_not_equal: + call get_difference_signum + test al,al + jnz evaluated_true + jmp evaluated_false +check_if_less: + call get_difference_signum + cmp al,0 + jl evaluated_true + jmp evaluated_false +check_if_not_less: + call get_difference_signum + cmp al,0 + jnl evaluated_true + jmp evaluated_false +check_if_greater: + call get_difference_signum + cmp al,0 + jg evaluated_true + jmp evaluated_false +check_if_not_greater: + call get_difference_signum + cmp al,0 + jng evaluated_true + jmp evaluated_false + get_difference_signum: + mov edi,[calculation_workspace.memory_start] + call calculate_parsed_expression + jc signum_error + call calculate_parsed_expression + jc signum_error + mov esi,subtraction_operator + call calculate_parsed_expression + call pop_terms + mov eax,edi + jnc check_difference_for_variable_terms + signum_error: + xor al,al + retn + check_difference_for_variable_terms: + add eax,sizeof.ExpressionTerm + cmp [eax+ExpressionTerm.attributes],0 + je difference_terms_ok + cmp [eax+ExpressionTerm.metadata],0 + je check_difference_for_variable_terms + mov edx,_values_not_comparable + call register_error + difference_terms_ok: + cmp byte [edi+ExpressionTerm.attributes],EXPR_FLOAT + je get_float_signum + call get_numeric_term_value + mov ecx,1 + call fit_value + js signum_negative + jc signum_positive + xor al,al + cmp [edi],al + jne signum_positive + retn + get_float_signum: + call get_term_value + mov esi,edx + call get_float_exponent + jz signum_zero + test [esi+FloatData.attributes],FLOAT_NEGATIVE + jnz signum_negative + signum_positive: + mov al,1 + retn + signum_negative: + or al,-1 + retn + signum_zero: + xor al,al + retn + +check_if_not_zero: + mov edi,[calculation_workspace.memory_start] + call calculate_parsed_expression + jc evaluated_false + call pop_terms + jc evaluated_false + cmp byte [edi+ExpressionTerm.attributes],EXPR_FLOAT + je check_if_not_zero_float + call get_numeric_term_value + mov ecx,1 + call fit_value + js evaluated_true + jc evaluated_true + cmp byte [edi],0 + jne evaluated_true + check_if_has_variable_terms: + add edi,sizeof.ExpressionTerm + cmp [edi+ExpressionTerm.attributes],0 + je evaluated_false + cmp [edi+ExpressionTerm.metadata],0 + je check_if_has_variable_terms + jmp evaluated_true + check_if_not_zero_float: + call get_term_value + mov esi,edx + call get_float_exponent + jz check_if_has_variable_terms + jmp evaluated_true + +check_if_relative: + mov edi,[calculation_workspace.memory_start] + call calculate_parsed_expression + jc evaluated_false + call calculate_parsed_expression + jc evaluated_false + mov ebx,edi + call pop_terms + jc evaluated_false + mov edx,edi + call pop_terms + jc evaluated_false + cmp [edx+sizeof.ExpressionTerm+ExpressionTerm.attributes],0 + je check_difference_terms + xchg edi,edx + cmp [edx+sizeof.ExpressionTerm+ExpressionTerm.attributes],0 + je check_difference_terms + mov esi,subtraction_operator + mov edi,ebx + call calculate_parsed_expression + call pop_terms + jc evaluated_false + check_difference_terms: + add edi,sizeof.ExpressionTerm + cmp [edi+ExpressionTerm.attributes],0 + je evaluated_true + cmp [edi+ExpressionTerm.metadata],0 + je check_difference_terms + jmp evaluated_false + undefined_condition: + mov edx,_invalid_value + call register_error + jmp evaluated_false + +check_if_type_equal: + mov edi,[calculation_workspace.memory_start] + call calculate_parsed_expression + jc evaluated_false + call pop_terms + jc evaluated_false + mov eax,[edi+ExpressionTerm.attributes] + mov [result_type],al + call calculate_parsed_expression + jc evaluated_false + call pop_terms + jc evaluated_false + mov eax,[edi+ExpressionTerm.attributes] + cmp al,[result_type] + je evaluated_true + jmp evaluated_false + +check_if_value_equal: + mov edi,[calculation_workspace.memory_start] + call calculate_parsed_expression + jc evaluated_false + mov ebx,edi + call pop_terms + jc evaluated_false + mov eax,[edi+ExpressionTerm.attributes] + mov [result_type],al + mov edi,ebx + call calculate_parsed_expression + jc evaluated_false + mov ebx,edi + call pop_terms + jc evaluated_false + mov eax,[edi+ExpressionTerm.attributes] + cmp al,[result_type] + jne evaluated_false + cmp al,EXPR_STRING + jne compare_values_numerically + call get_term_value + mov ecx,[edx] + call pop_terms + call get_term_value + cmp ecx,[edx] + jne evaluated_false + compare_values_numerically: + mov esi,subtraction_operator + mov edi,ebx + call calculate_parsed_expression + call pop_terms + jc evaluated_false + mov eax,edi + check_if_terms_equal: + add eax,sizeof.ExpressionTerm + cmp [eax+ExpressionTerm.attributes],0 + je check_if_constant_term_equal + cmp [eax+ExpressionTerm.metadata],0 + je check_if_terms_equal + jmp evaluated_false + check_if_constant_term_equal: + cmp byte [edi+ExpressionTerm.attributes],EXPR_FLOAT + je check_if_float_equal + call get_numeric_term_value + xor ecx,ecx + xor edi,edi + call fit_value + jc evaluated_false + jmp evaluated_true + check_if_float_equal: + call get_term_value + mov esi,edx + call get_float_exponent + jnz evaluated_false + jmp evaluated_true + +check_if_defined: + xor ecx,ecx + jmp check_if_expression_defined +check_if_defined_earlier: + mov ecx,[current_pass] + check_if_expression_defined: + and [outer_expression],0 + and [defined_element],0 + lodsd + test eax,eax + jnz checked_expression_invalid + check_expression_element: + lodsd + test eax,eax + jz check_expression_end + cmp al,EXPR_SYMBOL + je check_if_subexpression_defined + cmp al,EXPR_SYMBOL_VALUE + je check_if_symbol_defined + or [defined_element],1 + add esi,4 + cmp al,EXPR_NUMBER + je check_expression_element + cmp al,EXPR_STRING + je check_expression_element + cmp al,EXPR_FLOAT + je check_expression_element + cmp al,EXPR_OPERATOR + je check_expression_element + checked_expression_invalid: + jmp invalid_logical_value + check_expression_end: + xor esi,esi + xchg esi,[outer_expression] + test esi,esi + jnz check_expression_element + jecxz checked_expression_defined + cmp [defined_element],0 + je checked_expression_invalid + checked_expression_defined: + jmp evaluated_true + check_if_symbol_defined: + lodsd + test eax,eax + jz evaluated_false + lodsd + test eax,eax + jz evaluated_false + check_symbol_value: + or [defined_element],1 + jecxz check_expression_element + test [eax+ValueDefinition.flags],VAL_INTERNAL + jnz check_expression_element + cmp [eax+ValueDefinition.pass],ecx + jne evaluated_false + jmp check_expression_element + check_if_subexpression_defined: + mov ebx,[esi] + test ebx,ebx + jz evaluated_false + add esi,4 + call get_available_value + mov eax,edx + test eax,eax + jz evaluated_false + cmp [eax+ValueDefinition.type],VALTYPE_SYMBOLIC + jne check_symbol_value + mov [outer_expression],esi + push ecx + call get_subexpression + pop ecx + jnc invalid_logical_value + jmp check_expression_element + get_subexpression: + call clear_line_embeddings + xor esi,esi + xor ecx,ecx + call embed_symbolic_value + mov edi,[expression_workspace.memory_start] + and [leave_opening_parentheses],0 + call parse_expression + call get_constituent_value + mov esi,[expression_workspace.memory_start] + retn + +check_if_used: + lodsd + test eax,eax + jnz invalid_logical_value + check_if_expression_is_used_symbol: + lodsd + cmp al,EXPR_SYMBOL + je check_for_indirect_symbol + cmp al,EXPR_SYMBOL_VALUE + jne invalid_logical_value + lodsd + mov ebx,eax + lodsd + check_if_used_symbol: + lodsd + test eax,eax + jnz invalid_logical_value + test ebx,ebx + jz evaluated_false + mov ecx,[ebx+SymbolTree_Leaf.last_use_pass] + jecxz symbol_predicted_unused + mov eax,[current_pass] + sub eax,ecx + jz evaluated_true + cmp eax,1 + ja symbol_predicted_unused + symbol_predicted_used: + or [ebx+SymbolTree_Leaf.flags],SYM_USAGE_PREDICTED + SYM_PREDICTED_USED + jmp evaluated_true + symbol_predicted_unused: + mov al,[ebx+SymbolTree_Leaf.flags] + or al,SYM_USAGE_PREDICTED + and al,not SYM_PREDICTED_USED + mov [ebx+SymbolTree_Leaf.flags],al + jmp evaluated_false + check_for_indirect_symbol: + lodsd + mov ebx,eax + call get_available_value + test edx,edx + jz check_if_used_symbol + cmp [edx+ValueDefinition.type],VALTYPE_SYMBOLIC + jne check_if_used_symbol + call get_subexpression + jnc invalid_logical_value + jmp check_if_expression_is_used_symbol diff --git a/x86_64_sse2_x87/fasm/source/console.inc b/x86_64_sse2_x87/fasm/source/console.inc new file mode 100644 index 0000000..eb1ebea --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/console.inc @@ -0,0 +1,479 @@ + +show_display_data: + test [trace_mode],TRACE_DISPLAY + jnz display_line_feed + mov ecx,[display_data_length] + jecxz display_data_shown + mov esi,[display_buffer] + call display_string + display_line_feed: + mov esi,_new_line + xor ecx,ecx + call display_string + display_data_shown: + retn + +show_errors: + mov esi,[first_error] + display_error: + test esi,esi + jz display_data_shown + push esi + mov eax,[esi+sizeof.Error+SourceContext.number_of_entries] + test eax,eax + jz show_error_message + lea ebx,[esi+sizeof.Error+sizeof.SourceContext] + dec eax + imul eax,sizeof.SourceEntry + lea eax,[ebx+eax] + mov [last_source_entry],eax + test [trace_mode],TRACE_ERROR_STACK + jnz show_source_context + and [last_file_source_entry],0 + find_last_file_entry: + cmp [eax+SourceEntry.type],SOURCE_FILE + je last_file_entry_found + cmp eax,ebx + je show_source_context + sub eax,sizeof.SourceEntry + jmp find_last_file_entry + last_file_entry_found: + mov [last_file_source_entry],eax + show_source_context: + push ebx + cmp [ebx+SourceEntry.type],SOURCE_MEMORY + je display_memory_source + cmp [ebx+SourceEntry.type],SOURCE_MACRO + jne display_source_name + mov esi,_macro_source + test [ebx+SourceEntry.flags],SRCF_PREPROCESSED + jz display_source_type + mov esi,_preprocessed_source + display_source_type: + xor ecx,ecx + call display_error_string + display_source_name: + mov esi,[ebx+SourceEntry.name] + test esi,esi + jz unnamed_source + mov ecx,[ebx+SourceEntry.name_length] + cmp ecx,-1 + je display_source_symbol + call display_error_string + jmp display_line_number + display_source_symbol: + xchg ebx,esi + call show_symbol_name + mov ebx,esi + jmp display_line_number + unnamed_source: + mov esi,_unnamed_source + xor ecx,ecx + call display_error_string + jmp display_line_number + display_memory_source: + mov esi,_memory_source + xor ecx,ecx + call display_error_string + display_line_number: + mov esi,_line_number_prefix + xor ecx,ecx + call display_error_string + mov eax,[ebx+SourceEntry.line_number] + xor edx,edx + call itoa + call display_error_string + mov esi,_line_number_suffix + xor ecx,ecx + call display_error_string + pop ebx + mov esi,[esp] + push ebx + cmp [ebx+SourceEntry.line_number],0 + je skip_line_content + test [trace_mode],TRACE_ERROR_STACK + jnz show_source_line + cmp ebx,[last_source_entry] + je last_source_entry_line_content + cmp ebx,[last_file_source_entry] + je show_source_line + skip_line_content: + mov esi,_space + next_source_entry: + pop ebx + find_next_source_entry: + cmp ebx,[last_source_entry] + je source_context_shown + add ebx,sizeof.SourceEntry + test [trace_mode],TRACE_ERROR_STACK + jnz show_source_entry + test [ebx+SourceEntry.flags],SRCF_PREPROCESSED + jnz find_next_source_entry + show_source_entry: + xor ecx,ecx + call display_error_string + jmp show_source_context + last_source_entry_line_content: + test [esi+Error.flags],ERR_CUSTOM + jnz skip_line_content + show_source_line: + cmp [ebx+SourceEntry.type],SOURCE_CALM + je show_calm_source + mov esi,_line_content_prefix + xor ecx,ecx + call display_error_string + call show_line_content + mov esi,_new_line + jmp next_source_entry + show_calm_source: + mov esi,_calm_source + xor ecx,ecx + call display_error_string + mov esi,_new_line + jmp next_source_entry + source_context_shown: + mov esi,_new_line + xor ecx,ecx + call display_error_string + mov ebx,[esp] + test [ebx+Error.flags],ERR_CUSTOM + jnz show_error_message + cmp [ebx+Error.preprocessed_length],0 + je show_error_message + mov esi,_preprocessed_text_prefix + xor ecx,ecx + call display_error_string + mov esi,[ebx+Error.preprocessed_data] + mov ecx,[ebx+Error.preprocessed_length] + call show_preprocessed_line + mov esi,_new_line + xor ecx,ecx + call display_error_string + show_error_message: + pop ebx + mov esi,_error_prefix + test [ebx+Error.flags],ERR_CUSTOM + jz display_error_prefix + mov esi,_custom_error_prefix + display_error_prefix: + xor ecx,ecx + call display_error_string + mov esi,[ebx+Error.message] + test [ebx+Error.flags],ERR_CUSTOM + jz format_error_message + xor ecx,ecx + call display_error_string + finish_error_message: + mov esi,_message_suffix + xor ecx,ecx + call display_error_string + mov esi,ebx + next_error: + mov esi,[esi+Error.next] + jmp display_error + format_error_message: + mov edx,esi + cut_error_message: + lodsb + test al,al + jz show_error_message_segment + cmp al,'%' + jne cut_error_message + show_error_message_segment: + dec esi + push esi + xchg ecx,esi + sub ecx,edx + mov esi,edx + call display_error_string + pop esi + lodsb + test al,al + jz finish_error_message + lodsb + cmp al,'s' + je insert_string_into_error_message + cmp al,'i' + jne format_error_message + push ebx + mov ebx,[ebx+Error.symbol] + mov ebx,[ebx+SymbolTree_Leaf.branch] + call show_symbol_name + pop ebx + jmp format_error_message + insert_string_into_error_message: + push esi + mov esi,[ebx+Error.symbol] + xor ecx,ecx + call display_error_string + pop esi + jmp format_error_message + +show_symbol_name: +; in: +; ebx - SymbolTree_Foliage, may be null +; preserves: esi + test ebx,ebx + jz symbol_name_shown + mov edi,[identifier_workspace.memory_start] + cmp [ebx+SymbolTree_Foliage.name_kind],NAME_NUMERIC + je next_name_segment + compose_symbol_name: + mov ecx,[ebx+SymbolTree_Foliage.name_length] + mov edx,identifier_workspace + mov al,[ebx+SymbolTree_Foliage.name_kind] + cmp al,NAME_CASESENSITIVE + je name_segment_to_copy + cmp al,NAME_ABSTRACT + je name_segment_copied + cmp al,NAME_NUMERIC + je dot_label_name + mov al,'?' + stosb + name_segment_to_copy: + push ecx + add ecx,2 + call reserve_workspace + pop ecx + mov edx,[ebx+SymbolTree_Foliage.name_data] + copy_name_segment: + jecxz name_segment_copied + dec ecx + mov al,[edx+ecx] + stosb + jmp copy_name_segment + dot_label_name: + push esi + mov esi,[ebx+SymbolTree_Foliage.name_data] + xor eax,eax + read_dot_count: + jecxz dot_count_read + dec ecx + shl eax,8 + mov al,[esi+ecx] + jmp read_dot_count + dot_count_read: + pop esi + push eax + lea ecx,[eax+2] + call reserve_workspace + pop ecx + mov al,'.' + rep stosb + name_segment_copied: + mov edx,[ebx+SymbolTree_Foliage.root] + mov ebx,[edx+SymbolTree_Root.parent_branch] + test [edx+SymbolTree_Root.flags],NAMESPACE_LOCAL or NAMESPACE_CALM + jnz mark_local_symbol_name + test ebx,ebx + jz symbol_name_ready + next_name_segment: + mov al,'.' + stosb + jmp compose_symbol_name + mark_local_symbol_name: + mov al,':' + stosb + test [edx+SymbolTree_Root.flags],NAMESPACE_CALM + jz symbol_name_ready + mov eax,[ebx+SymbolTree_Foliage.name_data] + mov ebx,[eax+SymbolTree_Leaf.branch] + test ebx,ebx + jnz compose_symbol_name + symbol_name_ready: + mov ebx,[identifier_workspace.memory_start] + mov ecx,edi + sub ecx,ebx + jz symbol_name_shown + push esi + mov esi,ebx + reverse_composed_name: + dec edi + cmp ebx,edi + jae show_composed_name + mov al,[ebx] + xchg al,[edi] + mov [ebx],al + inc ebx + jmp reverse_composed_name + show_composed_name: + call display_error_string + pop esi + symbol_name_shown: + retn + +show_line_content: +; in: +; ebx - SourceEntry + cmp [ebx+SourceEntry.type],SOURCE_MACRO + je show_line_from_macro + mov esi,[ebx+SourceEntry.text] + add esi,[ebx+SourceEntry.line_offset] + mov ecx,[ebx+SourceEntry.number_of_attached_lines] + inc ecx + mov [number_of_lines],ecx + show_token: + mov al,[esi] + test al,al + jz line_content_shown + cmp al,0Ah + je line_content_shown + cmp al,1Ah + je show_name_token + cmp al,22h + je show_string_token + cmp al,27h + je show_string_token + cmp al,'\' + jne show_basic_token + cmp byte [esi+1],0Ah + jne show_basic_token + dec [number_of_lines] + jnz show_attached_line + show_basic_token: + mov ecx,1 + call display_error_string + inc esi + jmp show_token + show_name_token: + add esi,1+4 + mov ecx,[esi-4] + call display_error_string + add esi,[esi-4] + add esi,12 + jmp show_token + show_string_token: + mov ebx,esi + inc esi + call show_string_token_content + lea esi,[ebx+1] + lodsd + add esi,eax + jmp show_token + show_string_token_content: + lea edi,[esi+4] + mov ecx,[esi] + show_string_segment: + push ecx edi + mov esi,_single_quote + mov ecx,1 + call display_error_string + pop edi ecx + jecxz show_end_quote + mov edx,ecx + mov al,27h + repne scasb + sub edx,ecx + mov esi,edi + sub esi,edx + push ecx edi + mov ecx,edx + call display_error_string + pop edi ecx + test ecx,ecx + jnz show_string_segment + show_end_quote: + cmp byte [ebx],27h + je string_token_shown + mov esi,_single_quote + mov ecx,1 + call display_error_string + string_token_shown: + retn + show_attached_line: + mov ecx,1 + call display_error_string + lea ebx,[esi+2] + mov esi,_line_segment_prefix + xor ecx,ecx + call display_error_string + mov esi,ebx + jmp show_token + show_line_from_macro: + mov edx,[ebx+SourceEntry.text] + mov esi,[edx+ValueDefinition.value] + mov ecx,[edx+ValueDefinition.value_length] + mov eax,[ebx+SourceEntry.line_offset] + add esi,eax + sub ecx,eax + jbe line_content_shown + call show_preprocessed_line + line_content_shown: + retn + +show_preprocessed_line: +; in: +; esi - preprocessed tokens +; ecx = total length of preprocessed tokens + lea eax,[esi+ecx] + mov [preprocessed_text_end],eax + show_preprocessed_token: + cmp esi,[preprocessed_text_end] + jae preprocessed_line_shown + mov al,[esi] + test al,al + jz preprocessed_line_shown + cmp al,1Ah + je show_preprocessed_name_token + cmp al,22h + je show_preprocessed_string_token + cmp al,27h + je show_preprocessed_string_token + cmp al,30h + je show_internal_number + cmp al,40h + je show_context_token + mov ecx,1 + call display_error_string + inc esi + jmp show_preprocessed_token + show_preprocessed_name_token: + inc esi + lodsd + mov ebx,esi + mov esi,eax + lodsd + mov ecx,eax + call display_error_string + mov esi,ebx + jmp show_preprocessed_token + show_preprocessed_string_token: + mov ebx,esi + mov esi,[esi+1] + call show_string_token_content + lea esi,[ebx+1+4] + jmp show_preprocessed_token + show_internal_number: + inc esi + mov edx,esi + push esi + call convert_number_back + lea esi,[edx+4] + mov ecx,[edx] + call display_error_string + pop esi + add esi,[esi] + add esi,4 + jmp show_preprocessed_token + show_context_token: + add esi,1+sizeof.RecognitionContext + jmp show_preprocessed_token + preprocessed_line_shown: + retn + +itoa: +; in: +; edx:eax = unsigned number +; out: +; esi - temporary buffer containing decimal digits +; ecx = length of string (number of digits) + mov edi,temporary_value+4 + stosd + mov eax,edx + stosd + mov edx,temporary_value + mov dword [edx],8 + call convert_number_back + lea esi,[edx+4] + mov ecx,[edx] + retn diff --git a/x86_64_sse2_x87/fasm/source/directives.inc b/x86_64_sse2_x87/fasm/source/directives.inc new file mode 100644 index 0000000..280fb4c --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/directives.inc @@ -0,0 +1,5013 @@ + +struct DirectiveBlock + type db ? ; DBLOCK_# + subtype db ? ; CTRL_# for DBLOCK_CONTROL + flags db ? ; CTRLF_# for DBLOCK_CONTROL + prior_assembly_mode db ? + prior_special_parameters db ? + prior_counter_position dd ? + parameter_namespace dd ? + parameter dd ? ; pointer to first BlockParameter + length_of_data dd ? +ends + +struct BlockParameter + symbol dd ? ; pointer to SymbolTree_Leaf + definition dd ? ; pointer to ValueDefinition + next dd ? ; pointer to another BlockParameter +ends + +struct NamespaceData + prior_namespace dd ? + prior_label dd ? +ends + +struct VirtualBlockData + outer_area dd ? + shift_tracking db ? +ends + +struct ConditionalRepeatData + condition_length dd ? +ends + +struct RepeatData + limit_length dd ? + index_position dd ? +ends + +struct MacroData + nesting_counter dd ? +ends + +struct SymbolSubstitution + symbol_start dd ? + symbol_end dd ? + value_start dd ? + value_end dd ? + leftover_context dd ? +ends + +struct MatchedParameter + pattern dd ? + assembly_position dd ? +ends + +DBLOCK_CLOSED = 0 +DBLOCK_NAMESPACE = 1 +DBLOCK_CONTROL = 2 +DBLOCK_MACRO = 3 +DBLOCK_VIRTUAL = 4 +DBLOCK_CALMINSTRUCTION = 5 +DBLOCK_POSTPONED = 8 +DBLOCK_SUSPENDED = 9 + +CTRL_IF = 0 +CTRL_WHILE = 2 +CTRL_REPEAT = 3 +CTRL_IRP = 4 +CTRL_IRPV = 5 +CTRL_MATCH = 6 +CTRL_RMATCH = 7 +CTRL_POSTPONE = 9 + +CTRLF_INACTIVE = 1 +CTRLF_BREAKABLE = 2 +CTRLF_HAS_REPEAT_DATA = 4 +CTRLF_HAS_WRITABLE_INDEX = 8 +CTRLF_ALLOWS_ELSE = 16 +CTRLF_SUSPENDED = 32 + +add_directive_block: +; in: +; dl = DBLOCK_# +; ecx = length of additional data +; out: edi - new DirectiveBlock +; note: area for additional data is placed below the DirectiveBlock structure +; preserves: ebx, edx, esi + push esi ecx + mov esi,[source_context] + mov eax,[esi+SourceContext.number_of_entries] + imul eax,sizeof.SourceEntry + add eax,sizeof.SourceContext + add ecx,eax + add ecx,sizeof.DirectiveBlock + mov edi,[directives_stack] + lea eax,[edi+ecx] + cmp eax,[directives_stack_end] + jbe directives_stack_ready + add ecx,[directives_stack_end] + jc out_of_memory + mov eax,[directives_stack_base] + sub ecx,eax + push edx + call grow_stack + pop edx + add ecx,eax + mov [directives_stack_end],ecx + mov edi,eax + xchg eax,[directives_stack_base] + sub edi,eax + add edi,[directives_stack] + mov [directives_stack],edi + directives_stack_ready: + push edx + call clone_source_context + pop edx ecx + add edi,ecx + mov eax,edi + sub eax,[directives_stack] + mov [edi+DirectiveBlock.length_of_data],eax + mov [edi+DirectiveBlock.type],dl + mov al,[assembly_mode] + mov [edi+DirectiveBlock.prior_assembly_mode],al + mov ecx,[parameter_namespace] + mov [edi+DirectiveBlock.parameter_namespace],ecx + mov al,[ecx+SymbolTree_Root.parameters] + mov [edi+DirectiveBlock.prior_special_parameters],al + xor eax,eax + mov [edi+DirectiveBlock.subtype],al + mov [edi+DirectiveBlock.flags],al + mov [edi+DirectiveBlock.parameter],eax + mov eax,[current_counter] + sub eax,[counters_stack_base] + mov [edi+DirectiveBlock.prior_counter_position],eax + lea eax,[edi+sizeof.DirectiveBlock] + mov [directives_stack],eax + pop esi + retn + +find_directive_block: +; in: +; dl = DBLOCK_# +; out: +; cf set when no such block found +; when cf = 0 +; edi - DirectiveBlock of the latest block of this type +; preserves: ebx, edx, esi + mov edi,[directives_stack] + scan_directive_blocks: + cmp edi,[directives_stack_base] + je directive_block_not_found + sub edi,sizeof.DirectiveBlock + cmp dl,[edi+DirectiveBlock.type] + je directive_block_found + find_next_directive_block: + ; in: + ; dl = DBLOCK_# + ; edi - DirectiveBlock + ; out: + ; cf set when no such block found + ; when cf = 0 + ; edi - DirectiveBlock of the next latest block of this type + ; preserves: ebx, edx, esi + mov ecx,[edi+DirectiveBlock.length_of_data] + sub edi,ecx + jmp scan_directive_blocks + directive_block_found: + clc + retn + directive_block_not_found: + stc + retn + +close_control_directive_block: +; in: edi - DirectiveBlock +; preserves: esi + mov al,[edi+DirectiveBlock.prior_assembly_mode] + mov [assembly_mode],al + mov eax,[edi+DirectiveBlock.prior_counter_position] + add eax,[counters_stack_base] + mov [current_counter],eax + mov ecx,[edi+DirectiveBlock.parameter_namespace] + mov al,[edi+DirectiveBlock.prior_special_parameters] + mov [ecx+SymbolTree_Root.parameters],al + close_directive_block: + ; in: edi - DirectiveBlock + ; preserves: esi + mov [edi+DirectiveBlock.type],DBLOCK_CLOSED + mov eax,edi + mov ecx,[eax+DirectiveBlock.length_of_data] + sub eax,ecx + call release_source_context + call remove_block_parameters + lea eax,[edi+sizeof.DirectiveBlock] + cmp eax,[directives_stack] + je remove_directive_block + retn + remove_directive_block: + sub eax,sizeof.DirectiveBlock + mov ecx,[eax+DirectiveBlock.length_of_data] + sub eax,ecx + cmp eax,[directives_stack_base] + je directives_stack_cleared + cmp [eax-sizeof.DirectiveBlock+DirectiveBlock.type],DBLOCK_CLOSED + je remove_directive_block + directives_stack_cleared: + mov [directives_stack],eax + retn + +add_block_parameter: +; in: +; ebx - SymbolTree_Leaf +; edi - DirectiveBlock +; esi - value +; ecx = length of value +; [value_type] = VALTYPE_# +; preserves: ebx, edi + push ecx + or [ebx+SymbolTree_Leaf.flags],SYM_VARIABLE + call create_value_definition + pop ecx + mov eax,[current_pass] + mov [edx+ValueDefinition.pass],eax + mov [edx+ValueDefinition.value_length],ecx + add ecx,sizeof.BlockParameter + push edi + mov edi,[edx+ValueDefinition.value] + cmp ecx,[edx+ValueDefinition.block_length] + jbe write_parameter_value + push edx + push ecx + cmp [edx+ValueDefinition.block_length],0 + je allocate_memory_for_parameter + xor eax,eax + xchg eax,[edx+ValueDefinition.value] + call mfree + allocate_memory_for_parameter: + pop ecx + call malloc + pop edx + mov [edx+ValueDefinition.value],eax + mov [edx+ValueDefinition.block_length],ecx + mov edi,eax + write_parameter_value: + mov al,[value_type] + mov [edx+ValueDefinition.type],al + mov eax,[edx+ValueDefinition.value_length] + mov ecx,eax + shr ecx,2 + rep movsd + mov cl,al + and cl,11b + rep movsb + mov ecx,edi + pop edi + mov [ecx+BlockParameter.symbol],ebx + mov [ecx+BlockParameter.definition],edx + inc [edx+ValueDefinition.reference_count] + mov eax,ecx + xchg eax,[edi+DirectiveBlock.parameter] + mov [ecx+BlockParameter.next],eax + retn + +remove_block_parameters: +; in: edi - DirectiveBlock +; preserves: esi, edi + mov eax,[edi+DirectiveBlock.parameter] + test eax,eax + jz no_block_parameters + push esi edi + mov esi,eax + remove_block_parameter: + mov ebx,[esi+BlockParameter.symbol] + mov edx,[esi+BlockParameter.definition] + dec [edx+ValueDefinition.reference_count] + xor edi,edi + call remove_value_definition + mov esi,[esi+BlockParameter.next] + test esi,esi + jnz remove_block_parameter + pop edi esi + no_block_parameters: + retn + +identify_parameter_symbol: +; in: +; esi = pointer into preprocessed line or current embedded value +; out: +; same as from identify_symbol_in_namespace + or [symbol_definition],1 + mov ebx,[parameter_namespace] + mov dl,SYMCLASS_PARAMETER + call identify_symbol_in_namespace + jc parameter_symbol_identified + cmp edi,[parameter_namespace] + je parameter_symbol_identified + xor ebx,ebx + parameter_symbol_identified: + retn + +cut_argument_value: +; in: +; esi = pointer into preprocessed line or current embedded value +; edi - LineExcerpt to be filled with information about cut piece of line +; [breakpoint_token] = initial byte of symbol that - if encountered - should immediately end the value +; out: +; esi = pointer advanced past the cut piece +; al = initial byte of symbol at pointer, zero when no more symbols there +; preserves: ebx, edi + call move_to_next_symbol + jc cut_plain_value + cmp byte [esi],'<' + je cut_enclosed_value + cut_plain_value: + mov dl,',' + xor dh,dh + call cut_piece_of_line + retn + cut_enclosed_value: + inc esi + mov dl,'>' + mov dh,'<' + call cut_piece_of_line + cmp al,'>' + jne missing_enclosing + inc esi + call move_to_next_symbol + jnc parameter_value_cut + xor al,al + parameter_value_cut: + retn + missing_enclosing: + mov edx,_missing_closing_chevron + call register_error + retn + +extract_argument_value: +; in: +; esi = pointer into preprocessed line (not an embedded value) +; edi - buffer for token sequence, must be large enough to hold all the remaining tokens in line and an additional context token +; [breakpoint_token] = initial byte of symbol that - if encountered - should immediately end the value +; out: +; esi = pointer advanced past the processed piece +; edi - end of the extracted sequence of tokens in provided buffer +; al = initial byte of symbol at pointer, zero when no more symbols there +; preserves: ebx +; note: +; when AMODE_DEFINITION is active, current context is not added to the extracted value + call move_to_next_symbol + jc extract_plain_value + cmp byte [esi],'<' + je extract_enclosed_value + extract_plain_value: + mov dl,',' + xor dh,dh + call extract_piece_of_line + retn + extract_enclosed_value: + inc esi + mov dl,'>' + mov dh,'<' + call extract_piece_of_line + cmp al,'>' + jne missing_enclosing + inc esi + call move_to_next_symbol + jnc parameter_value_extracted + xor al,al + parameter_value_extracted: + retn + +get_macro_definition_symbol: +; in: +; esi = pointer into preprocessed line or current embedded value +; al = initial byte of symbol at esi +; dl = SYMCLASS_# +; out: +; ebx - SymbolTree_Leaf +; esi = pointer advanced past the processed declaration +; [macro_leaf] - SymbolTree_Leaf +; [macro_flags] = VAL_# + mov [macro_flags],VAL_NONRECURSIVE + cmp al,'!' + je unconditional_interceptor + or [symbol_definition],1 + call identify_symbol + jc invalid_macro_symbol + test ebx,ebx + jz invalid_macro_symbol + macro_symbol_identified: + call move_to_next_symbol + jc macro_symbol_ok + cmp al,':' + je recursive_macro + test ecx,ecx + jnz macro_symbol_ok + cmp al,'!' + je unconditional_macro + jmp macro_symbol_ok + unconditional_interceptor: + mov ebx,[interceptor_symbol] + cmp dl,SYMCLASS_STRUCTURE + jne unconditional_macro + mov ebx,[label_interceptor_symbol] + unconditional_macro: + or [macro_flags],VAL_UNCONDITIONAL + inc esi + call move_to_next_symbol + jc macro_symbol_ok + cmp al,':' + jne macro_symbol_ok + recursive_macro: + inc esi + call move_to_next_symbol + and [macro_flags],not VAL_NONRECURSIVE + or [ebx+SymbolTree_Leaf.flags],SYM_CONSTANT + jmp macro_symbol_ok + macro_symbol_ok: + mov [macro_leaf],ebx + mov edx,[ebx+SymbolTree_Leaf.definition] + test edx,edx + jz create_placeholder_value + test [edx+ValueDefinition.flags],VAL_INTERNAL + jnz macro_already_defined + mov eax,[current_pass] + sub eax,[edx+ValueDefinition.pass] + jz macro_already_defined + cmp eax,1 + je macro_symbol_ready + create_placeholder_value: + ; for VAL_UNCONDITIONAL macros this helps to detect when a macro tries to call itself, + ; for regular ones this section interferes with operation of SYM_PREDICTED to sometimes prevent excess passes, + ; when SYM_PREDICTED is needed to behave reliably for an instruction, its symbol + ; should be marked with SYMX_INSTRUCTION_PREDICTED to skip this section + test [ebx+SymbolTree_Leaf.extra_flags],SYMX_INSTRUCTION_PREDICTED + jnz macro_symbol_ready + call create_value_definition + test edx,edx + jz macro_symbol_ready + mov eax,[current_pass] + dec eax + mov [edx+ValueDefinition.pass],eax + mov [edx+ValueDefinition.type],VALTYPE_RESERVED + mov al,[macro_flags] + mov [edx+ValueDefinition.flags],al + test al,VAL_UNCONDITIONAL + jnz macro_symbol_ready + or [edx+ValueDefinition.flags],VAL_IN_USE + test [ebx+SymbolTree_Leaf.flags],SYM_PREDICTED + jz macro_symbol_ready + mov edi,ebx + mov ecx,[current_pass] + check_macro_usage: + cmp ecx,[edi+SymbolTree_Leaf.last_use_pass] + je macro_possibly_used_earlier + mov edx,[edi+SymbolTree_Leaf.fallback_neighbour] + test edx,edx + jz look_up_parent_namespace_macro + cmp ecx,[edx+SymbolTree_Leaf.last_use_pass] + je macro_possibly_used_earlier + look_up_parent_namespace_macro: + mov edi,[edi+SymbolTree_Leaf.fallback_parent] + test edi,edi + jnz check_macro_usage + and [ebx+SymbolTree_Leaf.flags],not SYM_PREDICTED + jmp macro_symbol_ready + macro_possibly_used_earlier: + test al,VAL_NONRECURSIVE + jnz macro_symbol_ready + or [next_pass_needed],-1 + macro_symbol_ready: + clc + retn + macro_already_defined: + test [ebx+SymbolTree_Leaf.flags],SYM_CONSTANT + jz macro_symbol_ready + mov edx,_conflicting_definition + call register_error + invalid_macro_symbol: + stc + retn + +; instruction handler +; in: +; esi = pointer into preprocessed line +; ecx = number of whitespace tokens between previous symbol and current position +; edx - ValueDefinition of instruction +; ebx - SymbolTree_Leaf of instruction +; edi - SymbolTree_Root of instruction +; when [SymbolTree_Leaf.class] = SYMCLASS_STRUCTURE: +; [label_leaf] - SymbolTree_Leaf of structure label +; [label_branch] - SymbolTree_Foliage of structure label +; [label_solid] = non-zero when initial component of label was a name and not a dot +; out: +; when done, handler should jump to instruction_assembled with esi containing a pointer moved past the processed part of line, +; or jump directly to assembly_line when the rest of line should be ignored +; note: +; when esi is equal to [line_end], pointer is at the end of line and there is no data available at this address + +set_namespace: + mov dl,DBLOCK_NAMESPACE + mov ecx,sizeof.NamespaceData + call add_directive_block + mov edx,[current_context.base_namespace] + mov [edi-sizeof.NamespaceData+NamespaceData.prior_namespace],edx + mov eax,[edx+SymbolTree_Root.current_label] + mov [edi-sizeof.NamespaceData+NamespaceData.prior_label],eax + and [symbol_definition],0 + mov dl,SYMCLASS_EXPRESSION + call identify_symbol + jc missing_argument + test ebx,ebx + jz invalid_identifier + call get_symbol_namespace + mov [current_context.base_namespace],ebx + and [ebx+SymbolTree_Root.current_label],0 + jmp instruction_assembled +end_namespace: + mov dl,DBLOCK_NAMESPACE + call find_directive_block + jc unexpected_instruction + mov edx,[edi-sizeof.NamespaceData+NamespaceData.prior_namespace] + mov eax,[edi-sizeof.NamespaceData+NamespaceData.prior_label] + mov [current_context.base_namespace],edx + mov [edx+SymbolTree_Root.current_label],eax + call close_directive_block + jmp instruction_assembled + +set_base_address: + mov edx,[current_area] + mov ebx,[edx+ValueDefinition.value] + test [ebx+AreaHeader.flags],AREA_VIRTUAL + jnz unexpected_instruction + call get_expression_value + jc invalid_argument + push esi + call convert_terms_to_numeric_value + begin_new_output_area: + call create_output_area + inc [edx+ValueDefinition.reference_count] + xchg edx,[current_area] + dec [edx+ValueDefinition.reference_count] + ; jz internal_error + pop esi + jmp instruction_assembled +begin_new_section: + mov edx,[current_area] + mov ebx,[edx+ValueDefinition.value] + test [ebx+AreaHeader.flags],AREA_VIRTUAL + jnz unexpected_instruction + call get_expression_value + jc invalid_argument + push esi + call convert_terms_to_numeric_value + call trim_output + call create_output_area + mov eax,edx + inc [eax+ValueDefinition.reference_count] + xchg eax,[current_area] + dec [eax+ValueDefinition.reference_count] + ; jz internal_error + pop esi + jmp instruction_assembled +restart_output: + mov edx,[current_area] + mov ebx,[edx+ValueDefinition.value] + test [ebx+AreaHeader.flags],AREA_VIRTUAL + jnz unexpected_instruction + call peek_at_constituent_value + jc restart_output_at_current_address + call get_expression_value + jc invalid_argument + push esi + call convert_terms_to_numeric_value + jmp make_new_initial_output_area + restart_output_at_current_address: + push esi + call get_current_address_value + make_new_initial_output_area: + and [initial_output_area_entry],0 + jmp begin_new_output_area + +define_label: + inc esi + xor ecx,ecx + call move_to_next_symbol + jc define_simple_label + test ecx,ecx + jnz define_simple_label + cmp al,'=' + je define_numeric_constant + cmp al,':' + je define_area_label + define_simple_label: + test [assembly_mode],AMODE_SKIP + jnz assemble_after_label + test [assembly_mode],AMODE_DEFINITION + jnz add_label_to_macro + cmp [interceptor],0 + jne execute_interceptor + mov [argument_start],esi + call get_current_address_value + mov edi,ecx + mov ebx,[label_leaf] + call create_constant_value_definition + test edx,edx + jz assembly_line + mov ecx,edi + call assign_shiftable_value + label_defined: + call update_current_label + mov esi,[argument_start] + assemble_after_label: + mov [line_start],esi + mov eax,[embedded_context] + mov [line_context],eax + jmp assemble_instruction + define_area_label: + inc esi + test [assembly_mode],AMODE_SKIP + jnz assemble_after_label + test [assembly_mode],AMODE_DEFINITION + jnz add_label_to_macro + cmp [interceptor],0 + jne execute_interceptor + mov [argument_start],esi + mov ebx,[label_leaf] + mov edx,[current_area] + call update_value_link + jmp label_defined + assign_shiftable_value: + mov [value_type],VALTYPE_NUMERIC + mov eax,[current_area] + mov eax,[eax+ValueDefinition.value] + test [eax+AreaHeader.flags],AREA_SHIFT_TRACKING_DISABLED + jnz assign_value + or [shift_tracking],-1 + call update_predicted_shift + call assign_value + or [edx+ValueDefinition.flags],VAL_SHIFTABLE + retn + update_current_label: + mov edx,[label_branch] + cmp [label_solid],0 + jne current_label_confirmed + mov eax,[local_namespace] + test eax,eax + jz current_label_ok + cmp edx,[eax+SymbolTree_Root.current_label] + jne current_label_ok + test [eax+SymbolTree_Root.flags],NAMESPACE_CURRENT_LABEL_TO_CONFIRM + jz current_label_ok + current_label_confirmed: + mov edi,[edx+SymbolTree_Foliage.root] + test [edi+SymbolTree_Root.flags],NAMESPACE_LOCAL + jnz current_label_ok + mov edi,[current_context.base_namespace] + mov [edi+SymbolTree_Root.current_label],edx + current_label_ok: + retn + +define_numeric_symbol: + test [assembly_mode],AMODE_SKIP + jnz assembly_line + test [assembly_mode],AMODE_DEFINITION + jnz add_line_to_macro + cmp [interceptor],0 + jne execute_interceptor + inc esi + xor ecx,ecx + call move_to_next_symbol + jc define_numeric_variable + test ecx,ecx + jnz define_numeric_variable + cmp al,':' + jne define_numeric_variable + inc esi + call get_expression_value + jc missing_argument + mov ebx,[label_leaf] + call create_value_definition + jmp define_numeric_value +define_numeric_constant: + test [assembly_mode],AMODE_SKIP + jnz assembly_line + test [assembly_mode],AMODE_DEFINITION + jnz add_line_to_macro + cmp [interceptor],0 + jne execute_interceptor + inc esi + call get_expression_value + jc missing_argument + mov ebx,[label_leaf] + call create_constant_value_definition + jmp define_numeric_value +define_numeric_variable: + call get_expression_value + jc missing_argument + mov ebx,[label_leaf] + call update_value_definition + define_numeric_value: + test edx,edx + jz assembly_line + push esi + push ebx edx + mov eax,[edi+ExpressionTerm.attributes] + cmp al,EXPR_STRING + je define_string_variable + cmp al,EXPR_FLOAT + je define_float_variable + mov [value_type],VALTYPE_NUMERIC + call convert_terms_to_numeric_value + assign_numeric_value: + pop edx ebx + call assign_value + pop esi + jmp instruction_assembled + define_string_variable: + mov [value_type],VALTYPE_STRING + call get_term_value + mov esi,edx + mov ecx,[esi] + add ecx,4 + jmp assign_numeric_value + define_float_variable: + mov [value_type],VALTYPE_FLOAT + call get_term_value + mov esi,edx + mov ecx,sizeof.FloatData + jmp assign_numeric_value + +define_element: + or [symbol_definition],1 + mov dl,SYMCLASS_EXPRESSION + call identify_symbol + jc missing_argument + test ebx,ebx + jz invalid_identifier + mov [label_leaf],ebx + call move_to_next_symbol + jc element_with_no_metadata + cmp al,':' + jne get_element_metadata + inc esi + get_element_metadata: + call get_expression_value + jc invalid_argument + mov ebx,[label_leaf] + call create_constant_value_definition + test edx,edx + jz assembly_line + mov [value_type],VALTYPE_ELEMENT + push esi + push ebx edx + call convert_terms_to_numeric_value + pop edx ebx + call assign_value + pop esi + jmp instruction_assembled + element_with_no_metadata: + mov ebx,[label_leaf] + call create_constant_value_definition + test edx,edx + jz assembly_line + mov [value_type],VALTYPE_ELEMENT + push esi + xor esi,esi + xor ecx,ecx + call assign_value + pop esi + jmp instruction_assembled + +redefine_raw_symbolic_variable: + mov [update_function],update_value_definition + jmp raw_symbolic_variable +define_raw_symbolic_variable: + mov [update_function],create_value_definition + raw_symbolic_variable: + or [symbol_definition],1 + mov dl,SYMCLASS_EXPRESSION + call identify_symbol + jc missing_argument + test ebx,ebx + jz invalid_identifier + mov [label_leaf],ebx + or [raw_value],1 + jmp symbolic_variable +redefine_symbolic_variable: + mov [update_function],update_value_definition + and [raw_value],0 + jmp symbolic_variable +define_symbolic_variable: + mov [update_function],create_value_definition + and [raw_value],0 + symbolic_variable: + mov edi,[assembly_workspace.memory_start] + and [previous_symbol_end],0 + expand_symbols: + cmp [raw_value],0 + jne copy_raw_symbols + and [symbol_definition],0 + mov dl,SYMCLASS_EXPRESSION + push edi + call identify_symbol + jc symbols_expanded + test edi,edi + jnz symbol_ready + call skip_literal + xor ebx,ebx + symbol_ready: + pop edi + test ebx,ebx + jz copy_symbol + mov [further_whitespace],ecx + call get_available_value + jc copy_symbol + cmp [edx+ValueDefinition.type],VALTYPE_SYMBOLIC + jne copy_symbol + and [previous_symbol_end],0 + mov eax,[current_pass] + mov [ebx+SymbolTree_Leaf.last_use_pass],eax + cmp ebx,[label_leaf] + jne replace_symbolic_variable + mov ecx,[edx+ValueDefinition.pass] + cmp ecx,[current_pass] + je replace_symbolic_variable + mov edx,_symbolic_self_reference + call register_error + jmp expand_symbols + replace_symbolic_variable: + mov ecx,[edx+ValueDefinition.value_length] + test ecx,ecx + jz expand_symbols + push esi + mov esi,[edx+ValueDefinition.value] + push ecx + add ecx,1+sizeof.RecognitionContext + add ecx,[further_whitespace] + mov edx,assembly_workspace + call reserve_workspace + mov al,40h + cmp al,[esi] + je copy_symbolic_value + stosb + mov eax,esi + mov esi,current_context + assert sizeof.RecognitionContext and 11b = 0 + mov ecx,sizeof.RecognitionContext shr 2 + rep movsd + mov esi,eax + copy_symbolic_value: + pop ecx + mov al,cl + shr ecx,2 + rep movsd + mov cl,al + and cl,11b + rep movsb + pop esi + mov al,20h + mov ecx,[further_whitespace] + rep stosb + jmp expand_symbols + copy_raw_symbols: + mov eax,esi + mov esi,[line_end] + cmp esi,eax + je symbolic_value_ready + mov [symbol_start],eax + copy_symbol: + mov ebx,esi + xchg [previous_symbol_end],esi + test esi,esi + jz copy_symbol_with_recognition_context + mov ecx,ebx + sub ecx,esi + mov edx,assembly_workspace + call reserve_workspace + and [context_boundary],0 + jmp copy_symbol_tokens + copy_symbol_with_recognition_context: + mov ecx,ebx + sub ecx,[symbol_start] + add ecx,1+sizeof.RecognitionContext + mov edx,assembly_workspace + call reserve_workspace + mov al,40h + stosb + cmp [raw_value],0 + je use_recognition_context + mov esi,[embedded_context] + test esi,esi + jz use_current_context + call store_recognition_context + jmp symbol_context_stored + use_current_context: + call store_current_context + jmp symbol_context_stored + use_recognition_context: + mov esi,recognition_context + assert sizeof.RecognitionContext and 11b = 0 + mov ecx,sizeof.RecognitionContext shr 2 + rep movsd + mov [context_boundary],edi + symbol_context_stored: + mov esi,[symbol_start] + copy_symbol_tokens: + cmp esi,ebx + jae expand_symbols + call store_token + jmp copy_symbol_tokens + symbols_expanded: + pop edi + symbolic_value_ready: + mov ebx,[label_leaf] + call [update_function] + test edx,edx + jz assembly_line + mov [value_type],VALTYPE_SYMBOLIC + mov esi,[assembly_workspace.memory_start] + mov ecx,edi + sub ecx,esi + call assign_value + jmp assembly_line + store_token: + lodsb + cmp al,40h + je make_recognition_context_token + stosb + cmp al,1Ah + je store_token_with_data + cmp al,22h + je store_token_with_data + cmp al,27h + je store_token_with_data + cmp al,30h + je store_internal_token + retn + store_token_with_data: + movsd + retn + store_internal_token: + lodsd + stosd + mov ecx,eax + rep movsb + retn + +restore_variables: + mov al,[edx+ValueDefinition.attribute] + mov [chosen_class],al + restore_symbol_value: + mov dl,[chosen_class] + or [symbol_definition],1 + call identify_symbol + jc missing_argument + test ebx,ebx + jz invalid_identifier + remove_variable_value: + xor edx,edx + xor edi,edi + call remove_value_definition + call move_to_next_symbol + jc assembly_line + cmp al,',' + jne invalid_argument + inc esi + jmp restore_symbol_value +move_variable_values: + mov dl,[edx+ValueDefinition.attribute] + mov [chosen_class],dl + or [symbol_definition],1 + call identify_symbol + jc missing_argument + test ebx,ebx + jz invalid_identifier + mov [label_leaf],ebx + call move_to_next_symbol + jc missing_argument + cmp al,',' + jne invalid_argument + inc esi + mov dl,[chosen_class] + and [symbol_definition],0 + call identify_symbol + jc missing_argument + test ebx,ebx + jz invalid_identifier + mov al,[ebx+SymbolTree_Leaf.class] + cmp al,[chosen_class] + jne instruction_assembled + xor edx,edx + mov edi,[label_leaf] + call remove_value_definition + jmp instruction_assembled + +label_directive: + or [symbol_definition],1 + mov dl,SYMCLASS_EXPRESSION + call identify_symbol + jc missing_argument + test ebx,ebx + jz invalid_identifier + mov [label_leaf],ebx + mov [label_branch],edx + mov al,[symbol_solid] + mov [label_solid],al + and [metadata_length],0 + call peek_at_constituent_value + jc label_at_current_address + cmp al,1Ah + jne label_with_size + test edx,edx + jz label_with_size + cmp [edx+ValueDefinition.type],VALTYPE_NATIVE_PREPOSITION + jne label_with_size + cmp [edx+ValueDefinition.value],PREPOSITION_AT + je label_at_specified_address + label_with_size: + cmp al,':' + jne get_label_size + and [current_constituent],0 + get_label_size: + call get_expression_value + jc invalid_argument + push esi + mov edx,auxiliary_workspace + call convert_terms_to_numeric_value_in_workspace + mov [metadata_length],ecx + pop esi + call peek_at_constituent_value + jc label_at_current_address + cmp al,1Ah + jne label_at_current_address + test edx,edx + jz invalid_argument + cmp [edx+ValueDefinition.type],VALTYPE_NATIVE_PREPOSITION + jne label_at_current_address + cmp [edx+ValueDefinition.value],PREPOSITION_AT + jne invalid_argument + label_at_specified_address: + and [current_constituent],0 + call get_expression_value + jc invalid_argument + push esi + call convert_terms_to_numeric_value + mov edi,ecx + mov ebx,[label_leaf] + call create_constant_value_definition + test edx,edx + jz label_ready + call prepare_label_value + mov [value_type],VALTYPE_NUMERIC + call assign_value + jmp label_ready + label_at_current_address: + push esi + call get_current_address_value + mov edi,ecx + mov ebx,[label_leaf] + call create_constant_value_definition + test edx,edx + jz label_ready + call prepare_label_value + call assign_shiftable_value + label_ready: + pop esi + call update_current_label + jmp instruction_assembled + prepare_label_value: + mov ecx,edi + cmp [metadata_length],0 + je label_metadata_ok + add edi,esi + push edx ecx + mov ecx,[metadata_length] + mov edx,assembly_workspace + call reserve_workspace + mov esi,[auxiliary_workspace.memory_start] + mov ecx,[metadata_length] + pop eax + add eax,ecx + rep movsb + pop edx + mov ecx,eax + mov esi,edi + sub esi,ecx + label_metadata_ok: + retn + +virtual_block: + mov dl,DBLOCK_VIRTUAL + mov ecx,sizeof.VirtualBlockData + call add_directive_block + mov edx,[current_area] + mov [edi-sizeof.VirtualBlockData+VirtualBlockData.outer_area],edx + mov al,[shift_tracking] + mov [edi-sizeof.VirtualBlockData+VirtualBlockData.shift_tracking],al + call peek_at_constituent_value + jc virtual_at_current_address + cmp al,1Ah + jne invalid_argument + test edx,edx + jz invalid_argument + cmp [edx+ValueDefinition.type],VALTYPE_NATIVE_PREPOSITION + jne continue_virtual_block + mov eax,[edx+ValueDefinition.value] + cmp eax,PREPOSITION_AS + je virtual_at_current_address + cmp eax,PREPOSITION_AT + jne invalid_argument + and [current_constituent],0 + call get_expression_value + jc invalid_argument + push esi + call convert_terms_to_numeric_value + jmp create_virtual_block + virtual_at_current_address: + push esi + call get_current_address_value + create_virtual_block: + lea ebx,[virtual_area] + call create_area + or [ebx+AreaHeader.flags],AREA_VIRTUAL + cmp [shift_tracking],0 + je virtual_block_ready + or [ebx+AreaHeader.flags],AREA_SHIFT_TRACKING_DISABLED + virtual_block_ready: + inc [edx+ValueDefinition.reference_count] + or [edx+ValueDefinition.flags],VAL_IN_USE + mov [current_area],edx + pop esi + call get_constituent_value + jc instruction_assembled + cmp al,1Ah + jne invalid_argument + test edx,edx + jz invalid_argument + cmp [edx+ValueDefinition.type],VALTYPE_NATIVE_PREPOSITION + jne invalid_argument + cmp [edx+ValueDefinition.value],PREPOSITION_AS + jne invalid_argument + call get_constant_value + cmp al,22h + jne invalid_argument + mov ebx,[auxiliary_output_areas] + mov edi,esi + mov ecx,[edx] + lea esi,[edx+4] + test ecx,ecx + jnz register_auxiliary_file + dec esi + register_auxiliary_file: + mov eax,[current_area] + inc [eax+ValueDefinition.reference_count] + push ecx + call put_into_map + pop ecx + jmp validate_extension + continue_virtual_block: + and [leave_opening_parentheses],0 + mov edi,[expression_workspace.memory_start] + call parse_expression + mov edi,[expression_workspace.memory_start] + call get_area_value + jc invalid_area + mov ecx,[current_pass] + cmp [edx+ValueDefinition.pass],ecx + jne invalid_area + mov eax,[edx+ValueDefinition.value] + test [eax+AreaHeader.flags],AREA_VIRTUAL + jz invalid_area + test [edx+ValueDefinition.flags],VAL_IN_USE + jnz invalid_area + inc [edx+ValueDefinition.reference_count] + or [edx+ValueDefinition.flags],VAL_IN_USE + mov [current_area],edx + jmp instruction_assembled + +end_virtual_block: + mov dl,DBLOCK_VIRTUAL + call find_directive_block + jc unexpected_instruction + mov al,[edi-sizeof.VirtualBlockData+VirtualBlockData.shift_tracking] + mov [shift_tracking],al + mov edx,[edi-sizeof.VirtualBlockData+VirtualBlockData.outer_area] + xchg edx,[current_area] + and [edx+ValueDefinition.flags],not VAL_IN_USE + dec [edx+ValueDefinition.reference_count] + jnz close_virtual_block + mov eax,edx + xchg eax,[retired_definition] + mov [edx+ValueDefinition.previous],eax + close_virtual_block: + call close_directive_block + jmp instruction_assembled + +include_source: + call move_to_next_symbol + jc missing_argument + cmp al,'!' + jne conditional_include + inc esi + jmp get_file_name + conditional_include: + test [assembly_mode],AMODE_SKIP + jnz assembly_line + test [assembly_mode],AMODE_DEFINITION + jnz add_line_to_macro + get_file_name: + call get_constant_value + cmp al,22h + jne invalid_argument + push esi + mov [file_name],edx + call prepare_file_path + mov esi,[assembly_workspace.memory_start] + call read_source + test eax,eax + jnz include_read + mov esi,[local_path] + call read_source + test eax,eax + jnz include_read + mov ebx,esi + mov esi,[include_paths] + try_include_paths: + lodsb + test al,al + jz include_not_found + cmp al,';' + je try_include_paths + lea ebx,[esi-1] + measure_path: + lodsb + test al,al + jz path_measured + cmp al,';' + jne measure_path + path_measured: + dec esi + xchg esi,ebx + mov ecx,ebx + sub ecx,esi + mov edx,[file_name] + add ecx,[edx] + add ecx,2 + mov edx,assembly_workspace + mov edi,[edx+Workspace.memory_start] + call reserve_workspace + mov ecx,ebx + sub ecx,esi + rep movsb + mov al,[esi-1] + cmp al,'/' + je path_separator_ok + cmp al,'\' + je path_separator_ok + mov al,'/' + stosb + path_separator_ok: + mov esi,[file_name] + lodsd + mov ecx,eax + rep movsb + xor al,al + stosb + mov esi,[assembly_workspace.memory_start] + push ebx + call read_source + mov ebx,esi + pop esi + test eax,eax + jz try_include_paths + mov esi,ebx + include_read: + mov edx,eax + call create_source_entry + jc include_stack_limit_exceeded + mov [ebx+SourceEntry.type],SOURCE_FILE + mov [ebx+SourceEntry.name],esi + mov [ebx+SourceEntry.text],edx + mov edx,[local_parameter_namespace] + mov eax,[edx+SymbolTree_Root.chain] + test eax,eax + jnz new_parameter_namespace_ok + call create_parameter_namespace + mov [edx+SymbolTree_Root.chain],eax + new_parameter_namespace_ok: + and [eax+SymbolTree_Root.parameters],0 + mov [ebx+SourceEntry.local_namespace],eax + pop esi + call get_constituent_value + jc instruction_assembled + cmp al,',' + jne instruction_assembled + cmp [number_of_line_embeddings],0 + jne invalid_argument + jmp assemble_after_label + include_stack_limit_exceeded: + pop esi + mov edx,_stack_limit_exceeded + call register_error + jmp instruction_assembled + include_not_found: + pop esi + mov edx,_source_file_not_found + call register_error + jmp instruction_assembled + prepare_file_path: + mov [local_path],edx + call get_file_source_entry + mov esi,[ebx+SourceEntry.name] + mov ecx,esi + cut_parent_path: + lodsb + test al,al + jz parent_path_cut + cmp al,'/' + je found_path_segment + cmp al,'\' + jne cut_parent_path + found_path_segment: + mov ecx,esi + jmp cut_parent_path + parent_path_cut: + mov esi,[ebx+SourceEntry.name] + sub ecx,esi + mov ebx,ecx + add ecx,[edx] + inc ecx + mov edx,assembly_workspace + mov edi,[edx+Workspace.memory_start] + call reserve_workspace + mov ecx,ebx + rep movsb + mov edx,edi + xchg edx,[local_path] + lea esi,[edx+4] + mov ecx,[edx] + mov ebx,edi + rep movsb + xor al,al + stosb + retn + +evaluate_string: + mov edi,[assembly_workspace.memory_start] + mov [string_end],edi + collect_source_string: + call get_constant_value + cmp al,30h + je collect_source_byte + cmp al,22h + jne invalid_argument + mov ebx,edx + mov edi,[string_end] + mov ecx,[ebx] + inc ecx + mov edx,assembly_workspace + call reserve_workspace + xchg esi,ebx + lodsd + mov ecx,eax + shr ecx,2 + rep movsd + mov cl,al + and cl,11b + rep movsb + mov esi,ebx + source_fragment_collected: + mov [string_end],edi + call get_constituent_value + jc source_string_ready + cmp al,',' + jne invalid_argument + jmp collect_source_string + collect_source_byte: + mov ebx,edx + mov edi,[string_end] + mov ecx,2 + mov edx,assembly_workspace + call reserve_workspace + mov edx,ebx + mov ecx,1 + call fit_value + jc value_out_of_range + inc edi + jmp source_fragment_collected + source_string_ready: + mov edi,[string_end] + xor al,al + stosb + push esi + mov esi,[assembly_workspace.memory_start] + call use_source + mov edx,eax + call create_source_entry + jc include_stack_limit_exceeded + mov [ebx+SourceEntry.type],SOURCE_MEMORY + mov [ebx+SourceEntry.name],esi + mov [ebx+SourceEntry.text],edx + mov eax,[parameter_namespace] + mov [ebx+SourceEntry.local_namespace],eax + pop esi + jmp instruction_assembled + +assert_condition: + call get_condition_value + test al,al + jnz instruction_assembled + mov edx,_assertion_failed + call register_error + jmp instruction_assembled + +conditional_block: + test [assembly_mode],AMODE_DEFINITION + jnz ignored_directive + mov dl,DBLOCK_CONTROL + xor ecx,ecx + call add_directive_block + mov [edi+DirectiveBlock.subtype],CTRL_IF + or [edi+DirectiveBlock.flags],CTRLF_ALLOWS_ELSE + test [assembly_mode],AMODE_SKIP + jnz skip_conditional_block + evaluate_conditional_block: + call get_condition_value + test al,al + jnz instruction_assembled + or [assembly_mode],AMODE_SKIP + jmp instruction_assembled + ignored_directive: + test [assembly_mode],AMODE_SKIP + jz add_line_to_macro + jmp assembly_line + +else_conditional_block: + test [assembly_mode],AMODE_DEFINITION + jnz ignored_directive + mov dl,DBLOCK_CONTROL + call find_directive_block + jc unexpected_instruction + mov al,[edi+DirectiveBlock.flags] + test al,CTRLF_ALLOWS_ELSE + jz unexpected_instruction + mov ah,[assembly_mode] + push eax + call close_control_directive_block + mov dl,DBLOCK_CONTROL + xor ecx,ecx + call add_directive_block + mov [edi+DirectiveBlock.subtype],CTRL_IF + or [edi+DirectiveBlock.flags],CTRLF_ALLOWS_ELSE + pop eax + test al,CTRLF_INACTIVE + jnz skip_conditional_block + test ah,AMODE_SKIP + jnz evaluate_conditional_block + skip_conditional_block: + or [assembly_mode],AMODE_SKIP + or [edi+DirectiveBlock.flags],CTRLF_INACTIVE + jmp assembly_line + +end_conditional_block: + test [assembly_mode],AMODE_DEFINITION + jnz ignored_directive + mov dl,DBLOCK_CONTROL + call find_directive_block + jc unexpected_instruction + cmp [edi+DirectiveBlock.subtype],CTRL_IF + jne unexpected_instruction + end_control_block: + call close_control_directive_block + jmp instruction_assembled + +conditionally_repeated_block: + test [assembly_mode],AMODE_DEFINITION + jnz ignored_directive + test [assembly_mode],AMODE_SKIP + jnz inactive_conditionally_repeated_block + mov ecx,[line_end] + sub ecx,esi + add ecx,sizeof.ConditionalRepeatData + cmp [embedded_context],0 + je condition_length_ok + add ecx,1+sizeof.RecognitionContext + condition_length_ok: + mov dl,DBLOCK_CONTROL + call add_directive_block + push esi + mov ecx,[line_end] + sub ecx,esi + sub edi,sizeof.ConditionalRepeatData + mov edx,ecx + sub edi,ecx + mov eax,ecx + shr ecx,2 + rep movsd + mov cl,al + and cl,11b + rep movsb + mov esi,[embedded_context] + test esi,esi + jz condition_stored + add edx,1+sizeof.RecognitionContext + mov ebx,edi + sub edi,edx + mov al,40h + stosb + assert sizeof.RecognitionContext and 11b = 0 + mov ecx,sizeof.RecognitionContext shr 2 + rep movsd + mov edi,ebx + condition_stored: + mov [edi+ConditionalRepeatData.condition_length],edx + add edi,sizeof.ConditionalRepeatData + pop esi + mov [edi+DirectiveBlock.subtype],CTRL_WHILE + or [edi+DirectiveBlock.flags],CTRLF_BREAKABLE + call get_condition_value + test al,al + jz skip_repeated_block + start_repeated_block: + mov ecx,2 + call allocate_counter + mov edi,ebx + mov al,1 + stosb + stosb + mov [current_counter],ebx + mov ecx,[parameter_namespace] + mov [ecx+SymbolTree_Root.parameters],SPECPARM_COUNTER + test esi,esi + jz assembly_line + jmp instruction_assembled + inactive_conditionally_repeated_block: + xor esi,esi + mov dh,CTRL_WHILE + inactive_breakable_block: + mov dl,DBLOCK_CONTROL + xor ecx,ecx + call add_directive_block + mov [edi+DirectiveBlock.subtype],dh + or [edi+DirectiveBlock.flags],CTRLF_BREAKABLE + skip_repeated_block: + or [assembly_mode],AMODE_SKIP + mov ecx,1 + call allocate_counter + mov byte [ebx],0 + mov [current_counter],ebx + mov ecx,[parameter_namespace] + mov [ecx+SymbolTree_Root.parameters],0 + test esi,esi + jz assembly_line + jmp instruction_assembled + allocate_counter: + mov ebx,[current_counter] + movzx eax,byte [ebx] + lea ebx,[ebx+1+eax] + add ecx,ebx + cmp ecx,[counters_stack_end] + jbe counter_allocated + mov eax,[counters_stack_base] + sub ecx,eax + sub ebx,eax + call grow_stack + add ecx,eax + mov [counters_stack_end],ecx + mov [counters_stack_base],eax + add ebx,eax + counter_allocated: + retn + +end_conditionally_repeated_block: + test [assembly_mode],AMODE_DEFINITION + jnz ignored_directive + mov dl,DBLOCK_CONTROL + call find_directive_block + jc unexpected_instruction + cmp [edi+DirectiveBlock.subtype],CTRL_WHILE + jne unexpected_instruction + test [assembly_mode],AMODE_SKIP + jnz end_control_block + push esi edi + push [line_end] + push [source_context] + lea esi,[edi-sizeof.ConditionalRepeatData] + mov [line_end],esi + sub esi,[esi+ConditionalRepeatData.condition_length] + sub edi,[edi+DirectiveBlock.length_of_data] + mov [source_context],edi + and [embedded_context],0 + call get_condition_value + pop [source_context] + pop [line_end] + pop edi esi + test al,al + jz end_control_block + repeat_condition_upheld: + mov ebx,[current_counter] + movzx ecx,byte [ebx] + inc ebx + increase_counter: + inc byte [ebx] + jnz counter_increased + inc ebx + loop increase_counter + cmp ebx,[counters_stack_end] + jb counter_length_grown + lea ecx,[ebx+1] + mov eax,[counters_stack_base] + sub ecx,eax + sub ebx,eax + sub [current_counter],eax + call grow_stack + add ecx,eax + mov [counters_stack_end],ecx + mov [counters_stack_base],eax + add [current_counter],eax + add ebx,eax + counter_length_grown: + mov byte [ebx],1 + mov ebx,[current_counter] + inc byte [ebx] + counter_increased: + push esi + test [edi+DirectiveBlock.flags],CTRLF_HAS_REPEAT_DATA + jz index_updated + mov esi,[current_counter] + mov eax,[counters_stack_base] + add eax,[edi-sizeof.RepeatData+RepeatData.index_position] + cmp eax,esi + je index_updated + xchg eax,edi + movzx ecx,byte [esi] + inc ecx + rep movsb + mov edi,eax + index_updated: + mov eax,[source_context] + mov esi,eax + call release_source_context + sub edi,[edi+DirectiveBlock.length_of_data] + xchg esi,edi + call clone_source_context + pop esi + jmp instruction_assembled + +repeated_block: + test [assembly_mode],AMODE_DEFINITION + jnz ignored_directive + test [assembly_mode],AMODE_SKIP + jnz inactive_repeated_block + call get_numeric_constant_value + test edx,edx + jz invalid_count + mov ecx,[edx] + add edx,4 + test ecx,ecx + jz inactive_repeated_block + mov al,[edx+ecx-1] + test al,al + js count_out_of_range + jnz count_ok + optimize_counter: + test ecx,ecx + jz inactive_repeated_block + dec ecx + mov al,[edx+ecx-1] + test al,al + jz optimize_counter + count_ok: + push esi ecx + mov esi,edx + mov dl,DBLOCK_CONTROL + add ecx,sizeof.RepeatData + call add_directive_block + mov [edi+DirectiveBlock.subtype],CTRL_REPEAT + or [edi+DirectiveBlock.flags],CTRLF_BREAKABLE + CTRLF_HAS_REPEAT_DATA + mov [directive_block],edi + pop ecx + sub edi,sizeof.RepeatData + mov [edi+RepeatData.limit_length],ecx + sub edi,ecx + rep movsb + pop esi + mov ecx,2 + call allocate_counter + mov eax,ebx + sub eax,[counters_stack_base] + mov [edi+RepeatData.index_position],eax + mov edi,ebx + mov al,1 + stosb + stosb + mov [current_counter],ebx + mov ecx,[parameter_namespace] + mov [ecx+SymbolTree_Root.parameters],SPECPARM_COUNTER + SPECPARM_LIMIT + define_named_counters: + xor al,al + xchg al,[current_constituent] + test al,al + jz get_counter_name + cmp al,',' + je get_counter_name + cmp al,1Ah + jne invalid_argument + mov esi,[symbol_start] + get_counter_name: + call identify_parameter_symbol + jc instruction_assembled + test ebx,ebx + jz invalid_argument + mov [label_leaf],ebx + call peek_at_constituent_value + jc counter_with_default_base + cmp al,':' + jne counter_with_default_base + and [current_constituent],0 + call get_numeric_constant_value + test edx,edx + jz invalid_counter_base + mov ecx,[edx] + test byte [edx+4+ecx-1],80h + jns counter_base_ok + mov edx,_value_out_of_range + call register_error + jmp counter_with_default_base + invalid_counter_base: + mov edx,_invalid_value + call register_error + counter_with_default_base: + mov edx,singular_value + counter_base_ok: + push esi + mov esi,edx + mov edx,assembly_workspace + mov edi,[edx+Workspace.memory_start] + mov ecx,[esi] + add ecx,8 + call reserve_workspace + mov eax,[current_counter] + sub eax,[counters_stack_base] + stosd + lodsd + mov ecx,eax + stosd + rep movsb + mov esi,[assembly_workspace.memory_start] + mov ecx,edi + sub ecx,esi + mov [value_type],VALTYPE_NUMERIC_SEQUENCE + mov ebx,[label_leaf] + mov edi,[directive_block] + call add_block_parameter + pop esi + jmp define_named_counters + invalid_count: + mov edx,_invalid_value + call register_error + jmp inactive_repeated_block + count_out_of_range: + mov edx,_value_out_of_range + call register_error + inactive_repeated_block: + xor esi,esi + mov dh,CTRL_REPEAT + jmp inactive_breakable_block + +end_repeated_block: + mov dh,CTRL_REPEAT + close_repeat: + test [assembly_mode],AMODE_DEFINITION + jnz ignored_directive + mov dl,DBLOCK_CONTROL + call find_directive_block + jc unexpected_instruction + cmp dh,[edi+DirectiveBlock.subtype] + jne unexpected_instruction + test [assembly_mode],AMODE_SKIP + jnz end_control_block + mov edx,[current_counter] + movzx ecx,byte [edx] + inc edx + lea ebx,[edi-sizeof.RepeatData] + cmp ecx,[ebx+RepeatData.limit_length] + jne repeat_condition_upheld + sub ebx,ecx + compare_counter_with_limit: + mov al,[ebx] + cmp al,[edx] + jne repeat_condition_upheld + inc ebx + inc edx + loop compare_counter_with_limit + jmp end_control_block + +iterator_block: + test [assembly_mode],AMODE_DEFINITION + jnz ignored_directive + test [assembly_mode],AMODE_SKIP + jnz inactive_iterator_block + xor eax,eax + mov [breakpoint_token],al + mov [number_of_parameters],eax + mov edi,[expression_workspace.memory_start] + call move_to_next_symbol + jc invalid_iterator + cmp al,'<' + jne iterator_parameter_declaration + inc esi + mov [breakpoint_token],'>' + iterator_parameter_declaration: + push edi + call identify_parameter_symbol + pop edi + jc invalid_iterator + test ebx,ebx + jz invalid_iterator + mov eax,[number_of_parameters] + shl eax,2 + add eax,[assembly_stack_base] + lea ecx,[eax+4] + cmp ecx,[assembly_stack_end] + jbe store_parameter_leaf + mov eax,[assembly_stack_base] + sub ecx,eax + call grow_stack + mov [assembly_stack_base],eax + add ecx,eax + mov [assembly_stack_end],ecx + mov ecx,[number_of_parameters] + shl ecx,2 + add eax,ecx + store_parameter_leaf: + mov [eax],ebx + mov edx,expression_workspace + mov ecx,sizeof.LineExcerpt + call reserve_workspace + mov [edi+LineExcerpt.data_start],esi + mov [edi+LineExcerpt.data_end],esi + inc [number_of_parameters] + call move_to_next_symbol + jc invalid_iterator + cmp al,':' + je parameter_with_default_value + cmp al,'=' + je parameter_with_default_value + cmp al,'*' + jne parameter_declaration_done + and [edi+LineExcerpt.data_start],0 + inc esi + call move_to_next_symbol + jc invalid_iterator + jmp parameter_declaration_done + parameter_with_default_value: + inc esi + call cut_argument_value + parameter_declaration_done: + add edi,sizeof.LineExcerpt + mov ah,[breakpoint_token] + test ah,ah + jz iterator_parameters_declared + inc esi + cmp al,',' + je iterator_parameter_declaration + cmp al,ah + jne invalid_iterator + call move_to_next_symbol + jc invalid_iterator + iterator_parameters_declared: + cmp al,',' + jne invalid_iterator + mov ebx,[number_of_parameters] + mov [number_of_values],ebx + and [number_of_iterations],0 + collect_iterator_values: + inc esi + call move_to_next_symbol + jc initialize_iterator + mov edx,expression_workspace + mov ecx,sizeof.LineExcerpt + call reserve_workspace + call cut_argument_value + add edi,sizeof.LineExcerpt + inc [number_of_values] + cmp ebx,[number_of_parameters] + jne iterator_value_collected + inc [number_of_iterations] + xor ebx,ebx + iterator_value_collected: + inc ebx + cmp al,',' + je collect_iterator_values + initialize_iterator: + cmp [number_of_iterations],0 + je inactive_iterator_block + mov dl,DBLOCK_CONTROL + mov ecx,5+sizeof.RepeatData + call add_directive_block + mov [edi+DirectiveBlock.subtype],CTRL_IRP + or [edi+DirectiveBlock.flags],CTRLF_BREAKABLE + CTRLF_HAS_REPEAT_DATA + CTRLF_HAS_WRITABLE_INDEX + mov [directive_block],edi + bsr ecx,[number_of_iterations] + shr ecx,3 + inc ecx + push esi + sub edi,sizeof.RepeatData + mov [edi+RepeatData.limit_length],ecx + sub edi,ecx + mov esi,number_of_iterations + rep movsb + mov ecx,[edi+RepeatData.limit_length] + add ecx,3 + call allocate_counter + mov eax,ebx + sub eax,[counters_stack_base] + mov [edi+RepeatData.index_position],eax + mov ecx,[edi+RepeatData.limit_length] + mov edi,ebx + mov al,1 + stosb + stosb + lea edi,[edi+ecx-1] + mov [current_counter],edi + stosb + stosb + mov ecx,[parameter_namespace] + mov [ecx+SymbolTree_Root.parameters],SPECPARM_COUNTER + SPECPARM_LIMIT + xor eax,eax + create_iterator_sequences: + mov [parameter_index],eax + mov edx,assembly_workspace + mov edi,[edx+Workspace.memory_start] + mov ecx,[number_of_iterations] + add ecx,2 + shl ecx,2 + call reserve_workspace + mov eax,[directive_block] + mov eax,[eax-sizeof.RepeatData+RepeatData.index_position] + stosd + mov eax,[number_of_iterations] + add eax,2 + shl eax,2 + stosd + mov [sequence_header_length],eax + mov edx,edi + mov edi,[assembly_workspace.memory_start] + sub edx,edi + mov [sequence_header_cursor],edx + add edi,eax + mov eax,[number_of_parameters] + add eax,[parameter_index] + cmp eax,[number_of_values] + jae copy_default_value + copy_individual_value: + mov [value_index],eax + mov ebx,eax + assert sizeof.LineExcerpt = 1 shl 4 + shl ebx,4 + add ebx,[expression_workspace.memory_start] + mov esi,[ebx+LineExcerpt.data_start] + mov ecx,[ebx+LineExcerpt.data_end] + sub ecx,esi + jnz individual_value_ready + copy_default_value: + mov ebx,[parameter_index] + assert sizeof.LineExcerpt = 1 shl 4 + shl ebx,4 + add ebx,[expression_workspace.memory_start] + mov esi,[ebx+LineExcerpt.data_start] + test esi,esi + jz missing_required_individual_value + mov ecx,[ebx+LineExcerpt.data_end] + sub ecx,esi + individual_value_ready: + mov edx,assembly_workspace + add ecx,2*(1+sizeof.RecognitionContext) + call reserve_workspace + mov ecx,[ebx+LineExcerpt.data_end] + sub ecx,esi + jz next_iterator_value + cmp [ebx+LineExcerpt.recognition_context],0 + je copy_iterator_value + mov al,40h + stosb + mov eax,esi + mov edx,ecx + mov esi,[ebx+LineExcerpt.recognition_context] + assert sizeof.RecognitionContext and 11b = 0 + mov ecx,sizeof.RecognitionContext shr 2 + rep movsd + mov ecx,edx + mov esi,eax + copy_iterator_value: + mov al,cl + shr ecx,2 + rep movsd + mov cl,al + and cl,11b + rep movsb + mov eax,[ebx+LineExcerpt.recognition_context] + or eax,[ebx+LineExcerpt.leftover_context] + jz next_iterator_value + mov al,40h + stosb + xor eax,eax + assert sizeof.RecognitionContext and 11b = 0 + mov ecx,sizeof.RecognitionContext shr 2 + rep stosd + next_iterator_value: + mov edx,[assembly_workspace.memory_start] + mov ecx,edi + sub ecx,edx + add edx,[sequence_header_cursor] + mov [edx],ecx + add [sequence_header_cursor],4 + mov eax,[value_index] + add eax,[number_of_parameters] + cmp eax,[number_of_values] + jb copy_individual_value + mov eax,[sequence_header_length] + sub eax,[sequence_header_cursor] + jne copy_default_value + mov ecx,edi + mov esi,[assembly_workspace.memory_start] + sub ecx,esi + mov eax,[parameter_index] + shl eax,2 + add eax,[assembly_stack_base] + mov ebx,[eax] + mov edi,[directive_block] + mov [value_type],VALTYPE_SYMBOLIC_SEQUENCE + call add_block_parameter + mov eax,[parameter_index] + inc eax + cmp eax,[number_of_parameters] + jb create_iterator_sequences + pop esi + jmp instruction_assembled + missing_required_individual_value: + mov edx,_missing_argument + call register_error + jmp next_iterator_value + invalid_iterator: + mov edx,_invalid_argument + call register_error + inactive_iterator_block: + xor esi,esi + mov dh,CTRL_IRP + jmp inactive_breakable_block + +end_iterator_block: + mov dh,CTRL_IRP + jmp close_repeat + +variable_iterator_block: + test [assembly_mode],AMODE_DEFINITION + jnz ignored_directive + test [assembly_mode],AMODE_SKIP + jnz skipped_variable_iterator_block + call move_to_next_symbol + jc invalid_iterator + call identify_parameter_symbol + jc invalid_variable_iterator + test ebx,ebx + jz invalid_variable_iterator + mov [label_leaf],ebx + call move_to_next_symbol + jc invalid_variable_iterator + cmp al,',' + jne invalid_variable_iterator + inc esi + and [symbol_definition],0 + mov dl,SYMCLASS_EXPRESSION + call identify_symbol + jc invalid_variable_identifier + test ebx,ebx + jz invalid_variable_identifier + mov eax,[current_pass] + mov [ebx+SymbolTree_Leaf.last_use_pass],eax + mov edi,[assembly_stack_base] + xor ecx,ecx + mov [number_of_values],ecx + mov edx,[ebx+SymbolTree_Leaf.definition] + collect_variable_values: + test edx,edx + jz variable_values_collected + mov eax,[edx+ValueDefinition.pass] + cmp eax,[current_pass] + jne variable_values_collected + cmp [edx+ValueDefinition.type],VALTYPE_RESERVED + je skip_variable_value + add edi,4 + cmp edi,[assembly_stack_end] + jbe store_variable_value + push ecx edx + mov eax,[assembly_stack_base] + sub edi,eax + mov ecx,edi + call grow_stack + add edi,eax + mov [assembly_stack_base],eax + add eax,ecx + mov [assembly_stack_end],eax + pop edx ecx + store_variable_value: + mov [edi-4],edx + inc [number_of_values] + add ecx,[edx+ValueDefinition.value_length] + add ecx,1+sizeof.RecognitionContext + skip_variable_value: + mov edx,[edx+ValueDefinition.previous] + jmp collect_variable_values + variable_values_collected: + mov eax,[number_of_values] + test eax,eax + jz inactive_variable_iterator_block + add eax,2 + shl eax,2 + add ecx,eax + mov edx,assembly_workspace + mov edi,[edx+Workspace.memory_start] + call reserve_workspace + mov dl,DBLOCK_CONTROL + mov ecx,5+sizeof.RepeatData + call add_directive_block + mov [edi+DirectiveBlock.subtype],CTRL_IRPV + or [edi+DirectiveBlock.flags],CTRLF_BREAKABLE + CTRLF_HAS_REPEAT_DATA + CTRLF_HAS_WRITABLE_INDEX + mov [directive_block],edi + bsr ecx,[number_of_values] + shr ecx,3 + inc ecx + push esi + sub edi,sizeof.RepeatData + mov [edi+RepeatData.limit_length],ecx + sub edi,ecx + mov esi,number_of_values + rep movsb + mov ecx,[edi+RepeatData.limit_length] + add ecx,3 + call allocate_counter + mov eax,ebx + sub eax,[counters_stack_base] + mov [edi+RepeatData.index_position],eax + mov ecx,[edi+RepeatData.limit_length] + mov edi,ebx + mov al,1 + stosb + stosb + lea edi,[edi+ecx-1] + mov [current_counter],edi + stosb + stosb + mov ecx,[parameter_namespace] + mov [ecx+SymbolTree_Root.parameters],SPECPARM_COUNTER + SPECPARM_LIMIT + mov edi,[assembly_workspace.memory_start] + mov ecx,[number_of_values] + mov edx,ecx + add ecx,2 + shl ecx,2 + mov ebx,edi + add edi,ecx + mov eax,[directive_block] + mov eax,[eax-sizeof.RepeatData+RepeatData.index_position] + mov [ebx],eax + add ebx,4 + mov [ebx],ecx + create_value_sequence: + mov eax,edx + dec eax + shl eax,2 + add eax,[assembly_stack_base] + mov eax,[eax] + push edx + mov cl,[eax+ValueDefinition.type] + cmp cl,VALTYPE_SYMBOLIC + je symbolic_value_in_sequence + cmp cl,VALTYPE_NUMERIC + jne proxy_value_in_sequence + mov esi,[eax+ValueDefinition.value] + mov ecx,[esi] + add esi,4 + test byte [esi+ecx-1],80h + jnz proxy_value_in_sequence + cmp dword [esi+ecx],0 + jne proxy_value_in_sequence + push ecx + add ecx,1+4 + call allocate_value_in_sequence + pop ecx + mov al,30h + stosb + mov eax,ecx + stosd + rep movsb + jmp next_value_in_sequence + allocate_value_in_sequence: + mov eax,[ebx] + add eax,ecx + add ebx,4 + mov [ebx],eax + mov edx,assembly_workspace + sub ebx,[edx+Workspace.memory_start] + call reserve_workspace + add ebx,[assembly_workspace.memory_start] + retn + proxy_value_in_sequence: + mov esi,eax + mov ecx,(1+sizeof.RecognitionContext)*2+1 + call allocate_value_in_sequence + push ebx edi + push esi + xor eax,eax + mov ecx,[proxy_number] + inc [proxy_number] + mov dl,SYMCLASS_EXPRESSION + mov ebx,[current_context.base_namespace] + call get_abstract_symbol + xchg edx,[esp] + call update_value_link + pop edx + pop edi ebx + mov al,40h + stosb + mov eax,[current_context.base_namespace] + mov [edi+RecognitionContext.base_namespace],eax + mov [edi+RecognitionContext.base_label],edx + add edi,sizeof.RecognitionContext + mov al,'.' + stosb + jmp close_symbolic_value_in_sequence + symbolic_value_in_sequence: + mov esi,[eax+ValueDefinition.value] + mov ecx,[eax+ValueDefinition.value_length] + push ecx + add ecx,1+sizeof.RecognitionContext + call allocate_value_in_sequence + pop ecx + rep movsb + close_symbolic_value_in_sequence: + mov al,40h + stosb + xor eax,eax + assert sizeof.RecognitionContext and 11b = 0 + mov ecx,sizeof.RecognitionContext shr 2 + rep stosd + next_value_in_sequence: + pop edx + dec edx + jnz create_value_sequence + mov esi,[assembly_workspace.memory_start] + mov ecx,edi + sub ecx,esi + mov ebx,[label_leaf] + mov edi,[directive_block] + mov [value_type],VALTYPE_SYMBOLIC_SEQUENCE + call add_block_parameter + pop esi + jmp instruction_assembled + invalid_variable_identifier: + mov edx,_invalid_identifier + call register_error + jmp inactive_variable_iterator_block + invalid_variable_iterator: + mov edx,_invalid_argument + call register_error + skipped_variable_iterator_block: + xor esi,esi + inactive_variable_iterator_block: + mov dh,CTRL_IRPV + jmp inactive_breakable_block + +end_variable_iterator_block: + mov dh,CTRL_IRPV + jmp close_repeat + +set_iterator_index: + mov dl,DBLOCK_CONTROL + call find_directive_block + jc unexpected_instruction + find_indexable_block: + test [edi+DirectiveBlock.flags],CTRLF_HAS_WRITABLE_INDEX + jnz indexable_block_found + call find_next_directive_block + jc unexpected_instruction + jmp find_indexable_block + indexable_block_found: + ; test [edi+DirectiveBlock.flags],CTRLF_HAS_REPEAT_DATA + ; jnz internal_error + mov [directive_block],edi + call get_numeric_constant_value + test edx,edx + jz invalid_argument + mov ecx,[edx] + add edx,4 + mov al,[edx+ecx-1] + test al,al + js value_out_of_range + jnz index_optimized + optimize_index: + test ecx,ecx + jz value_out_of_range + dec ecx + mov al,[edx+ecx-1] + test al,al + jz optimize_index + index_optimized: + mov edi,[directive_block] + sub edi,sizeof.RepeatData + mov ebx,[edi+RepeatData.limit_length] + cmp ecx,ebx + ja value_out_of_range + jb index_ok + sub edi,ebx + compare_index_with_limit: + mov al,[edx+ebx-1] + cmp al,[edi+ebx-1] + ja value_out_of_range + jb index_ok + dec ebx + jnz compare_index_with_limit + index_ok: + xchg esi,edx + mov edi,[directive_block] + mov edi,[edi-sizeof.RepeatData+RepeatData.index_position] + add edi,[counters_stack_base] + mov al,cl + stosb + rep movsb + mov esi,edx + jmp instruction_assembled + +break_directive: + mov dl,DBLOCK_CONTROL + call find_directive_block + jc unexpected_instruction + find_block_to_break: + test [edi+DirectiveBlock.flags],CTRLF_BREAKABLE + jnz block_to_break_found + call find_next_directive_block + jc unexpected_instruction + jmp find_block_to_break + block_to_break_found: + or [assembly_mode],AMODE_SKIP + mov dl,DBLOCK_CONTROL + call find_directive_block + break_blocks: + or [edi+DirectiveBlock.flags],CTRLF_INACTIVE + test [edi+DirectiveBlock.flags],CTRLF_BREAKABLE + jnz disable_counter + or [edi+DirectiveBlock.prior_assembly_mode],AMODE_SKIP + call find_next_directive_block + jc unexpected_instruction + jmp break_blocks + disable_counter: + mov edi,[current_counter] + xor al,al + stosb + jmp instruction_assembled + +else_directive: + mov edx,[ebx+SymbolTree_Leaf.branch] + call get_symbol_namespace + and [symbol_definition],0 + mov dl,SYMCLASS_INSTRUCTION + call identify_symbol_in_namespace + jc plain_else + test ebx,ebx + jz invalid_else + mov al,[ebx+SymbolTree_Leaf.class] + cmp al,SYMCLASS_INSTRUCTION + jne invalid_else + call get_available_value + jc invalid_else + jmp prefixed_directive + invalid_else: + mov esi,[symbol_start] + plain_else: + test [assembly_mode],AMODE_DEFINITION + jnz ignored_directive + mov dl,DBLOCK_CONTROL + call find_directive_block + jc unexpected_instruction + test [edi+DirectiveBlock.flags],CTRLF_ALLOWS_ELSE + jz unexpected_instruction + and [edi+DirectiveBlock.flags],not CTRLF_ALLOWS_ELSE + call remove_block_parameters + test [edi+DirectiveBlock.flags],CTRLF_INACTIVE + jnz assembly_line + xor [assembly_mode],AMODE_SKIP + jmp instruction_assembled + +end_directive: + mov edx,[ebx+SymbolTree_Leaf.branch] + call get_symbol_namespace + and [symbol_definition],0 + mov dl,SYMCLASS_INSTRUCTION + call identify_symbol_in_namespace + jc missing_prefixed_directive + test ebx,ebx + jz invalid_prefixed_directive + mov al,[ebx+SymbolTree_Leaf.class] + cmp al,SYMCLASS_INSTRUCTION + jne invalid_prefixed_directive + call get_available_value + jc invalid_prefixed_directive + prefixed_directive: + test [edx+ValueDefinition.flags],VAL_UNCONDITIONAL + jnz execute_prefixed_directive + test [assembly_mode],AMODE_SKIP + jnz assembly_line + test [assembly_mode],AMODE_DEFINITION + jnz add_line_to_macro + execute_prefixed_directive: + mov al,[edx+ValueDefinition.type] + cmp al,VALTYPE_CALM + je launch_calm + cmp al,VALTYPE_SYMBOLIC + je use_macro + cmp al,VALTYPE_NATIVE_COMMAND + jne invalid_argument + jmp [edx+ValueDefinition.value] + missing_prefixed_directive: + test [assembly_mode],AMODE_SKIP + jnz assembly_line + test [assembly_mode],AMODE_DEFINITION + jnz add_line_to_macro + cmp [interceptor],0 + jne execute_interceptor + jmp missing_argument + invalid_prefixed_directive: + test [assembly_mode],AMODE_SKIP + jnz assembly_line + test [assembly_mode],AMODE_DEFINITION + jnz add_line_to_macro + cmp [interceptor],0 + jne execute_interceptor + jmp invalid_argument + +raw_match_block: + mov bl,CTRL_RMATCH + jmp begin_match_block +match_block: + mov bl,CTRL_MATCH + begin_match_block: + test [assembly_mode],AMODE_DEFINITION + jnz ignored_directive + cmp bl,CTRL_RMATCH + sete [unprocessed_matching] + mov dl,DBLOCK_CONTROL + xor ecx,ecx + call add_directive_block + mov [directive_block],edi + mov [edi+DirectiveBlock.subtype],bl + or [edi+DirectiveBlock.flags],CTRLF_ALLOWS_ELSE + test [assembly_mode],AMODE_SKIP + jnz skip_conditional_block + evaluate_match: + mov [pattern_start],esi + and [number_of_parameters],0 + get_match_pattern: + call move_to_next_symbol + jc invalid_match + qualify_pattern_element: + cmp al,',' + je match_pattern_ok + cmp al,1Ah + je count_pattern_parameter + cmp al,30h + je invalid_match + cmp al,'=' + jne skip_pattern_symbol + call skip_token + xor ecx,ecx + call move_to_next_symbol + jc invalid_match + test ecx,ecx + jz skip_pattern_symbol + jmp qualify_pattern_element + count_pattern_parameter: + call detect_numeric_symbol + jc invalid_match + inc [number_of_parameters] + add esi,1+4 + jmp get_match_pattern + skip_pattern_symbol: + call skip_token + jmp get_match_pattern + match_pattern_ok: + mov [pattern_end],esi + inc esi + mov [argument_start],esi + mov eax,[embedded_context] + mov [matched_context],eax + mov edi,[expression_workspace.memory_start] + mov ecx,[number_of_parameters] + assert sizeof.MatchedParameter = 1 shl 3 + shl ecx,3 + mov ebx,ecx + mov edx,expression_workspace + call reserve_workspace + add edi,ebx + mov [substitutions_end],edi + cmp [unprocessed_matching],0 + jne substitutions_ready + detect_substitutions: + mov eax,[embedded_context] + mov [stored_context],eax + and [symbol_definition],0 + mov dl,SYMCLASS_EXPRESSION + call identify_symbol + jc substitutions_ready + test edi,edi + jnz check_for_substitution + call skip_literal + xor ebx,ebx + check_for_substitution: + test ebx,ebx + jz detect_substitutions + call get_available_value + jc detect_substitutions + cmp [edx+ValueDefinition.type],VALTYPE_SYMBOLIC + jne detect_substitutions + mov eax,[current_pass] + mov [ebx+SymbolTree_Leaf.last_use_pass],eax + mov ebx,edx + mov edi,[substitutions_end] + mov edx,expression_workspace + mov ecx,sizeof.SymbolSubstitution + call reserve_workspace + mov eax,[ebx+ValueDefinition.value] + mov [edi+SymbolSubstitution.value_start],eax + add eax,[ebx+ValueDefinition.value_length] + mov [edi+SymbolSubstitution.value_end],eax + mov ebx,esi + mov esi,[symbol_start] + mov [edi+SymbolSubstitution.symbol_start],esi + scan_replaced_symbol: + mov al,[esi] + cmp al,20h + je whitespace_in_replaced_symbol + cmp al,40h + jne actual_replaced_symbol + inc esi + mov [stored_context],esi + add esi,sizeof.RecognitionContext + jmp next_token_in_replaced_symbol + whitespace_in_replaced_symbol: + inc esi + jmp next_token_in_replaced_symbol + actual_replaced_symbol: + call skip_token + mov ecx,esi + mov edx,[stored_context] + next_token_in_replaced_symbol: + cmp esi,ebx + jb scan_replaced_symbol + mov [edi+SymbolSubstitution.symbol_end],ecx + mov [edi+SymbolSubstitution.leftover_context],edx + add edi,sizeof.SymbolSubstitution + mov [substitutions_end],edi + jmp detect_substitutions + substitutions_ready: + mov ebx,[number_of_parameters] + assert sizeof.MatchedParameter = 1 shl 3 + shl ebx,3 + add ebx,[expression_workspace.memory_start] + mov esi,[argument_start] + mov edi,[pattern_start] + xor eax,eax + mov [parameter_index],eax + mov [stored_position],eax + mov [substitution_active],al + match_with_pattern: + call get_pattern_token + cmp ah,1Ah + jne exact_match + wildcard_match: + call get_matched_token + jc no_match + cmp al,20h + jne wildcard_matched + inc esi + jmp wildcard_match + wildcard_matched: + xor eax,eax + mov edx,[expression_workspace.memory_start] + mov ecx,[parameter_index] + jecxz prepare_matched_parameter + dec ecx + assert sizeof.MatchedParameter = 1 shl 3 + shl ecx,3 + add edx,ecx + push edi + mov edi,[assembly_workspace.memory_start] + add edi,[edx+MatchedParameter.assembly_position] + push edx + call close_matched_value + pop edx + mov eax,edi + sub eax,[assembly_workspace.memory_start] + mov [edx+MatchedParameter.assembly_position],eax + pop edi + add edx,sizeof.MatchedParameter + prepare_matched_parameter: + mov [edx+MatchedParameter.pattern],edi + inc [parameter_index] + mov edi,[assembly_workspace.memory_start] + add edi,eax + xor eax,eax + mov [embedded_context],eax + mov [stored_position],eax + mov [whitespace_matching],al + push edx + call copy_matched_token + pop edx + sub edi,[assembly_workspace.memory_start] + mov [edx+MatchedParameter.assembly_position],edi + mov edi,[edx+MatchedParameter.pattern] + add edi,1+4 + call get_pattern_token + cmp ah,1Ah + je wildcard_match + cmp ah,'?' + jne exact_match + inc edi + jmp match_with_pattern + exact_match: + cmp [stored_position],0 + jne match_position_stored + mov [stored_position],esi + mov [stored_substitution],ebx + mov dl,[substitution_active] + mov [stored_substitution_activity],dl + mov ecx,[matched_context] + mov [stored_context],ecx + mov [stored_pattern],edi + or [whitespace_matching],1 + match_position_stored: + cmp ah,',' + je end_of_pattern + cmp ah,20h + je allow_whitespace + cmp ah,'=' + jne match_tokens + inc edi + call get_pattern_token + match_tokens: + call get_matched_token + jc no_match + cmp al,20h + je match_whitespace + and [whitespace_matching],0 + cmpsb + jne token_mismatch + cmp ah,1Ah + je match_name_tokens + cmp ah,22h + je match_string_tokens + cmp ah,27h + je match_string_tokens + cmp ah,30h + jne match_with_pattern + mov ecx,esi + mov edx,edi + lodsd + add esi,eax + mov eax,[edi] + lea edi,[edi+4+eax] + call get_pattern_token + cmp ah,'?' + jne compare_token_contents + inc edi + jmp compare_token_contents + match_string_tokens: + lodsd + mov ecx,eax + mov edx,[edi] + add edi,4 + compare_token_contents: + cmp ecx,edx + je match_with_pattern + push esi edi + mov esi,ecx + mov edi,edx + mov ecx,[esi] + add ecx,4 + repe cmpsb + pop edi esi + jne retract_match + jmp match_with_pattern + match_name_tokens: + lodsd + mov ecx,eax + mov edx,[edi] + add edi,4 + call get_pattern_token + cmp ah,'?' + je case_insensitive_match + cmp ecx,edx + je match_with_pattern + push esi edi + mov esi,ecx + mov edi,edx + lodsd + scasd + jne name_mismatch + mov ecx,eax + mov eax,[esi+ecx] + cmp eax,[edi+ecx] + jne name_mismatch + repe cmpsb + jne name_mismatch + pop edi esi + jmp match_with_pattern + name_mismatch: + pop edi esi + jmp retract_match + case_insensitive_match: + inc edi + cmp ecx,edx + je match_with_pattern + push esi edi + mov esi,ecx + mov edi,edx + lodsd + scasd + jne name_mismatch + mov ecx,eax + mov eax,[esi+ecx+4] + cmp eax,[edi+ecx+4] + jne name_mismatch + xor eax,eax + compare_case_insensitively: + lodsb + mov dl,[characters+eax] + mov al,[edi] + inc edi + cmp dl,[characters+eax] + jne name_mismatch + loop compare_case_insensitively + pop edi esi + jmp match_with_pattern + match_converted_number: + call convert_number_to_match + jmp compare_token_contents + match_with_converted_number: + xchg esi,edi + call convert_number_to_match + xchg esi,edi + jmp compare_token_contents + convert_number_to_match: + mov edx,esi + lodsd + add esi,eax + push ebx esi edi + call convert_number_back + pop edi esi ebx + mov ecx,[edi] + add edi,4 + retn + token_mismatch: + cmp ax,1A30h + je match_converted_number + cmp ax,301Ah + je match_with_converted_number + retract_match: + xor esi,esi + xchg esi,[stored_position] + test esi,esi + jz no_match + mov ebx,[stored_substitution] + mov dl,[stored_substitution_activity] + mov [substitution_active],dl + mov ecx,[stored_context] + mov [matched_context],ecx + mov edi,[stored_pattern] + and [whitespace_matching],0 + mov edx,[parameter_index] + sub edx,1 + jc no_match + assert sizeof.MatchedParameter = 1 shl 3 + shl edx,3 + add edx,[expression_workspace.memory_start] + push edi + mov edi,[assembly_workspace.memory_start] + add edi,[edx+MatchedParameter.assembly_position] + push edx + copy_whitespace: + call get_matched_token + jc no_match + cmp al,20h + jne copy_additional_symbol + call copy_matched_token + jmp copy_whitespace + copy_additional_symbol: + call copy_matched_token + pop edx + sub edi,[assembly_workspace.memory_start] + mov [edx+MatchedParameter.assembly_position],edi + pop edi + jmp match_with_pattern + match_whitespace: + cmp al,ah + je required_whitespace + cmp [whitespace_matching],0 + je retract_match + inc esi + jmp match_tokens + required_whitespace: + inc esi + allow_whitespace: + inc edi + or [whitespace_matching],1 + jmp match_with_pattern + end_of_pattern: + call get_matched_token + jc pattern_matched + cmp al,20h + jne retract_match + call skip_token + jmp end_of_pattern + pattern_matched: + mov ebx,[parameter_index] + sub ebx,1 + jc matching_done + assert sizeof.MatchedParameter = 1 shl 3 + shl ebx,3 + add ebx,[expression_workspace.memory_start] + mov edi,[assembly_workspace.memory_start] + add edi,[ebx+MatchedParameter.assembly_position] + call close_matched_value + sub edi,[assembly_workspace.memory_start] + mov [ebx+MatchedParameter.assembly_position],edi + matching_done: + cmp [number_of_parameters],0 + je assembly_line + mov [symbol_class],SYMCLASS_PARAMETER + and [name_volatile],0 + and [name_token],0 + or [symbol_required],1 + or [symbol_expected],1 + mov [value_type],VALTYPE_SYMBOLIC + xor eax,eax + mov [value_index],eax + define_matched_parameters: + mov [parameter_index],eax + assert sizeof.MatchedParameter = 1 shl 3 + shl eax,3 + add eax,[expression_workspace.memory_start] + mov edi,[eax+MatchedParameter.pattern] + mov esi,[edi+1] + lodsd + mov ecx,eax + add edi,1+4 + call get_pattern_token + mov edx,[esi+ecx] + mov al,NAME_CASESENSITIVE + cmp ah,'?' + jne matched_name_kind_ok + mov edx,[esi+ecx+4] + mov al,NAME_CASEINSENSITIVE + matched_name_kind_ok: + mov [name_kind],al + mov ebx,[parameter_namespace] + call scan_namespace + mov eax,[parameter_index] + assert sizeof.MatchedParameter = 1 shl 3 + shl eax,3 + add eax,[expression_workspace.memory_start] + mov ecx,[eax+MatchedParameter.assembly_position] + mov esi,ecx + xchg esi,[value_index] + sub ecx,esi + add esi,[assembly_workspace.memory_start] + mov edi,[directive_block] + call add_block_parameter + mov eax,[parameter_index] + inc eax + cmp eax,[number_of_parameters] + jb define_matched_parameters + jmp assembly_line + no_match: + or [assembly_mode],AMODE_SKIP + jmp assembly_line + invalid_match: + mov edx,_invalid_argument + call register_error + or [assembly_mode],AMODE_SKIP + jmp assembly_line + get_pattern_token: + mov ah,[edi] + cmp ah,40h + je skip_pattern_context + retn + skip_pattern_context: + add edi,1+sizeof.RecognitionContext + jmp get_pattern_token + get_matched_token: + cmp ebx,[substitutions_end] + je token_in_line + cmp [substitution_active],0 + jne token_in_substitution + cmp esi,[ebx+SymbolSubstitution.symbol_start] + je substitution_encountered + token_in_line: + cmp esi,[line_end] + je no_more_matched_tokens + check_for_matched_token: + mov al,[esi] + cmp al,40h + je matched_context_change + clc + retn + no_more_matched_tokens: + stc + retn + matched_context_change: + inc esi + cmp [unprocessed_matching],0 + jne matched_context_change_done + mov [matched_context],esi + matched_context_change_done: + add esi,sizeof.RecognitionContext + jmp get_matched_token + substitution_encountered: + mov esi,[ebx+SymbolSubstitution.value_start] + and [matched_context],0 + or [substitution_active],1 + token_in_substitution: + cmp esi,[ebx+SymbolSubstitution.value_end] + jb check_for_matched_token + mov esi,[ebx+SymbolSubstitution.symbol_end] + mov ecx,[ebx+SymbolSubstitution.leftover_context] + mov [matched_context],ecx + add ebx,sizeof.SymbolSubstitution + and [substitution_active],0 + jmp get_matched_token + skip_token: + inc esi + cmp al,1Ah + je skip_token_with_data + cmp al,22h + je skip_token_with_data + cmp al,27h + je skip_token_with_data + cmp al,30h + je skip_internal_token + cmp al,40h + je skip_context_token + retn + skip_token_with_data: + add esi,4 + retn + skip_internal_token: + lodsd + add esi,eax + retn + skip_context_token: + add esi,sizeof.RecognitionContext + retn + copy_matched_token: + mov edx,[matched_context] + cmp edx,[embedded_context] + je matched_context_ok + test edx,edx + jz copy_matched_context + mov eax,[edx] + test eax,eax + jnz copy_matched_context + mov [matched_context],eax + cmp eax,[embedded_context] + je matched_context_ok + mov edx,eax + copy_matched_context: + mov [embedded_context],edx + mov ecx,1+sizeof.RecognitionContext + mov edx,assembly_workspace + call reserve_workspace + mov al,40h + stosb + assert sizeof.RecognitionContext and 11b = 0 + mov ecx,sizeof.RecognitionContext shr 2 + mov edx,[matched_context] + test edx,edx + jz clear_matched_context + xchg esi,edx + rep movsd + mov esi,edx + jmp matched_context_ok + clear_matched_context: + xor eax,eax + rep stosd + matched_context_ok: + mov ecx,1+4 + mov edx,assembly_workspace + call reserve_workspace + lodsb + stosb + cmp al,1Ah + je copy_token_with_data + cmp al,22h + je copy_token_with_data + cmp al,27h + je copy_token_with_data + cmp al,30h + je copy_internal_token + retn + copy_token_with_data: + movsd + retn + copy_internal_token: + mov ecx,[esi] + add ecx,4 + mov edx,assembly_workspace + call reserve_workspace + lodsd + stosd + mov ecx,eax + rep movsb + retn + close_matched_value: + mov eax,[embedded_context] + test eax,eax + jz matched_value_closed + mov ecx,1+sizeof.RecognitionContext + mov edx,assembly_workspace + call reserve_workspace + mov al,40h + stosb + xor eax,eax + assert sizeof.RecognitionContext and 11b = 0 + mov ecx,sizeof.RecognitionContext shr 2 + rep stosd + matched_value_closed: + retn + +else_raw_match_block: + mov bl,CTRL_RMATCH + jmp begin_else_match_block +else_match_block: + mov bl,CTRL_MATCH + begin_else_match_block: + test [assembly_mode],AMODE_DEFINITION + jnz ignored_directive + mov dl,DBLOCK_CONTROL + call find_directive_block + jc unexpected_instruction + mov al,[edi+DirectiveBlock.flags] + test al,CTRLF_ALLOWS_ELSE + jz unexpected_instruction + mov ah,[assembly_mode] + push eax ebx + call close_control_directive_block + mov dl,DBLOCK_CONTROL + xor ecx,ecx + call add_directive_block + mov [directive_block],edi + pop ebx eax + cmp bl,CTRL_RMATCH + sete [unprocessed_matching] + mov [edi+DirectiveBlock.subtype],bl + or [edi+DirectiveBlock.flags],CTRLF_ALLOWS_ELSE + test al,CTRLF_INACTIVE + jnz skip_conditional_block + test ah,AMODE_SKIP + jnz evaluate_match + jmp skip_conditional_block + +end_raw_match_block: + mov bl,CTRL_RMATCH + jmp close_match_block +end_match_block: + mov bl,CTRL_MATCH + close_match_block: + test [assembly_mode],AMODE_DEFINITION + jnz ignored_directive + mov dl,DBLOCK_CONTROL + call find_directive_block + jc unexpected_instruction + cmp [edi+DirectiveBlock.subtype],bl + jne unexpected_instruction + call close_control_directive_block + jmp instruction_assembled + +postponed_block: + test [assembly_mode],AMODE_DEFINITION + jnz ignored_directive + mov dl,DBLOCK_CONTROL + xor ecx,ecx + call add_directive_block + mov [edi+DirectiveBlock.subtype],CTRL_POSTPONE + mov [directive_block],edi + or [assembly_mode],AMODE_SKIP + call get_constituent_value + jc instruction_assembled + cmp al,'?' + jne invalid_argument + mov edi,[directive_block] + or [edi+DirectiveBlock.flags],CTRLF_SUSPENDED + jmp instruction_assembled + +end_postponed_block: + test [assembly_mode],AMODE_DEFINITION + jnz ignored_directive + mov dl,DBLOCK_CONTROL + call find_directive_block + jc end_postponed_execution + cmp [edi+DirectiveBlock.subtype],CTRL_POSTPONE + jne unexpected_instruction + mov al,[edi+DirectiveBlock.prior_assembly_mode] + mov [assembly_mode],al + test al,AMODE_SKIP + jz keep_postponed_block + call close_directive_block + jmp instruction_assembled + keep_postponed_block: + test [edi+DirectiveBlock.flags],CTRLF_SUSPENDED + jnz keep_suspended_block + mov [edi+DirectiveBlock.type],DBLOCK_POSTPONED + jmp instruction_assembled + keep_suspended_block: + mov [edi+DirectiveBlock.type],DBLOCK_SUSPENDED + jmp instruction_assembled + end_postponed_execution: + test [assembly_mode],AMODE_POSTPONED + jz unexpected_instruction + mov eax,[source_context] + mov esi,eax + call release_source_context + and [esi+SourceContext.number_of_entries],0 + jmp pass_done + +define_struc: + mov bl,SYMCLASS_STRUCTURE + jmp begin_macro_definition +define_macro: + mov bl,SYMCLASS_INSTRUCTION + begin_macro_definition: + test [assembly_mode],AMODE_DEFINITION + jnz nested_macro_definition + mov dl,DBLOCK_MACRO + mov ecx,sizeof.MacroData + call add_directive_block + mov [edi+DirectiveBlock.subtype],bl + mov [edi-sizeof.MacroData+MacroData.nesting_counter],1 + xor eax,eax + mov [macro_end_position],eax + mov [macro_definition_active],al + or [assembly_mode],AMODE_DEFINITION + test [assembly_mode],AMODE_SKIP + jnz assembly_line + xor ecx,ecx + call move_to_next_symbol + jc invalid_macro_definition + cmp al,'(' + je macro_with_label_parameter + read_macro_name: + mov dl,bl + call get_macro_definition_symbol + jc invalid_identifier + mov [argument_start],esi + mov eax,[embedded_context] + mov [macro_parameters_context],eax + xor ecx,ecx + call move_to_next_symbol + jc macro_parameters_correct + check_macro_parameters: + cmp al,'#' + jne check_parameter_name + call check_concatenation + jnc check_macro_parameters + invalid_macro_definition: + mov edx,_invalid_argument + call register_error + jmp assembly_line + check_parameter_name: + cmp al,'?' + je parameter_starting_question_mark + cmp al,1Ah + jne invalid_macro_definition + call detect_numeric_symbol + jc invalid_macro_definition + parameter_name_token: + inc esi + lodsd + parameter_name_present: + xor ecx,ecx + call move_to_next_symbol + jc macro_parameters_correct + cmp al,'?' + je check_parameter_name_modifier + cmp al,'#' + jne check_parameter_modifiers + call check_concatenation + jnc check_parameter_name_concatenation + call move_to_next_symbol + jc macro_parameters_correct + jmp check_parameter_modifiers + parameter_starting_question_mark: + inc esi + parameter_name_freeform: + call move_to_next_symbol + jc macro_parameters_correct + cmp al,1Ah + je parameter_name_token + cmp al,'#' + je parameter_name_freeform_concatenation + cmp al,30h + jne invalid_macro_definition + parameter_number_token: + inc esi + lodsd + add esi,eax + jmp parameter_name_present + parameter_name_freeform_concatenation: + call check_concatenation + jc invalid_macro_definition + jmp parameter_name_freeform + check_parameter_name_concatenation: + cmp al,1Ah + je parameter_name_token + cmp al,30h + je parameter_number_token + cmp al,'?' + jne check_parameter_modifiers + check_parameter_name_modifier: + inc esi + call move_to_next_symbol + jc macro_parameters_correct + check_parameter_modifiers: + cmp al,':' + je check_default_value + cmp al,'=' + je check_default_value + cmp al,'*' + jne check_next_parameter + inc esi + call move_to_next_symbol + jnc check_next_parameter + jmp macro_parameters_correct + check_default_value: + mov edi,[expression_workspace.memory_start] + inc esi + call move_to_next_symbol + jc check_plain_default_value + cmp al,'<' + je check_enclosed_default_value + check_plain_default_value: + mov dl,',' + xor dh,dh + mov [breakpoint_token],'&' + call cut_piece_of_line + jmp check_next_parameter + check_enclosed_default_value: + inc esi + mov dl,'>' + mov dh,'<' + mov [breakpoint_token],0 + call cut_piece_of_line + cmp al,'>' + jne invalid_macro_definition + inc esi + call move_to_next_symbol + jc macro_parameters_correct + check_next_parameter: + test al,al + jz macro_parameters_correct + inc esi + cmp al,',' + jne check_final_parameter_modifiers + call move_to_next_symbol + jc invalid_macro_definition + jmp check_macro_parameters + check_final_parameter_modifiers: + cmp al,'&' + jne invalid_macro_definition + call move_to_next_symbol + jnc invalid_macro_definition + macro_parameters_correct: + or [macro_definition_active],1 + cmp [macro_parameters_context],0 + je store_macro_header + xor esi,esi + mov ecx,1+sizeof.RecognitionContext + call append_to_macro + mov al,40h + stosb + mov esi,[macro_parameters_context] + assert sizeof.RecognitionContext and 11b = 0 + mov ecx,sizeof.RecognitionContext shr 2 + rep movsd + store_macro_header: + mov esi,[argument_start] + mov ecx,[line_end] + sub ecx,esi + call append_to_macro + call add_line_break_to_macro + jmp instruction_assembled + nested_macro_definition: + mov dl,DBLOCK_MACRO + call find_directive_block + ; jc internal_error + cmp bl,[edi+DirectiveBlock.subtype] + jne add_nested_macro_block + inc [edi-sizeof.MacroData+MacroData.nesting_counter] + jmp add_line_to_macro + add_nested_macro_block: + mov ecx,sizeof.MacroData + call add_directive_block + mov [edi+DirectiveBlock.subtype],bl + mov [edi-sizeof.MacroData+MacroData.nesting_counter],1 + jmp add_line_to_macro + macro_with_label_parameter: + cmp bl,SYMCLASS_STRUCTURE + jne invalid_macro_definition + mov [argument_start],esi + inc esi + xor ecx,ecx + call move_to_next_symbol + jc invalid_macro_definition + check_label_parameter_initial_concatenation: + cmp al,'#' + jne check_label_parameter_name + call check_concatenation + jnc check_label_parameter_initial_concatenation + jmp invalid_macro_definition + check_label_parameter_name: + cmp al,1Ah + jne invalid_macro_definition + call detect_numeric_symbol + jc invalid_macro_definition + label_parameter_name_present: + inc esi + lodsd + xor ecx,ecx + call move_to_next_symbol + jc invalid_macro_definition + cmp al,'?' + je check_label_parameter_name_modifier + cmp al,'#' + jne label_parameter_name_end + call check_concatenation + jnc check_label_parameter_name_concatenation + call move_to_next_symbol + jc invalid_macro_definition + jmp label_parameter_name_end + check_label_parameter_name_concatenation: + cmp al,1Ah + je label_parameter_name_present + cmp al,'?' + jne label_parameter_name_end + check_label_parameter_name_modifier: + inc esi + call move_to_next_symbol + jc invalid_macro_definition + label_parameter_name_end: + inc esi + cmp al,')' + jne invalid_macro_definition + push esi + or [macro_definition_active],1 + mov ecx,esi + mov esi,[argument_start] + sub ecx,esi + call append_to_macro + and [macro_definition_active],0 + pop esi + xor ecx,ecx + call move_to_next_symbol + jc invalid_macro_definition + jmp read_macro_name + +end_struc: + mov bl,SYMCLASS_STRUCTURE + jmp close_macro_block +end_macro: + mov bl,SYMCLASS_INSTRUCTION + close_macro_block: + mov dl,DBLOCK_MACRO + call find_directive_block + jc unexpected_instruction + cmp bl,[edi+DirectiveBlock.subtype] + je close_macro_definition + ; handle improper nesting, for partial backward-compatibility: + cmp bl,SYMCLASS_INSTRUCTION + jne add_line_to_macro + call close_directive_block + mov dl,DBLOCK_MACRO + call find_directive_block + jc unexpected_instruction + ; cmp [edi+DirectiveBlock.subtype],SYMCLASS_INSTRUCTION + ; jne internal_error + close_macro_definition: + dec [edi-sizeof.MacroData+MacroData.nesting_counter] + jnz add_line_to_macro + mov al,[assembly_mode] + and al,not (AMODE_SKIP or AMODE_DEFINITION) + or al,[edi+DirectiveBlock.prior_assembly_mode] + mov [assembly_mode],al + call close_directive_block + mov al,[assembly_mode] + test al,AMODE_DEFINITION + jne add_line_to_macro + test al,AMODE_SKIP + jnz assembly_line + push esi + cmp [macro_definition_active],0 + je macro_definition_done + mov ebx,[macro_leaf] + call create_value_definition + test edx,edx + jz macro_definition_done + mov [value],eax + mov [value_length],ecx + mov esi,[macro_buffer] + mov ecx,[macro_end_position] + mov [value_type],VALTYPE_SYMBOLIC + call assign_value + mov al,[macro_flags] + or [edx+ValueDefinition.flags],al + macro_definition_done: + pop esi + jmp instruction_assembled + +escape_directive: + mov dl,DBLOCK_MACRO + call find_directive_block + jc unexpected_instruction + cmp [edi-sizeof.MacroData+MacroData.nesting_counter],1 + ja add_line_to_macro + test [edi+DirectiveBlock.prior_assembly_mode],AMODE_DEFINITION + jnz add_line_to_macro + cmp [embedded_context],0 + je escape_context_ok + push esi + xor esi,esi + mov ecx,1+sizeof.RecognitionContext + call append_to_macro + mov al,40h + stosb + mov esi,[embedded_context] + assert sizeof.RecognitionContext and 11b = 0 + mov ecx,sizeof.RecognitionContext shr 2 + rep movsd + pop esi + escape_context_ok: + mov ecx,[line_end] + sub ecx,esi + call append_to_macro + call add_line_break_to_macro + jmp assembly_line +add_line_to_macro: + mov esi,[line_start] + mov ecx,[line_end] + sub ecx,esi + call append_to_macro + call add_line_break_to_macro + jmp assembly_line + append_to_macro: + cmp [macro_definition_active],0 + je macro_appended + mov edi,[macro_buffer] + mov eax,[macro_end_position] + add edi,eax + add eax,ecx + mov [macro_end_position],eax + cmp eax,[macro_buffer_length] + jbe macro_block_ready + push ecx + mov ecx,eax + mov eax,[macro_buffer] + sub edi,eax + call realloc + add edi,eax + mov [macro_buffer],eax + mov [macro_buffer_length],ecx + pop ecx + macro_block_ready: + test esi,esi + jz macro_appended + mov al,cl + and al,11b + shr ecx,2 + rep movsd + mov cl,al + rep movsb + macro_appended: + retn +add_label_to_macro: + mov ecx,esi + xchg esi,[line_start] + sub ecx,esi + call append_to_macro + mov esi,[line_start] + cmp esi,[line_end] + jne assemble_instruction + call add_line_break_to_macro + jmp assemble_instruction +add_line_break_to_macro: + mov eax,[macro_buffer] + mov ecx,[macro_end_position] + cmp ecx,[macro_buffer_length] + jb macro_line_break + add ecx,100h + call realloc + mov [macro_buffer],eax + mov [macro_buffer_length],ecx + mov ecx,[macro_end_position] + macro_line_break: + lea edi,[eax+ecx] + xor al,al + stosb + inc [macro_end_position] + retn + +use_macro: + and [label_branch],0 +use_struc: + mov eax,[current_pass] + mov [ebx+SymbolTree_Leaf.last_use_pass],eax + mov [instruction_value],edx + mov eax,[ebx+SymbolTree_Leaf.branch] + mov [instruction_branch],eax + xor ecx,ecx + call embed_symbolic_value + measure_macro_header: + lodsb + test al,al + jz macro_header_measured + cmp al,1Ah + je measure_token_with_data + cmp al,22h + je measure_token_with_data + cmp al,27h + je measure_token_with_data + cmp al,30h + je measure_internal_token + cmp al,40h + jne measure_macro_header + add esi,sizeof.RecognitionContext + jmp measure_macro_header + measure_token_with_data: + add esi,4 + jmp measure_macro_header + measure_internal_token: + lodsd + add esi,eax + jmp measure_macro_header + macro_header_measured: + mov [instruction_body],esi + dec esi + mov [line_end],esi + mov eax,ebx + mov ecx,[parameter_namespace] + call get_local_anchor + call get_local_namespace + mov [new_local_namespace],ebx + mov eax,[parameter_namespace] + mov [ebx+SymbolTree_Root.chain],eax + mov edx,[instruction_value] + local_namespace_ready: + mov esi,[edx+ValueDefinition.value] + or [symbol_definition],1 + mov ebx,[new_local_namespace] + mov eax,[label_branch] + mov [ebx+SymbolTree_Root.current_label],eax + cmp [label_solid],0 + jne struc_label_to_confirm + mov edx,[local_namespace] + test edx,edx + jz struc_label_ok + cmp eax,[edx+SymbolTree_Root.current_label] + jne struc_label_ok + test [edx+SymbolTree_Root.flags],NAMESPACE_CURRENT_LABEL_TO_CONFIRM + jz struc_label_ok + struc_label_to_confirm: + or [ebx+SymbolTree_Root.flags],NAMESPACE_CURRENT_LABEL_TO_CONFIRM + struc_label_ok: + cmp byte [esi],'(' + jne prepare_macro_parameters + inc esi + mov ebx,[new_local_namespace] + mov dl,SYMCLASS_PARAMETER + call identify_symbol_in_namespace + jc invalid_argument + test ebx,ebx + jz invalid_argument + cmp edi,[new_local_namespace] + jne invalid_argument + push esi + push [line_end] + push [embedded_context] + mov eax,[line_context] + mov [embedded_context],eax + mov esi,[line_start] + mov ecx,[label_instruction_start] + mov [line_end],ecx + sub ecx,esi + add ecx,(1+sizeof.RecognitionContext)*2 + mov edx,assembly_workspace + mov edi,[edx+Workspace.memory_start] + call reserve_workspace + xor edx,edx + mov [breakpoint_token],dl + call extract_piece_of_line + call finish_extracted_parameter + pop [embedded_context] + pop [line_end] + or [ebx+SymbolTree_Leaf.flags],SYM_VARIABLE + call update_value_definition + mov [value_type],VALTYPE_SYMBOLIC + mov esi,[assembly_workspace.memory_start] + mov ecx,edi + sub ecx,esi + call assign_value + pop esi + call move_to_next_symbol + jc invalid_argument + cmp al,')' + jne invalid_argument + inc esi + prepare_macro_parameters: + mov edi,[expression_workspace.memory_start] + add edi,sizeof.LineExcerpt + xor eax,eax + mov [macro_greedy],al + mov [number_of_parameters],eax + macro_parameter_declaration: + mov ebx,[new_local_namespace] + mov dl,SYMCLASS_PARAMETER + push edi + call identify_symbol_in_namespace + mov eax,edi + pop edi + jc macro_parameters_declared + test ebx,ebx + jz invalid_argument + cmp eax,[new_local_namespace] + jne invalid_argument + mov eax,[number_of_parameters] + shl eax,2 + add eax,[assembly_stack_base] + lea ecx,[eax+4] + cmp ecx,[assembly_stack_end] + jbe store_macro_parameter_leaf + mov eax,[assembly_stack_base] + sub ecx,eax + call grow_stack + mov [assembly_stack_base],eax + add ecx,eax + mov [assembly_stack_end],ecx + mov ecx,[number_of_parameters] + shl ecx,2 + add eax,ecx + store_macro_parameter_leaf: + mov [eax],ebx + mov edx,expression_workspace + mov ecx,sizeof.LineExcerpt + call reserve_workspace + mov [edi+LineExcerpt.data_start],esi + mov [edi+LineExcerpt.data_end],esi + inc [number_of_parameters] + call move_to_next_symbol + jc macro_parameters_declared + cmp al,':' + je macro_parameter_with_default_value + cmp al,'=' + je macro_parameter_with_default_value + cmp al,'*' + jne next_parameter_declaration + and [edi+LineExcerpt.data_start],0 + inc esi + call move_to_next_symbol + jnc next_parameter_declaration + xor al,al + jmp next_parameter_declaration + macro_parameter_with_default_value: + inc esi + call move_to_next_symbol + jc get_plain_default_value + cmp al,'<' + je get_enclosed_default_value + get_plain_default_value: + mov dl,',' + xor dh,dh + mov [breakpoint_token],'&' + call cut_piece_of_line + jmp next_parameter_declaration + get_enclosed_default_value: + inc esi + mov dl,'>' + mov dh,'<' + mov [breakpoint_token],0 + call cut_piece_of_line + cmp al,'>' + jne invalid_argument + inc esi + call move_to_next_symbol + jc macro_parameters_declared + next_parameter_declaration: + test al,al + jz macro_parameters_declared + inc esi + add edi,sizeof.LineExcerpt + cmp al,',' + je macro_parameter_declaration + cmp al,'&' + jne invalid_argument + or [macro_greedy],1 + call move_to_next_symbol + jnc invalid_argument + macro_parameters_declared: + call warp_to_next_symbol + ; cmp [number_of_line_embeddings],0 + ; jne internal_error + mov edx,assembly_workspace + mov edi,[edx+Workspace.memory_start] + mov ecx,[line_end] + sub ecx,esi + add ecx,(1+sizeof.RecognitionContext)*2 + call reserve_workspace + xor eax,eax + mov [breakpoint_token],al + mov [parameter_index],eax + macro_parameter_definition: + mov eax,[number_of_parameters] + sub eax,[parameter_index] + jz macro_parameters_ready + jmp get_macro_parameter + macro_parameter_after_comma: + mov eax,[number_of_parameters] + sub eax,[parameter_index] + jz macro_parameters_ready + inc esi + get_macro_parameter: + mov edi,[assembly_workspace.memory_start] + cmp [macro_greedy],0 + je standard_parameter_value + cmp eax,1 + ja standard_parameter_value + xor edx,edx + call extract_piece_of_line + jmp parameter_value_ready + standard_parameter_value: + call extract_argument_value + parameter_value_ready: + mov eax,[parameter_index] + shl eax,2 + add eax,[assembly_stack_base] + mov ebx,[eax] + or [ebx+SymbolTree_Leaf.flags],SYM_VARIABLE + call update_value_definition + mov [value_type],VALTYPE_SYMBOLIC + cmp edi,[assembly_workspace.memory_start] + je empty_parameter_value + call finish_extracted_parameter + push esi + mov esi,[assembly_workspace.memory_start] + mov ecx,edi + sub ecx,esi + assign_parameter_value: + call assign_value + pop esi + parameter_value_assigned: + inc [parameter_index] + call move_to_next_symbol + jc macro_parameter_definition + cmp al,',' + je macro_parameter_after_comma + macro_parameters_ready: + mov edx,[instruction_value] + mov ecx,[instruction_body] + sub ecx,[edx+ValueDefinition.value] + push ecx + call create_source_entry + pop eax + jc stack_limit_exceeded + mov [ebx+SourceEntry.type],SOURCE_MACRO + mov [ebx+SourceEntry.text],edx + mov [ebx+SourceEntry.offset],eax + or [edx+ValueDefinition.flags],VAL_IN_USE + inc [edx+ValueDefinition.reference_count] + mov eax,[new_local_namespace] + mov [local_namespace],eax + and [eax+SymbolTree_Root.parameters],0 + mov [ebx+SourceEntry.local_namespace],eax + mov ecx,[instruction_branch] + test ecx,ecx + jz instruction_assembled + mov [ebx+SourceEntry.name],ecx + or [ebx+SourceEntry.name_length],-1 + jmp instruction_assembled + stack_limit_exceeded: + mov edx,_stack_limit_exceeded + call register_error + test [assembly_mode],AMODE_SKIP + jnz assembly_line + test [assembly_mode],AMODE_DEFINITION + jz assembly_line + jmp add_line_to_macro + empty_parameter_value: + mov edi,[parameter_index] + inc edi + assert sizeof.LineExcerpt = 1 shl 4 + shl edi,4 + add edi,[expression_workspace.memory_start] + mov eax,[edi+LineExcerpt.data_start] + test eax,eax + jz missing_argument + mov ecx,[edi+LineExcerpt.data_end] + sub ecx,eax + push esi + mov esi,eax + test ecx,ecx + jz assign_parameter_value + push ebx edx + mov ebx,edi + mov edi,[assembly_workspace.memory_start] + add ecx,2*(1+sizeof.RecognitionContext) + mov edx,assembly_workspace + call reserve_workspace + mov al,40h + stosb + cmp [ebx+LineExcerpt.recognition_context],0 + je parameter_with_current_context + mov eax,esi + mov esi,[ebx+LineExcerpt.recognition_context] + assert sizeof.RecognitionContext and 11b = 0 + mov ecx,sizeof.RecognitionContext shr 2 + rep movsd + mov [context_boundary],edi + mov esi,eax + jmp copy_parameter_contents + parameter_with_current_context: + call store_current_context + copy_parameter_contents: + call store_token + cmp esi,[ebx+LineExcerpt.data_end] + jne copy_parameter_contents + call store_context_reset_token + pop edx ebx + mov esi,[assembly_workspace.memory_start] + mov ecx,edi + sub ecx,esi + jmp assign_parameter_value + finish_extracted_parameter: + test [assembly_mode],AMODE_DEFINITION + jz store_context_reset_token + cmp [embedded_context],0 + jne store_context_reset_token + retn + store_context_reset_token: + cmp edi,[context_boundary] + je context_reset_in_reused_token + mov al,40h + stosb + store_context_reset: + xor eax,eax + assert sizeof.RecognitionContext and 11b = 0 + mov ecx,sizeof.RecognitionContext shr 2 + rep stosd + retn + context_reset_in_reused_token: + sub edi,sizeof.RecognitionContext + jmp store_context_reset + +local_directive: + test [assembly_mode],AMODE_SKIP + jnz assembly_line + test [assembly_mode],AMODE_DEFINITION + jnz add_line_to_macro + mov ebx,[local_namespace] + test ebx,ebx + jz unexpected_instruction + or [symbol_definition],1 + mov dl,SYMCLASS_PARAMETER + call identify_symbol_in_namespace + jc missing_argument + test ebx,ebx + jz invalid_argument + cmp edi,[local_namespace] + jne invalid_argument + or [ebx+SymbolTree_Leaf.flags],SYM_VARIABLE + call update_value_definition + mov eax,[current_pass] + mov [edx+ValueDefinition.pass],eax + mov [edx+ValueDefinition.type],VALTYPE_NATIVE_COMMAND + mov eax,local_symbol_name + xchg eax,[edx+ValueDefinition.value] + cmp [edx+ValueDefinition.block_length],0 + je next_local_declaration + and [edx+ValueDefinition.block_length],0 + call mfree + next_local_declaration: + call move_to_next_symbol + jc assembly_line + cmp al,',' + jne invalid_argument + inc esi + jmp local_directive +outscope_directive: + test [assembly_mode],AMODE_SKIP + jnz assemble_prefixed_instruction + mov ebx,[source_context] + mov eax,[ebx+SourceContext.number_of_entries] + dec eax + imul eax,sizeof.SourceEntry + lea ebx,[ebx+sizeof.SourceContext+eax] + test [assembly_mode],AMODE_DEFINITION + jnz define_outscope + mov ecx,[ebx+SourceEntry.local_namespace] + test [ecx+SymbolTree_Root.flags],NAMESPACE_UNATTACHED + jnz unexpected_instruction + mov eax,[ecx+SymbolTree_Root.chain] + test eax,eax + jz unexpected_instruction + outscope_namespace_ok: + mov [parameter_namespace],eax + test [eax+SymbolTree_Root.flags],NAMESPACE_UNATTACHED + jz outscope_local_namespace_ok + xor eax,eax + outscope_local_namespace_ok: + mov [local_namespace],eax + assemble_prefixed_instruction: + mov [line_start],esi + mov eax,[embedded_context] + mov [line_context],eax + jmp assemble_instruction + define_outscope: + cmp [ebx+SourceEntry.type],SOURCE_MACRO + je assemble_prefixed_instruction + jmp ignored_directive + +define_data: + and [label_leaf],0 +define_labeled_data: + movzx eax,[edx+ValueDefinition.attribute] + mov [data_unit_length],eax + test eax,eax + jnz data_unit_length_ok + call get_numeric_constant_value + test edx,edx + jz invalid_argument + mov ecx,4 + mov edi,data_unit_length + call fit_value + jc value_out_of_range + js value_out_of_range + cmp [data_unit_length],0 + je value_out_of_range + call get_constituent_value + jc missing_argument + cmp al,':' + je data_unit_length_ok + cmp al,',' + jne invalid_argument + data_unit_length_ok: + cmp [label_leaf],0 + je data_definition + call define_data_label + data_definition: + call peek_at_constituent_value + jc missing_argument + cmp al,'?' + je single_uninitialized_unit + call get_constant_value + cmp al,30h + je numeric_data + cmp al,2Eh + je float_data + cmp al,22h + jne invalid_argument + call output_string + data_outputted: + call get_constituent_value + jc instruction_assembled + cmp al,',' + jne invalid_argument + jmp data_definition + float_data: + push esi + push edx + mov ecx,[data_unit_length] + call initialize_output + pop esi + mov ecx,[data_unit_length] + call fit_float + pop esi + jmp data_outputted + numeric_data: + call keep_value + call peek_at_constituent_value + jc single_data_unit + cmp al,1Ah + jne single_data_unit + test edx,edx + jz single_data_unit + cmp [edx+ValueDefinition.type],VALTYPE_NATIVE_PREPOSITION + jne single_data_unit + cmp [edx+ValueDefinition.value],PREPOSITION_DUP + je duplication_operator + single_data_unit: + mov ecx,[data_unit_length] + call initialize_output + call get_kept_value + mov ecx,[data_unit_length] + call fit_value + jc value_out_of_range + jmp data_outputted + single_uninitialized_unit: + and [current_constituent],0 + mov ecx,[data_unit_length] + call uninitialized_output + jmp data_outputted + duplication_operator: + call get_kept_value + mov edi,number_of_iterations + mov ecx,4 + call fit_value + jc value_out_of_range + js value_out_of_range + xor eax,eax + mov [current_constituent],al + mov [initial_parentheses],eax + call peek_at_constituent_value + jc invalid_argument + cmp al,'?' + je reserve_through_duplication + mov edi,[expression_workspace.memory_start] + mov [expression_sequence_end],edi + or [leave_opening_parentheses],1 + parse_duplicated_expression: + call parse_expression + add [initial_parentheses],ecx + jecxz duplicated_value_parsed + mov eax,[expression_workspace.memory_start] + mov eax,[eax] + test eax,eax + jnz duplicated_value_parsed + call get_constituent_value + jc invalid_argument + cmp al,'?' + jne invalid_argument + mov edi,[expression_workspace.memory_start] + or eax,-1 + stosd + duplicated_value_parsed: + mov [expression_sequence_end],edi + and [leave_opening_parentheses],0 + cmp [initial_parentheses],1 + jb duplicated_data_parsed + ja invalid_argument + call get_constituent_value + jc missing_closing_parenthesis + cmp al,')' + je duplicated_data_parsed + cmp al,',' + jne invalid_argument + mov edi,[expression_sequence_end] + call peek_at_constituent_value + jc invalid_argument + mov edi,[expression_sequence_end] + cmp al,'?' + jne parse_duplicated_expression + and [current_constituent],0 + mov ecx,4 + mov edx,expression_workspace + call reserve_workspace + or eax,-1 + stosd + jmp duplicated_value_parsed + duplicated_data_parsed: + cmp [number_of_iterations],0 + je data_outputted + duplicated_data: + mov eax,[expression_workspace.memory_start] + mov [expression_sequence_cursor],eax + generate_duplicate: + mov eax,[expression_sequence_cursor] + cmp eax,[expression_sequence_end] + je duplicate_outputted + cmp dword [eax],-1 + je duplicated_uninitialized_unit + push esi + mov esi,eax + mov edi,[calculation_workspace.memory_start] + call calculate_parsed_expression + mov [expression_sequence_cursor],esi + pop esi + jc duplicate_outputted + call pop_terms + jc invalid_argument + call get_calculated_constant_value + cmp al,30h + je duplicated_data_unit + cmp al,2Eh + je duplicated_data_unit + cmp al,22h + jne invalid_argument + call output_string + jmp generate_duplicate + duplicated_data_unit: + push eax edx + mov ecx,[data_unit_length] + call initialize_output + pop edx eax + mov ecx,[data_unit_length] + cmp al,2Eh + je duplicated_float + call fit_value + jc value_out_of_range + jmp generate_duplicate + duplicated_float: + push esi + mov esi,edx + call fit_float + pop esi + jmp generate_duplicate + duplicated_uninitialized_unit: + add eax,4 + mov [expression_sequence_cursor],eax + mov ecx,[data_unit_length] + call uninitialized_output + jmp generate_duplicate + duplicate_outputted: + dec [number_of_iterations] + jnz duplicated_data + jmp data_outputted + reserve_through_duplication: + and [current_constituent],0 + mov eax,[number_of_iterations] + mul [data_unit_length] + test edx,edx + jnz duplication_overflow + mov ecx,eax + call uninitialized_output + jmp data_outputted + duplication_overflow: + mov edx,_area_overflow + call register_error + jmp data_outputted + output_string: + push esi + mov esi,edx + mov ecx,[esi] + call initialize_output + lodsd + mov ecx,eax + shr ecx,2 + rep movsd + mov cl,al + and cl,11b + rep movsb + pop esi + mov ecx,[data_unit_length] + xor edx,edx + div ecx + test edx,edx + jz string_outputted + sub ecx,edx + push ecx + call initialize_output + pop ecx + xor al,al + rep stosb + string_outputted: + retn + define_data_label: + push esi + call get_current_address_value + lea edi,[esi+ecx] + mov ecx,4+4+4 + mov edx,assembly_workspace + call reserve_workspace + mov edx,[data_unit_length] + bsr eax,edx + inc al + shr al,3 + inc al + stosd + mov [edi],edx + add edi,eax + xor eax,eax + stosd + mov ebx,[label_leaf] + call create_constant_value_definition + test edx,edx + jz data_label_defined + mov ecx,edi + sub ecx,esi + call assign_shiftable_value + data_label_defined: + call update_current_label + pop esi + retn + +reserve_labeled_data: + movzx eax,[edx+ValueDefinition.attribute] + mov [data_unit_length],eax + call define_data_label + jmp data_reservation +reserve_data: + movzx eax,[edx+ValueDefinition.attribute] + mov [data_unit_length],eax + data_reservation: + call get_numeric_constant_value + test edx,edx + jz invalid_argument + mov ecx,4 + mov edi,[assembly_workspace.memory_start] + call fit_value + jc reservation_out_of_range + js reservation_out_of_range + mov eax,[edi] + mul [data_unit_length] + test edx,edx + jnz reservation_overflow + mov ecx,eax + call uninitialized_output + jmp instruction_assembled + reservation_out_of_range: + mov edx,_value_out_of_range + call register_error + jmp instruction_assembled + reservation_overflow: + mov edx,_area_overflow + call register_error + jmp instruction_assembled + +include_labeled_data: + mov [data_unit_length],1 + call define_data_label +include_data: + xor eax,eax + mov [data_offset],eax + mov [data_offset+4],eax + call get_constant_value + cmp al,22h + jne invalid_argument + push esi + call prepare_file_path + pop esi + call get_constituent_value + jc include_all_available_data + cmp al,':' + jne get_data_length + call get_numeric_constant_value + test edx,edx + jz invalid_argument + mov edi,file_offset + mov ecx,8 + call fit_value + jc value_out_of_range + js value_out_of_range + call get_constituent_value + jc include_all_available_data + get_data_length: + cmp al,',' + jne invalid_argument + call get_numeric_constant_value + test edx,edx + jz invalid_argument + mov edi,data_length + mov ecx,4 + call fit_value + jc value_out_of_range + js value_out_of_range + push esi + mov ecx,[data_length] + call initialize_output + mov esi,[assembly_workspace.memory_start] + call get_file_data + test ebx,ebx + jnz read_from_file + mov esi,[local_path] + call get_file_data + test ebx,ebx + jz data_file_not_found + read_from_file: + call read_file_data + jc file_data_not_read + pop esi + jmp instruction_assembled + data_file_not_found: + mov ebx,esi + mov edx,_source_file_not_found + call register_error + pop esi + jmp instruction_assembled + include_all_available_data: + push esi + mov esi,[assembly_workspace.memory_start] + call get_file_data + test ebx,ebx + jnz measure_available_data + mov esi,[local_path] + call get_file_data + test ebx,ebx + jz data_file_not_found + measure_available_data: + mov ecx,dword [ebx+FileData.length] + mov edx,dword [ebx+FileData.length+4] + sub ecx,dword [file_offset] + sbb edx,dword [file_offset+4] + jc file_data_not_read + jnz out_of_memory + mov [data_length],ecx + push ebx + call initialize_output + pop ebx + call read_file_data + jc file_data_not_read + pop esi + jmp instruction_assembled + file_data_not_read: + mov ebx,esi + mov edx,_error_reading_file + call register_error + pop esi + jmp instruction_assembled + +load_value: + or [symbol_definition],1 + mov dl,SYMCLASS_EXPRESSION + call identify_symbol + jc missing_argument + test ebx,ebx + jz invalid_identifier + mov [label_leaf],ebx + mov [size_specified],0 + call peek_at_constituent_value + jc invalid_load_syntax + cmp al,':' + je size_after_separator + cmp al,1Ah + jne get_load_size + test edx,edx + jz get_load_size + cmp [edx+ValueDefinition.type],VALTYPE_NATIVE_PREPOSITION + jne get_load_size + cmp [edx+ValueDefinition.value],PREPOSITION_FROM + jne invalid_load_syntax + and [current_constituent],0 + jmp get_source_address + size_after_separator: + and [current_constituent],0 + get_load_size: + call get_numeric_constant_value + test edx,edx + jz invalid_load_syntax + or [size_specified],1 + mov edi,value_length + mov ecx,4 + call fit_value + jc value_out_of_range + js value_out_of_range + call get_constituent_value + jc invalid_load_syntax + cmp al,1Ah + jne invalid_load_syntax + test edx,edx + jz invalid_load_syntax + cmp [edx+ValueDefinition.type],VALTYPE_NATIVE_PREPOSITION + jne invalid_load_syntax + cmp [edx+ValueDefinition.value],PREPOSITION_FROM + jne invalid_load_syntax + get_source_address: + call peek_at_constituent_value + jc invalid_argument + cmp al,':' + je load_from_output_offset + mov eax,[current_area] + mov [data_area],eax + and [data_area_symbol],0 + and [leave_opening_parentheses],0 + mov edi,[expression_workspace.memory_start] + call parse_expression + call peek_at_constituent_value + jc source_address_parsed + cmp al,':' + jne source_address_parsed + and [current_constituent],0 + mov edi,[expression_workspace.memory_start] + call get_area_value + jc invalid_area + mov [data_area],edx + mov edi,[expression_workspace.memory_start] + call parse_expression + source_address_parsed: + call calculate_relative_address + jc invalid_argument + mov edi,data_offset + mov ecx,4 + call fit_value + jc data_out_of_area + js data_out_of_area + mov edi,[assembly_workspace.memory_start] + mov eax,[value_length] + stosd + mov ecx,eax + mov edx,assembly_workspace + call reserve_workspace + mov edx,[data_area] + load_from_area: + mov eax,[edx+ValueDefinition.value] + mov ecx,[edx+ValueDefinition.pass] + cmp ecx,[current_pass] + je area_ok + test [eax+AreaHeader.flags],AREA_VARIABLE + jnz source_area_inaccessible + area_ok: + mov ebx,[data_offset] + add ebx,[eax+AreaHeader.base_address_length] + jc data_out_of_area + add ebx,sizeof.AreaHeader + jc data_out_of_area + mov ecx,[edx+ValueDefinition.value_length] + sub ecx,ebx + jbe load_out_of_initialized_data + call prepare_load_length + jc data_out_of_area + xchg esi,ebx + add esi,eax + mov dl,cl + shr ecx,2 + rep movsd + mov cl,dl + and cl,11b + rep movsb + mov esi,ebx + cmp [value_length],0 + je value_loaded + mov ecx,[eax+AreaHeader.uninitialized_length] + jmp load_uninitialized_data + load_out_of_initialized_data: + add ecx,[eax+AreaHeader.uninitialized_length] + jc load_uninitialized_data + xor ecx,ecx + load_uninitialized_data: + call prepare_load_length + jc data_out_of_area + xor eax,eax + mov dl,cl + shr ecx,2 + rep stosd + mov cl,dl + and cl,11b + rep stosb + cmp [value_length],0 + je value_loaded + xor ebx,ebx + xchg ebx,[data_area_symbol] + test ebx,ebx + jz data_out_of_area + mov edx,[ebx+SymbolTree_Leaf.retired_definition] + test edx,edx + jz area_with_no_history + mov ecx,[current_pass] + sub ecx,[edx+ValueDefinition.pass] + cmp ecx,1 + ja area_with_no_history + or [ebx+SymbolTree_Leaf.flags],SYM_LINK_PREDICTED + mov eax,[edx+ValueDefinition.value] + jmp load_from_area + invalid_source_area: + call parse_expression + source_area_inaccessible: + mov edx,_invalid_area + call register_error + jmp loaded_no_value + area_with_no_history: + or [next_pass_needed],1 + data_out_of_area: + mov edx,_address_out_of_range + call register_error + jmp loaded_no_value + invalid_load_syntax: + mov edx,_invalid_argument + call register_error + loaded_no_value: + mov edi,[assembly_workspace.memory_start] + xor eax,eax + stosd + value_loaded: + mov ebx,[label_leaf] + call update_value_definition + test edx,edx + jz instruction_assembled + push esi + mov esi,[assembly_workspace.memory_start] + mov ecx,[esi] + add ecx,4 + mov [value_type],VALTYPE_STRING + call assign_value + pop esi + jmp instruction_assembled + prepare_load_length: + cmp ecx,[value_length] + jbe value_length_ok + mov ecx,[value_length] + value_length_ok: + add [data_offset],ecx + jc load_length_ready + sub [value_length],ecx + load_length_ready: + retn + load_from_output_offset: + call read_output_offset + jc value_out_of_range + mov edx,assembly_workspace + mov edi,[edx+Workspace.memory_start] + mov eax,[value_length] + stosd + mov ecx,eax + call reserve_workspace + push esi + call read_from_output + pop esi + cmp [value_length],0 + jne data_out_of_area + jmp value_loaded + +store_value: + mov [size_specified],0 + call get_constant_value + cmp al,22h + je store_string_value + cmp al,30h + jne invalid_argument + call keep_value + call peek_at_constituent_value + jc invalid_argument + cmp al,':' + je size_after_value + cmp al,1Ah + jne size_before_value + test edx,edx + jz invalid_argument + cmp [edx+ValueDefinition.type],VALTYPE_NATIVE_PREPOSITION + je get_destination_address + size_before_value: + or [size_specified],1 + call get_kept_value + mov edi,value_length + mov ecx,4 + call fit_value + jc value_out_of_range + js value_out_of_range + call get_constant_value + cmp al,22h + je value_after_size_ok + cmp al,30h + jne invalid_argument + value_after_size_ok: + call keep_value + jmp value_with_size_ready + store_string_value: + mov eax,[edx] + mov [value_length],eax + call keep_value + call peek_at_constituent_value + jc invalid_argument + cmp al,':' + jne check_symbol_after_value_with_size + size_after_value: + and [current_constituent],0 + call get_constant_value + cmp al,30h + jne invalid_argument + or [size_specified],1 + mov edi,value_length + mov ecx,4 + call fit_value + jc value_out_of_range + js value_out_of_range + value_with_size_ready: + call peek_at_constituent_value + jc invalid_argument + check_symbol_after_value_with_size: + cmp al,1Ah + jne invalid_argument + test edx,edx + jz invalid_argument + cmp [edx+ValueDefinition.type],VALTYPE_NATIVE_PREPOSITION + jne invalid_argument + get_destination_address: + cmp [edx+ValueDefinition.value],PREPOSITION_AT + jne invalid_argument + and [current_constituent],0 + call peek_at_constituent_value + jc invalid_argument + cmp al,':' + je store_at_output_offset + mov eax,[current_area] + mov [data_area],eax + and [leave_opening_parentheses],0 + mov edi,[expression_workspace.memory_start] + call parse_expression + call peek_at_constituent_value + jc destination_address_parsed + cmp al,':' + jne destination_address_parsed + and [current_constituent],0 + mov edi,[expression_workspace.memory_start] + call get_area_value + jc invalid_area + mov ecx,[current_pass] + cmp [edx+ValueDefinition.pass],ecx + jne invalid_area + mov [data_area],edx + mov edi,[expression_workspace.memory_start] + call parse_expression + destination_address_parsed: + call calculate_relative_address + jc invalid_argument + mov edi,data_offset + mov ecx,4 + call fit_value + jc store_out_of_area + js store_out_of_area + prepare_store: + mov edx,[data_area] + mov eax,[edx+ValueDefinition.value] + or [eax+AreaHeader.flags],AREA_VARIABLE + mov ebx,[data_offset] + xor ecx,ecx + add ebx,[eax+AreaHeader.base_address_length] + adc ecx,0 + add ebx,sizeof.AreaHeader + adc ecx,0 + add ebx,[value_length] + adc ecx,0 + jnz store_outside_initialized_area + cmp ebx,[edx+ValueDefinition.value_length] + ja store_outside_initialized_area + lea edi,[eax+ebx] + mov ecx,[value_length] + sub edi,ecx + call get_kept_value + call fit_value + jc value_out_of_range + jmp instruction_assembled + store_outside_initialized_area: + sub ebx,[edx+ValueDefinition.value_length] + sbb ecx,0 + jnz store_out_of_area + sub [eax+AreaHeader.uninitialized_length],ebx + jnc extend_area + add [eax+AreaHeader.uninitialized_length],ebx + test [eax+AreaHeader.flags],AREA_VIRTUAL + jz store_out_of_area + test [edx+ValueDefinition.flags],VAL_IN_USE + jnz store_out_of_area + and [eax+AreaHeader.uninitialized_length],0 + push edx + mov edx,_address_out_of_range + call register_error + pop edx + extend_virtual_area: + call expand_value + jmp prepare_store + extend_area: + test [eax+AreaHeader.flags],AREA_VIRTUAL + jnz extend_virtual_area + call expand_value + call update_output_offsets + jmp prepare_store + store_out_of_area: + mov edx,_address_out_of_range + call register_error + jmp instruction_assembled + invalid_area: + mov edx,_invalid_area + call register_error + jmp assembly_line + calculate_relative_address: + push esi + mov esi,[expression_workspace.memory_start] + mov edi,[calculation_workspace.memory_start] + call calculate_parsed_expression + jc invalid_relative_address + mov ebx,edi + mov edi,[expression_workspace.memory_start] + mov esi,edi + mov eax,EXPR_AREA_ADDRESS + stosd + mov eax,[data_area] + stosd + mov eax,EXPR_OPERATOR + stosd + mov eax,calculate_sub + stosd + xor eax,eax + stosd + mov edi,ebx + call calculate_parsed_expression + jc invalid_relative_address + call pop_terms + jc invalid_relative_address + cmp [size_specified],0 + jne addressed_length_ok + and [value_length],0 + mov edx,[edi+ExpressionTerm.metadata] + test edx,edx + jz addressed_length_ok + mov esi,edi + mov ecx,4 + mov edi,value_length + call fit_value + jc invalid_relative_address + js invalid_relative_address + mov ecx,[edx] + cmp dword [edx+4+ecx],0 + jne invalid_relative_address + mov edi,esi + addressed_length_ok: + pop esi + mov eax,edi + check_for_uncanceled_terms: + add eax,sizeof.ExpressionTerm + cmp [eax+ExpressionTerm.attributes],0 + je relative_address_ok + cmp [eax+ExpressionTerm.metadata],0 + je check_for_uncanceled_terms + mov edx,_address_out_of_range + call register_error + relative_address_ok: + call get_term_value + clc + retn + invalid_relative_address: + pop esi + stc + retn + read_output_offset: + and [current_constituent],0 + call get_numeric_constant_value + test edx,edx + jz output_offset_out_of_range + push edi + mov edi,file_offset + mov ecx,8 + call fit_value + pop edi + jc output_offset_out_of_range + js output_offset_out_of_range + cmp [size_specified],0 + jne output_offset_ok + and [value_length],0 + mov edx,[edi+ExpressionTerm.metadata] + test edx,edx + jz output_offset_ok + mov ecx,4 + mov edi,value_length + call fit_value + jc output_offset_out_of_range + js output_offset_out_of_range + output_offset_ok: + clc + retn + output_offset_out_of_range: + stc + retn + store_at_output_offset: + call read_output_offset + jc value_out_of_range + mov ecx,[value_length] + mov edx,assembly_workspace + mov edi,[edx+Workspace.memory_start] + call reserve_workspace + call get_kept_value + mov ecx,[value_length] + call fit_value + jc value_out_of_range + push esi + mov esi,[assembly_workspace.memory_start] + call rewrite_output + pop esi + cmp [value_length],0 + jne store_out_of_area + jmp instruction_assembled + +display_data: + call get_constant_value + cmp al,30h + je display_byte + cmp al,22h + jne invalid_argument + test [trace_mode],TRACE_DISPLAY + jnz bypass_display_buffer + cmp [next_pass_needed],0 + jne display_next_value + mov ecx,[edx] + call reserve_display_buffer + mov ebx,esi + mov esi,edx + lodsd + mov ecx,eax + rep movsb + mov esi,ebx + display_next_value: + call get_constituent_value + jc assembly_line + cmp al,',' + jne invalid_argument + jmp display_data + bypass_display_buffer: + mov ebx,esi + mov esi,edx + lodsd + mov ecx,eax + jecxz display_next_bypassing_value + call display_string + display_next_bypassing_value: + mov esi,ebx + jmp display_next_value + display_byte: + test [trace_mode],TRACE_DISPLAY + jnz byte_bypassing_display_buffer + cmp [next_pass_needed],0 + jne display_next_value + mov ecx,1 + call reserve_display_buffer + mov ecx,1 + call fit_value + jc value_out_of_range + jmp display_next_value + byte_bypassing_display_buffer: + mov edi,displayed_byte + mov ecx,1 + call fit_value + jc value_out_of_range + mov ebx,esi + mov esi,edi + mov ecx,1 + call display_string + mov esi,ebx + jmp display_next_value + reserve_display_buffer: + mov edi,[display_data_length] + add ecx,edi + add edi,[display_buffer] + mov [display_data_length],ecx + cmp ecx,[display_buffer_length] + jbe display_buffer_reserve_ok + mov eax,[display_buffer] + sub edi,eax + push edx + call grow_stack + pop edx + add edi,eax + mov [display_buffer],eax + mov [display_buffer_length],ecx + display_buffer_reserve_ok: + retn + +format_directive: + mov edx,[ebx+SymbolTree_Leaf.branch] + call get_symbol_namespace + and [symbol_definition],0 + mov dl,SYMCLASS_INSTRUCTION + call identify_symbol_in_namespace + jc invalid_argument + test ebx,ebx + jz invalid_argument + mov al,[ebx+SymbolTree_Leaf.class] + cmp al,SYMCLASS_INSTRUCTION + jne invalid_argument + call get_available_value + jc invalid_argument + jmp prefixed_directive +format_binary: + call get_constituent_value + jc instruction_assembled + cmp al,1Ah + jne invalid_argument + test edx,edx + jz invalid_argument + cmp [edx+ValueDefinition.type],VALTYPE_NATIVE_PREPOSITION + jne invalid_argument + cmp [edx+ValueDefinition.value],PREPOSITION_AS + jne invalid_argument + cmp [output_extension],0 + jne repeated_declaration + call get_constant_value + cmp al,22h + jne invalid_argument + mov edi,esi + mov ecx,[edx] + lea esi,[edx+4] + mov [output_extension_length],ecx + mov ebx,[auxiliary_output_areas] + call get_from_map + jnc extension_stored + xor eax,eax + mov ecx,[output_extension_length] + call put_into_map + extension_stored: + mov [output_extension],esi + validate_extension: + mov ebx,characters + scan_extension: + test ecx,ecx + jz extension_valid + lodsb + xlatb + test al,al + jz invalid_argument + dec ecx + jmp scan_extension + extension_valid: + mov esi,edi + jmp instruction_assembled + +custom_error: + mov edi,[assembly_workspace.memory_start] + add edi,4 + create_custom_error_message: + push edi + call get_constant_value + pop edi + cmp al,30h + je attach_byte_to_error_message + cmp al,22h + jne invalid_argument + mov ebx,edx + mov ecx,[edx] + mov edx,assembly_workspace + call reserve_workspace + mov edx,esi + lea esi,[ebx+4] + mov ecx,[ebx] + rep movsb + mov esi,edx + jmp collect_next_part_of_message + attach_byte_to_error_message: + mov ebx,edx + mov ecx,1 + mov edx,assembly_workspace + call reserve_workspace + mov edx,ebx + mov ecx,1 + call fit_value + jc value_out_of_range + inc edi + collect_next_part_of_message: + push edi + call get_constituent_value + pop edi + jc custom_error_message_ready + cmp al,',' + jne invalid_argument + jmp create_custom_error_message + custom_error_message_ready: + mov edx,[assembly_workspace.memory_start] + sub edi,edx + sub edi,4 + mov [edx],edi + call register_volatile_error + test edx,edx + jz instruction_assembled + or [edx+Error.flags],ERR_CUSTOM + jmp instruction_assembled + +choose_to_remove_comments: + and [preprocessing_mode],not PMODE_RETAIN_COMMENTS + jmp instruction_assembled +choose_to_retain_comments: + or [preprocessing_mode],PMODE_RETAIN_COMMENTS + jmp instruction_assembled +choose_to_combine_lines: + and [preprocessing_mode],not PMODE_ISOLATE_LINES + jmp instruction_assembled +choose_to_isolate_lines: + or [preprocessing_mode],PMODE_ISOLATE_LINES + jmp instruction_assembled + +missing_argument: + mov edx,_missing_argument + call register_error + jmp assembly_line + +invalid_argument: + mov edx,_invalid_argument + call register_error + jmp assembly_line + +invalid_identifier: + mov edx,_invalid_identifier + call register_error + jmp assembly_line + +value_out_of_range: + mov edx,_value_out_of_range + call register_error + jmp assembly_line + +missing_closing_parenthesis: + mov edx,_missing_closing_parenthesis + call register_error + jmp assembly_line + +unexpected_instruction: + mov edx,_unexpected_instruction + call register_error + jmp assembly_line + +repeated_declaration: + mov edx,_repeated_declaration + call register_error + jmp assembly_line diff --git a/x86_64_sse2_x87/fasm/source/dos/fasmg.asm b/x86_64_sse2_x87/fasm/source/dos/fasmg.asm new file mode 100644 index 0000000..4ce246c --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/dos/fasmg.asm @@ -0,0 +1,651 @@ + +match ,{ + + include 'macro/struct.inc' + +} match -,{ +else + + include 'selfhost.inc' + +end match +_ equ } + +include '../version.inc' + +BUFFER_SIZE = 4000h +STACK_SIZE = 4000h + + format MZ + heap 0 + stack stack_segment:stack_top-stack_bottom + entry loader:startup + +segment loader use16 + + startup: + + mov ax,1687h + int 2Fh + or ax,ax ; DPMI installed? + jnz short no_dpmi + test bl,1 ; 32-bit programs supported? + jz short no_dpmi + mov word [cs:mode_switch],di + mov word [cs:mode_switch+2],es + mov bx,si ; allocate memory for DPMI data + mov ah,48h + int 21h + jnc switch_to_protected_mode + init_failed: + call startup_error + db 'DPMI initialization failed.',0Dh,0Ah,0 + no_dpmi: + call startup_error + db '32-bit DPMI services are not available.',0Dh,0Ah,0 + startup_error: + pop si + push cs + pop ds + show_message: + lodsb + test al,al + jz message_shown + mov dl,al + mov ah,2 + int 21h + jmp show_message + message_shown: + mov ax,4CFFh + int 21h + switch_to_protected_mode: + mov es,ax + mov ds,[ds:2Ch] + mov ax,1 + call far [cs:mode_switch] ; switch to protected mode + jc init_failed + mov cx,1 + xor ax,ax + int 31h ; allocate descriptor for code + jc init_failed + mov si,ax + xor ax,ax + int 31h ; allocate descriptor for data + jc init_failed + mov di,ax + mov dx,cs + lar cx,dx + shr cx,8 + or cx,0C000h + mov bx,si + mov ax,9 + int 31h ; set code descriptor access rights + jc init_failed + mov dx,ds + lar cx,dx + shr cx,8 + or cx,0C000h + mov bx,di + int 31h ; set data descriptor access rights + jc init_failed + mov ecx,main + shl ecx,4 + mov dx,cx + shr ecx,16 + mov ax,7 + int 31h ; set data descriptor base address + jc init_failed + mov bx,si + int 31h ; set code descriptor base address + jc init_failed + mov cx,0FFFFh + mov dx,0FFFFh + mov ax,8 ; set segment limit to 4 GB + int 31h + jc init_failed + mov bx,di + int 31h + jc init_failed + mov ax,ds + mov ds,di + mov [main_selector],di + mov [psp_selector],es + mov gs,ax ; environment selector in GS + cli + mov ss,di + mov esp,stack_top + sti + mov es,di + mov cx,1 + xor ax,ax + int 31h ; allocate descriptor for BIOS data segment + jc init_failed + mov bx,ax + mov ax,gs + lar cx,ax + shr cx,8 + mov ax,9 + int 31h ; set descriptor access rights + jc init_failed + xor cx,cx + mov dx,400h + mov ax,7 + int 31h ; set base address of BIOS data segment + jc init_failed + xor cx,cx + mov dx,0FFh + mov ax,8 + int 31h ; set limit of BIOS data segment + jc init_failed + mov fs,bx ; BIOS data selector in FS + push si + push start + retf + + mode_switch dd ? + +segment main use32 + + start: + + call system_init + + call get_arguments + mov bl,al + cmp [no_logo],0 + jne logo_ok + mov esi,_logo + xor ecx,ecx + call display_string + logo_ok: + test bl,bl + jnz display_usage_information + + xor al,al + mov ecx,[verbosity_level] + jecxz init + or al,TRACE_ERROR_STACK + dec ecx + jz init + or al,TRACE_DISPLAY + init: + + call assembly_init + + mov eax,[fs:6Ch] + mov [timer],eax + + assemble: + mov esi,[initial_commands] + mov edx,[source_path] + call assembly_pass + jc assembly_done + + mov eax,[current_pass] + cmp eax,[maximum_number_of_passes] + jb assemble + + call show_display_data + + mov esi,_error_prefix + xor ecx,ecx + call display_error_string + mov esi,_code_cannot_be_generated + xor ecx,ecx + call display_error_string + mov esi,_message_suffix + xor ecx,ecx + call display_error_string + + jmp assembly_failed + + assembly_done: + + call show_display_data + + cmp [first_error],0 + jne assembly_failed + + cmp [no_logo],0 + jne summary_done + mov eax,[current_pass] + xor edx,edx + call itoa + call display_string + mov esi,_passes + cmp [current_pass],1 + jne display_passes_suffix + mov esi,_pass + display_passes_suffix: + xor ecx,ecx + call display_string + mov eax,[fs:6Ch] + sub eax,[timer] + mov ecx,36000 + mul ecx + shrd eax,edx,16 + shr edx,16 + mov ecx,10 + div ecx + mov [timer],edx + or edx,eax + jz display_output_length + xor edx,edx + call itoa + call display_string + mov esi,_message_suffix + mov ecx,1 + call display_string + mov eax,[timer] + xor edx,edx + call itoa + call display_string + mov esi,_seconds + xor ecx,ecx + call display_string + display_output_length: + call get_output_length + push eax edx + call itoa + call display_string + pop edx eax + mov esi,_bytes + cmp eax,1 + jne display_bytes_suffix + test edx,edx + jnz display_bytes_suffix + mov esi,_byte + display_bytes_suffix: + xor ecx,ecx + call display_string + mov esi,_new_line + xor ecx,ecx + call display_string + summary_done: + + mov ebx,[source_path] + mov edi,[output_path] + call write_output_file + jc write_failed + + call assembly_shutdown + call system_shutdown + + mov ax,4C00h + int 21h + + assembly_failed: + + call show_errors + + call assembly_shutdown + call system_shutdown + + mov ax,4C02h + int 21h + + write_failed: + mov ebx,_write_failed + jmp fatal_error + + out_of_memory: + mov ebx,_out_of_memory + jmp fatal_error + + fatal_error: + + mov esi,_error_prefix + xor ecx,ecx + call display_error_string + mov esi,ebx + xor ecx,ecx + call display_error_string + mov esi,_message_suffix + xor ecx,ecx + call display_error_string + + call assembly_shutdown + call system_shutdown + + mov ax,4C03h + int 21h + + display_usage_information: + + mov esi,_usage + xor ecx,ecx + call display_string + + call system_shutdown + + mov ax,4C01h + int 21h + + get_arguments: + push ds + mov ds,[psp_selector] + mov esi,81h + mov edi,command_line + mov ecx,7Fh + move_command_line: + lodsb + cmp al,0Dh + je command_line_moved + stosb + loop move_command_line + command_line_moved: + pop ds + xor eax,eax + stosb + mov [initial_commands],eax + mov [source_path],eax + mov [output_path],eax + mov [no_logo],al + mov [verbosity_level],eax + mov [maximum_number_of_passes],100 + mov [maximum_number_of_errors],1 + mov [maximum_depth_of_stack],10000 + mov [maximum_depth_of_stack],10000 + mov esi,command_line + mov edi,parameters + get_argument: + xor ah,ah + read_character: + lodsb + test al,al + jz no_more_arguments + cmp al,22h + je switch_quote + cmp ax,20h + je end_argument + stosb + jmp read_character + end_argument: + xor al,al + stosb + find_next_argument: + mov al,[esi] + test al,al + jz no_more_arguments + cmp al,20h + jne next_argument_found + inc esi + jmp find_next_argument + switch_quote: + xor ah,1 + jmp read_character + next_argument_found: + cmp al,'-' + je get_option + cmp al,'/' + je get_option + cmp [source_path],0 + je get_source_path + cmp [output_path],0 + je get_output_path + error_in_arguments: + or al,-1 + retn + get_source_path: + mov [source_path],edi + jmp get_argument + get_output_path: + mov [output_path],edi + jmp get_argument + no_more_arguments: + cmp [source_path],0 + je error_in_arguments + xor al,al + stosb + retn + get_option: + inc esi + lodsb + cmp al,'e' + je set_errors_limit + cmp al,'E' + je set_errors_limit + cmp al,'i' + je insert_initial_command + cmp al,'I' + je insert_initial_command + cmp al,'p' + je set_passes_limit + cmp al,'P' + je set_passes_limit + cmp al,'r' + je set_recursion_limit + cmp al,'R' + je set_recursion_limit + cmp al,'v' + je set_verbose_mode + cmp al,'V' + je set_verbose_mode + cmp al,'n' + je set_no_logo + cmp al,'N' + jne error_in_arguments + set_no_logo: + or [no_logo],-1 + mov al,[esi] + cmp al,20h + je find_next_argument + test al,al + jnz error_in_arguments + jmp find_next_argument + set_verbose_mode: + call get_option_value + jc error_in_arguments + cmp edx,2 + ja error_in_arguments + mov [verbosity_level],edx + jmp find_next_argument + set_errors_limit: + call get_option_value + jc error_in_arguments + test edx,edx + jz error_in_arguments + mov [maximum_number_of_errors],edx + jmp find_next_argument + set_recursion_limit: + call get_option_value + jc error_in_arguments + test edx,edx + jz error_in_arguments + mov [maximum_depth_of_stack],edx + jmp find_next_argument + set_passes_limit: + call get_option_value + jc error_in_arguments + test edx,edx + jz error_in_arguments + mov [maximum_number_of_passes],edx + jmp find_next_argument + get_option_value: + xor eax,eax + mov edx,eax + find_option_value: + cmp byte [esi],20h + jne get_option_digit + inc esi + jmp find_option_value + get_option_digit: + lodsb + cmp al,20h + je option_value_ok + test al,al + jz option_value_ok + sub al,30h + jc invalid_option_value + cmp al,9 + ja invalid_option_value + imul edx,10 + jo invalid_option_value + add edx,eax + jc invalid_option_value + jmp get_option_digit + option_value_ok: + dec esi + clc + ret + invalid_option_value: + stc + ret + insert_initial_command: + push edi + find_command_segment: + cmp byte [esi],20h + jne command_segment_found + inc esi + jmp find_command_segment + command_segment_found: + xor ah,ah + cmp byte [esi],22h + jne measure_command_segment + inc esi + inc ah + measure_command_segment: + mov ebx,esi + scan_command_segment: + mov ecx,esi + mov al,[esi] + test al,al + jz command_segment_measured + cmp ax,20h + je command_segment_measured + cmp ax,22h + je command_segment_measured + inc esi + cmp al,22h + jne scan_command_segment + command_segment_measured: + sub ecx,ebx + mov edi,[initial_commands] + lea eax,[ecx+2] + test edi,edi + jz allocate_initial_commands_buffer + mov edx,[initial_commands_length] + add edi,edx + add eax,edx + cmp eax,[initial_commands_maximum_length] + ja grow_initial_commands_buffer + copy_initial_command: + xchg esi,ebx + rep movsb + mov esi,ebx + sub edi,[initial_commands] + mov [initial_commands_length],edi + mov al,[esi] + test al,al + jz initial_command_ready + cmp al,20h + jne command_segment_found + initial_command_ready: + mov edi,[initial_commands] + add edi,[initial_commands_length] + mov ax,0Ah + stosw + inc [initial_commands_length] + pop edi + jmp find_next_argument + allocate_initial_commands_buffer: + push ecx + mov ecx,eax + call malloc + mov [initial_commands],eax + mov [initial_commands_maximum_length],ecx + mov edi,eax + pop ecx + jmp copy_initial_command + grow_initial_commands_buffer: + push ecx + mov ecx,eax + mov eax,[initial_commands] + call realloc + mov [initial_commands],eax + mov [initial_commands_maximum_length],ecx + mov edi,eax + add edi,[initial_commands_length] + pop ecx + jmp copy_initial_command + + include 'system.inc' + + include '../assembler.inc' + include '../symbols.inc' + include '../expressions.inc' + include '../conditions.inc' + include '../floats.inc' + include '../directives.inc' + include '../calm.inc' + include '../errors.inc' + include '../map.inc' + include '../reader.inc' + include '../output.inc' + include '../console.inc' + + _logo db 'flat assembler version g.',VERSION,13,10,0 + + _usage db 'Usage: fasmg source [output]',13,10 + db 'Optional settings:',13,10 + db ' -e limit Set the maximum number of displayed errors (default 1)',13,10 + db ' -p limit Set the maximum allowed number of passes (default 100)',13,10 + db ' -r limit Set the maximum depth of the stack (default 10000)',13,10 + db ' -v flag Enable or disable showing all lines from the stack (default 0)',13,10 + db ' -i command Insert instruction at the beginning of source',13,10 + db ' -n Do not show logo nor summary',13,10 + db 0 + + _pass db ' pass, ',0 + _passes db ' passes, ',0 + _dot db '.' + _seconds db ' seconds, ',0 + _byte db ' byte.',0 + _bytes db ' bytes.',0 + + _write_failed db 'failed to write the output file',0 + _out_of_memory db 'not enough memory to complete the assembly',0 + _code_cannot_be_generated db 'could not generate code within the allowed number of passes',0 + + include '../tables.inc' + include '../messages.inc' + + align 4 + + include '../variables.inc' + + psp_selector dw ? + main_selector dw ? + + malloc_freelist dd ? + + source_path dd ? + output_path dd ? + maximum_number_of_passes dd ? + + initial_commands dd ? + initial_commands_length dd ? + initial_commands_maximum_length dd ? + + timestamp dq ? + + timer dd ? + verbosity_level dd ? + no_logo db ? + + command_line db 80h dup ? + parameters db 80h dup ? + +segment buffer_segment + + buffer = (buffer_segment-main) shl 4 + + db BUFFER_SIZE dup ? + +segment stack_segment + + stack_bottom = (stack_segment-main) shl 4 + + db STACK_SIZE dup ? + + stack_top = stack_bottom + $ diff --git a/x86_64_sse2_x87/fasm/source/dos/malloc.inc b/x86_64_sse2_x87/fasm/source/dos/malloc.inc new file mode 100644 index 0000000..0bded1b --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/dos/malloc.inc @@ -0,0 +1,373 @@ + +; a very basic implementation of malloc/realloc +; for porting fasmg to systems that do not have such API natively + +struct MemoryHeader + size dd ? ; total size of this block, the lowest bit set for blocks in use + preceding_size dd ? ; total size of the block that precedes this one in memory (zero if this is an initial block of address range) +ends + +struct FreeMemory + header MemoryHeader + right dd ? ; next free block in cyclic list + left dd ? ; previous free block in cyclic list +ends + +VALLOC_MIN = 40000h + +valloc: +; in: ecx = requested minimum size +; out: eax - allocated block, ecx = allocated size, zero if failed +; preserves: ebx, esi, edi +; note: +; this function requests raw memory from the OS; +; it may allocate much more memory than requested (even entire available memory), +; the obtained memory is kept indefinitely in the pool for malloc +; and should be released by OS automatically when the process ends; +; if the OS does not do it automatically, additional list of the memory areas +; may need to be maintained to release them before exit + cmp ecx,VALLOC_MIN + jbe valloc_size_minimum + dec ecx + and ecx,(-1) shl 12 + add ecx,1 shl 12 + jmp valloc_size_ready + valloc_size_minimum: + mov ecx,VALLOC_MIN + valloc_size_ready: + push ebx esi edi + push ecx + mov ebx,ecx + shr ebx,16 + mov ax,501h + int 31h + movzx eax,cx + pop ecx + jc valloc_failed + shl ebx,16 + or eax,ebx + mov edx,main + shl edx,4 + sub eax,edx + pop edi esi ebx + ret + valloc_failed: + xor ecx,ecx + pop edi esi ebx + retn + +malloc: +malloc_fixed: +malloc_growable: +; in: ecx = requested size +; out: eax - allocated block, ecx = allocated size, on error jumps to out_of_memory (does not return) +; preserves: ebx, esi, edi + add ecx,sizeof.MemoryHeader-1 + jc out_of_memory + and ecx,(-1) shl 2 + add ecx,1 shl 2 + jc out_of_memory + cmp ecx,sizeof.FreeMemory + jae malloc_size_ok + mov ecx,sizeof.FreeMemory + malloc_size_ok: + mov eax,[malloc_freelist] + test eax,eax + jz malloc_new + find_fit: + cmp ecx,[eax+MemoryHeader.size] + jbe malloc_use_block + mov eax,[eax+FreeMemory.left] + cmp eax,[malloc_freelist] + jne find_fit + malloc_new: + push ecx + add ecx,sizeof.MemoryHeader + jc out_of_memory + call valloc + test ecx,ecx + jz out_of_memory + xor edx,edx + mov [eax+MemoryHeader.preceding_size],edx + pop edx + push eax + sub ecx,edx + cmp ecx,sizeof.FreeMemory+sizeof.MemoryHeader + jb no_space_for_free_block + mov [eax+MemoryHeader.size],edx + add eax,edx + mov [eax+MemoryHeader.preceding_size],edx + mov edx,ecx + sub edx,sizeof.MemoryHeader + dec edx + and edx,(-1) shl 2 + add edx,1 shl 2 + mov [eax+MemoryHeader.size],edx + mov ecx,[malloc_freelist] + jecxz freelist_empty + mov [eax+FreeMemory.left],ecx + mov edx,eax + xchg edx,[ecx+FreeMemory.right] + mov [eax+FreeMemory.right],edx + mov [edx+FreeMemory.left],eax + mov edx,[eax+MemoryHeader.size] + jmp free_block_ready + no_space_for_free_block: + sub ecx,sizeof.MemoryHeader + add edx,ecx + mov [eax+MemoryHeader.size],edx + jmp append_limiter + freelist_empty: + mov [eax+FreeMemory.left],eax + mov [eax+FreeMemory.right],eax + free_block_ready: + mov [malloc_freelist],eax + append_limiter: + add eax,edx + mov [eax+MemoryHeader.preceding_size],edx + mov [eax+MemoryHeader.size],sizeof.MemoryHeader or 1 ; cannot be freed + pop eax + finish_malloc: + mov ecx,[eax+MemoryHeader.size] + or [eax+MemoryHeader.size],1 + add eax,sizeof.MemoryHeader + sub ecx,sizeof.MemoryHeader + retn + malloc_use_block: + mov edx,[eax+MemoryHeader.size] + sub edx,ecx + cmp edx,sizeof.FreeMemory + jb use_whole_block + mov [eax+MemoryHeader.size],ecx + mov [eax+ecx+MemoryHeader.preceding_size],ecx + add ecx,eax + mov [malloc_freelist],ecx + mov [ecx+MemoryHeader.size],edx + mov [ecx+edx+MemoryHeader.preceding_size],edx + mov edx,[eax+FreeMemory.right] + cmp edx,eax + je update_free_singleton + mov [ecx+FreeMemory.right],edx + mov [edx+FreeMemory.left],ecx + mov edx,[eax+FreeMemory.left] + mov [ecx+FreeMemory.left],edx + mov [edx+FreeMemory.right],ecx + jmp finish_malloc + update_free_singleton: + mov [ecx+FreeMemory.left],ecx + mov [ecx+FreeMemory.right],ecx + jmp finish_malloc + use_whole_block: + mov edx,[eax+FreeMemory.right] + cmp edx,eax + je depleted_freelist + mov ecx,[eax+FreeMemory.left] + mov [ecx+FreeMemory.right],edx + mov [edx+FreeMemory.left],ecx + mov [malloc_freelist],edx + depleted_freelist: + and [malloc_freelist],0 + jmp finish_malloc + +mfree: +; in: eax - memory block +; out: cf set on error +; preserves: ebx, esi, edi +; note: eax may have value 0 or -1, it should be treated as invalid input then + test eax,eax + jz mfree_error + cmp eax,-1 + je mfree_error + sub eax,sizeof.MemoryHeader + mov ecx,[eax+MemoryHeader.size] + btr ecx,0 + jnc mfree_error + cmp ecx,sizeof.FreeMemory + jb mfree_error + cmp [eax+ecx+MemoryHeader.preceding_size],ecx + jne mfree_error + mov [eax+MemoryHeader.size],ecx + mov edx,eax + sub edx,[eax+MemoryHeader.preceding_size] + cmp edx,eax + je no_preceding_block + test [edx+MemoryHeader.size],1 + jz coalesce_with_preceding_block + no_preceding_block: + test [eax+ecx+MemoryHeader.size],1 + jz coalesce_with_following_block + mov ecx,[malloc_freelist] + jecxz mfree_init_freelist + mov edx,[ecx+FreeMemory.right] + mov [eax+FreeMemory.left],ecx + mov [edx+FreeMemory.left],eax + mov [eax+FreeMemory.right],edx + mov [ecx+FreeMemory.right],eax + mfree_ok: + mov [malloc_freelist],eax + clc + retn + mfree_init_freelist: + mov [eax+FreeMemory.left],eax + mov [eax+FreeMemory.right],eax + jmp mfree_ok + mfree_error: + stc + retn + coalesce_with_preceding_block: + add ecx,[edx+MemoryHeader.size] + test [edx+ecx+MemoryHeader.size],1 + jz coalesce_on_both_ends + mov [edx+MemoryHeader.size],ecx + mov [edx+ecx+MemoryHeader.preceding_size],ecx + clc + retn + coalesce_on_both_ends: + lea eax,[edx+ecx] + add ecx,[eax+MemoryHeader.size] + mov [edx+MemoryHeader.size],ecx + mov [edx+ecx+MemoryHeader.preceding_size],ecx + mov [malloc_freelist],edx + mov ecx,[eax+FreeMemory.left] + mov edx,[eax+FreeMemory.right] + mov [edx+FreeMemory.left],ecx + mov [ecx+FreeMemory.right],edx + clc + retn + coalesce_with_following_block: + push ebx + lea ebx,[eax+ecx] + add ecx,[ebx+MemoryHeader.size] + mov [eax+MemoryHeader.size],ecx + mov [eax+ecx+MemoryHeader.preceding_size],ecx + mov ecx,[ebx+FreeMemory.left] + mov edx,[ebx+FreeMemory.right] + mov [ecx+FreeMemory.right],eax + mov [edx+FreeMemory.left],eax + mov ecx,[ebx+FreeMemory.left] + mov edx,[ebx+FreeMemory.right] + mov [eax+FreeMemory.left],ecx + mov [eax+FreeMemory.right],edx + pop ebx + jmp mfree_ok + +realloc: +; in: eax - memory block, ecx = requested size +; out: eax - resized block, ecx = allocated size, on error jumps to out_of_memory (does not return) +; preserves: ebx, esi, edi + add ecx,sizeof.MemoryHeader-1 + jc out_of_memory + and ecx,(-1) shl 2 + add ecx,1 shl 2 + jc out_of_memory + sub eax,sizeof.MemoryHeader + mov edx,[eax+MemoryHeader.size] + and edx,not 1 + cmp ecx,edx + jbe realloc_retain + test [eax+edx+MemoryHeader.size],1 + jnz realloc_and_copy + add edx,[eax+edx+MemoryHeader.size] + cmp ecx,edx + ja realloc_and_copy + sub edx,ecx + cmp edx,sizeof.FreeMemory + jb append_whole_block + push esi edi + push ecx edx + lea edi,[eax+ecx] + xchg ecx,[eax+MemoryHeader.size] + and ecx,not 1 + lea esi,[eax+ecx] + mov ecx,[esi+FreeMemory.left] + mov edx,[esi+FreeMemory.right] + mov [edx+FreeMemory.left],edi + mov [ecx+FreeMemory.right],edi + mov ecx,[esi+FreeMemory.left] + mov edx,[esi+FreeMemory.right] + mov [edi+FreeMemory.left],ecx + mov [edi+FreeMemory.right],edx + mov [malloc_freelist],edi + pop edx ecx + mov [edi+MemoryHeader.size],edx + mov [edi+edx+MemoryHeader.preceding_size],edx + mov [edi+MemoryHeader.preceding_size],ecx + pop edi esi + jmp finish_malloc + append_whole_block: + add edx,ecx + mov [eax+edx+MemoryHeader.preceding_size],edx + xchg edx,[eax+MemoryHeader.size] + and edx,not 1 + add edx,eax + mov ecx,[edx+FreeMemory.left] + cmp ecx,edx + je depleted_freelist + mov edx,[edx+FreeMemory.right] + mov [ecx+FreeMemory.right],edx + mov [edx+FreeMemory.left],ecx + mov [malloc_freelist],ecx + jmp finish_malloc + realloc_retain: + and [eax+MemoryHeader.size],not 1 + jmp finish_malloc + realloc_and_copy: + push esi edi + lea esi,[eax+sizeof.MemoryHeader] + call malloc_growable + push eax ecx + mov edi,eax + mov eax,esi + mov ecx,[esi-sizeof.MemoryHeader+MemoryHeader.size] + shr ecx,2 + rep movsd + call mfree + pop ecx eax + pop edi esi + retn + +if used mcheck + + mcheck: + pushfd + pushad + mov eax,[malloc_freelist] + test eax,eax + jz integrity_verified + verify_freelist: + mov ebx,eax + verify_preceding_blocks: + mov ecx,[ebx+MemoryHeader.preceding_size] + jecxz preceding_blocks_ok + sub ebx,ecx + mov edx,[ebx+MemoryHeader.size] + and edx,not 1 + cmp ecx,edx + je verify_preceding_blocks + jmp internal_error + preceding_blocks_ok: + mov ebx,eax + verify_following_blocks: + mov ecx,[ebx+MemoryHeader.size] + and ecx,not 1 + cmp ecx,sizeof.MemoryHeader + je following_blocks_ok + add ebx,ecx + cmp ecx,[ebx+MemoryHeader.preceding_size] + je verify_following_blocks + jmp internal_error + following_blocks_ok: + mov edx,[eax+FreeMemory.right] + cmp eax,[edx+FreeMemory.left] + je verify_next + jmp internal_error + verify_next: + mov eax,edx + cmp eax,[malloc_freelist] + jne verify_freelist + integrity_verified: + popad + popfd + retn + +end if diff --git a/x86_64_sse2_x87/fasm/source/dos/selfhost.inc b/x86_64_sse2_x87/fasm/source/dos/selfhost.inc new file mode 100644 index 0000000..e60a93e --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/dos/selfhost.inc @@ -0,0 +1,22 @@ + +include '../../examples/x86/include/80386.inc' + +macro format?.MZ? + format binary as 'exe' + include '../../examples/x86/include/format/mz.inc' +end macro + +macro struct? name + macro ends?! + end namespace + esc end struc + virtual at 0 + name name + sizeof.name = $ + end virtual + purge ends? + end macro + esc struc name + label . : sizeof.name + namespace . +end macro diff --git a/x86_64_sse2_x87/fasm/source/dos/system.inc b/x86_64_sse2_x87/fasm/source/dos/system.inc new file mode 100644 index 0000000..8f4866e --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/dos/system.inc @@ -0,0 +1,388 @@ + +LINE_FEED equ 13,10 + +system_init: + cld + mov [malloc_freelist],0 + mov ah,2Ah + int 21h + push dx cx + movzx ecx,cx + mov eax,ecx + sub eax,1970 + mov ebx,365 + mul ebx + mov ebp,eax + mov eax,ecx + sub eax,1969 + shr eax,2 + add ebp,eax + mov eax,ecx + sub eax,1901 + mov ebx,100 + div ebx + sub ebp,eax + mov eax,ecx + xor edx,edx + sub eax,1601 + mov ebx,400 + div ebx + add ebp,eax + movzx ecx,byte [esp+3] + mov eax,ecx + dec eax + mov ebx,30 + mul ebx + add ebp,eax + cmp ecx,8 + jbe months_correction + mov eax,ecx + sub eax,7 + shr eax,1 + add ebp,eax + mov ecx,8 + months_correction: + mov eax,ecx + shr eax,1 + add ebp,eax + cmp ecx,2 + pop cx + jbe day_correction_ok + sub ebp,2 + test ecx,11b + jnz day_correction_ok + xor edx,edx + mov eax,ecx + mov ebx,100 + div ebx + or edx,edx + jnz day_correction + mov eax,ecx + mov ebx,400 + div ebx + or edx,edx + jnz day_correction_ok + day_correction: + inc ebp + day_correction_ok: + pop dx + movzx eax,dl + dec eax + add eax,ebp + mov ebx,24 + mul ebx + push eax + mov ah,2Ch + int 21h + pop eax + push dx + movzx ebx,ch + add eax,ebx + mov ebx,60 + mul ebx + movzx ebx,cl + add eax,ebx + mov ebx,60 + mul ebx + pop bx + movzx ebx,bh + add eax,ebx + adc edx,0 + mov dword [timestamp],eax + mov dword [timestamp+4],edx + retn + +system_shutdown: + ; call mcheck + retn + +dos_int: + push 0 + push 0 + push 0 + pushw buffer_segment + pushw buffer_segment + stc + pushfw + push eax + push ecx + push edx + push ebx + push 0 + push ebp + push esi + push edi + mov ax,300h + mov bx,21h + xor cx,cx + mov edi,esp + push es ss + pop es + int 31h + pop es + mov edi,[esp] + mov esi,[esp+4] + mov ebp,[esp+8] + mov ebx,[esp+10h] + mov edx,[esp+14h] + mov ecx,[esp+18h] + mov ah,[esp+20h] + sahf + mov eax,[esp+1Ch] + lea esp,[esp+32h] + retn + +open: +; in: edx - path to file +; out: ebx = file handle, cf set on error +; preserves: esi, edi + push esi edi + call adapt_path + mov ax,716Ch + mov bx,100000b + mov dx,1 + xor cx,cx + xor si,si + call dos_int + jnc open_done + cmp ax,7100h + je old_open + stc + jmp open_done + old_open: + mov ax,3D00h + xor dx,dx + call dos_int + open_done: + mov bx,ax + pop edi esi + retn + adapt_path: + mov esi,edx + mov edi,buffer + copy_path: + lodsb + cmp al,'/' + jne path_char_ok + mov al,'\' + path_char_ok: + stosb + cmp edi,buffer+BUFFER_SIZE + jae out_of_memory + test al,al + jnz copy_path + retn + +create: +; in: edx - path to file +; out: ebx = file handle, cf set on error +; preserves: esi, edi + push esi edi + call adapt_path + mov ax,716Ch + mov bx,100001b + mov dx,10010b + xor cx,cx + xor si,si + xor di,di + call dos_int + jnc create_done + cmp ax,7100h + je old_create + stc + jmp create_done + old_create: + mov ah,3Ch + xor cx,cx + xor dx,dx + call dos_int + create_done: + mov bx,ax + pop edi esi + retn + +write: +; in: ebx = file handle, edx - data, ecx = number of bytes +; out: cf set on error +; preserves: ebx, esi, edi + push esi edi ebp + mov ebp,ecx + mov esi,edx + write_loop: + mov ecx,BUFFER_SIZE + sub ebp,BUFFER_SIZE + jnc do_write + add ebp,BUFFER_SIZE + mov ecx,ebp + xor ebp,ebp + do_write: + push ecx + mov edi,buffer + shr ecx,2 + rep movsd + mov ecx,[esp] + and ecx,11b + rep movsb + pop ecx + mov ah,40h + xor dx,dx + call dos_int + or ebp,ebp + jnz write_loop + pop ebp edi esi + ret + +read: +; in: ebx = file handle, edx - buffer, ecx = number of bytes +; out: cf set on error +; preserves: ebx, esi, edi + push esi edi ebp + mov ebp,ecx + mov edi,edx + read_loop: + mov ecx,BUFFER_SIZE + sub ebp,BUFFER_SIZE + jnc do_read + add ebp,BUFFER_SIZE + mov ecx,ebp + xor ebp,ebp + do_read: + push ecx + mov ah,3Fh + xor dx,dx + call dos_int + cmp ax,cx + jne read_eof + mov esi,buffer + mov ecx,[esp] + shr ecx,2 + rep movsd + pop ecx + and ecx,11b + rep movsb + or ebp,ebp + jnz read_loop + read_done: + pop ebp edi esi + ret + read_eof: + pop ecx + stc + jmp read_done + +close: +; in: ebx = file handle +; preserves: ebx, esi, edi + mov ah,3Eh + int 21h + ret + +lseek: +; in: ebx = file handle, cl = method, edx:eax = offset +; out: edx:eax = new offset from the beginning of file, cf set on error +; preserves: ebx, esi, edi + mov dx,ax + xchg ecx,eax + shr ecx,16 + mov ah,42h + int 21h + pushf + shl edx,16 + popf + mov dx,ax + mov eax,edx + xor edx,edx + ret + +get_timestamp: +; out: edx:eax = timestamp +; preserves: ebx, ecx, esi, edi + mov eax,dword [timestamp] + mov edx,dword [timestamp+4] + retn + +display_string: +; in: +; esi - string +; ecx = string length, zero for ASCIIZ string +; preserves: ebx, esi + push ebx + mov ebx,1 + jmp write_string +display_error_string: +; in: +; esi - string +; ecx = string length, zero for ASCIIZ string +; preserves: ebx, esi + push ebx + mov ebx,2 + write_string: + test ecx,ecx + jnz string_length_ok + xor al,al + mov edi,esi + or ecx,-1 + repne scasb + neg ecx + sub ecx,2 + string_length_ok: + mov edx,esi + call write + pop ebx + retn + +get_environment_variable: +; in: +; esi - name +; edi - buffer for value +; ecx = size of buffer +; out: +; eax = length of value +; preserves: ebx, esi, edi + push ebx esi edi + mov ebx,esi + xor esi,esi + compare_variable_names: + mov edx,ebx + compare_name_character: + lods byte [gs:esi] + mov ah,[edx] + inc edx + cmp al,'=' + je end_of_variable_name + test ah,ah + jz next_variable + sub ah,al + jz compare_name_character + cmp ah,20h + jne next_variable + cmp al,41h + jb next_variable + cmp al,5Ah + jna compare_name_character + next_variable: + lods byte [gs:esi] + test al,al + jnz next_variable + cmp byte [gs:esi],0 + jne compare_variable_names + mov ah,al + end_of_variable_name: + test ah,ah + jnz next_variable + add ecx,edi + mov edx,esi + copy_variable_value: + lods byte [gs:esi] + cmp edi,ecx + jae variable_value_next_character + stosb + variable_value_next_character: + or al,al + jnz copy_variable_value + lea eax,[esi-1] + sub eax,edx + pop edi esi ebx + ret + +include 'malloc.inc' diff --git a/x86_64_sse2_x87/fasm/source/errors.inc b/x86_64_sse2_x87/fasm/source/errors.inc new file mode 100644 index 0000000..5be1102 --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/errors.inc @@ -0,0 +1,177 @@ + +struct Error + flags dd ? ; ERR_# + message dd ? + symbol dd ? + preprocessed_data dd ? + preprocessed_length dd ? + next dd ? ; pointer to another Error + ; source SourceContext +ends + +ERR_CUSTOM = 1 + +register_volatile_error: +; in: +; edx - 32-bit length followed by string data +; ebx - data for message formatting (only relevant for non-custom messages that contain % character) +; out: +; edx - Error +; preserves: eax, ebx, ecx, esi, edi + cmp [next_pass_needed],0 + jne error_ignored + push eax ebx ecx esi edi + or [message_volatile],1 + jmp get_error_line +register_delayed_error: +; in: +; edx - error message +; esi - SourceContext +; ebx - data for message formatting (only relevant for non-custom messages that contain % character) +; out: +; edx - Error +; preserves: eax, ebx, ecx, esi, edi + cmp [next_pass_needed],0 + jne error_ignored + push eax ebx ecx esi edi + and [message_volatile],0 + and [error_line_start],0 + and [error_line_end],0 + jmp add_error +register_error: +; in: +; edx - error message +; ebx - data for message formatting (only relevant for non-custom messages that contain % character) +; out: +; edx - Error, null when error was not registered +; preserves: eax, ebx, ecx, esi, edi + cmp [next_pass_needed],0 + jne error_ignored + push eax ebx ecx esi edi + and [message_volatile],0 + get_error_line: + mov eax,[line_start] + mov [error_line_start],eax + mov ecx,[line_end] + cmp [number_of_line_embeddings],0 + je error_line_end_ok + mov eax,[line_embeddings] + mov ecx,[eax+LineEmbedding.previous_end] + error_line_end_ok: + mov [error_line_end],ecx + xor esi,esi + add_error: + mov [error_symbol],ebx + lea ebx,[first_error] + xor ecx,ecx + find_last_error: + mov eax,[ebx] + test eax,eax + jz last_error_found + lea ebx,[eax+Error.next] + inc ecx + cmp ecx,[maximum_number_of_errors] + jb find_last_error + pop edi esi ecx ebx eax + xor edx,edx + retn + last_error_found: + cmp [message_volatile],0 + jne prepare_volatile_error + test esi,esi + jnz prepare_error + mov esi,[source_context] + prepare_error: + mov ecx,[esi+SourceContext.number_of_entries] + imul ecx,sizeof.SourceEntry + add ecx,sizeof.SourceContext + cmp [esi+ecx-sizeof.SourceEntry+SourceEntry.type],SOURCE_CALM + jne error_source_context_ready + mov eax,[calm_instruction_number] + mov [esi+ecx-sizeof.SourceEntry+SourceEntry.line_number],eax + and [error_line_start],0 + and [error_line_end],0 + error_source_context_ready: + add ecx,sizeof.Error + add ecx,[error_line_end] + sub ecx,[error_line_start] + mov edi,edx + call malloc + mov [ebx],eax + mov [eax+Error.message],edi + mov ecx,[error_symbol] + mov [eax+Error.symbol],ecx + xor ecx,ecx + mov [eax+Error.flags],ecx + mov [eax+Error.next],ecx + lea edi,[eax+sizeof.Error] + push eax + call clone_source_context + pop edx + store_preprocessed_data: + mov [edx+Error.preprocessed_data],edi + mov esi,[error_line_start] + mov ecx,[error_line_end] + sub ecx,esi + mov [edx+Error.preprocessed_length],ecx + rep movsb + pop edi esi ecx ebx eax + retn + error_ignored: + xor edx,edx + retn + prepare_volatile_error: + mov esi,edx + mov eax,[source_context] + mov ecx,[eax+SourceContext.number_of_entries] + imul ecx,sizeof.SourceEntry + add ecx,sizeof.SourceContext + cmp [eax+ecx-sizeof.SourceEntry+SourceEntry.type],SOURCE_CALM + jne volatile_error_source_context_ready + mov edx,[calm_instruction_number] + mov [eax+ecx-sizeof.SourceEntry+SourceEntry.line_number],edx + and [error_line_start],0 + and [error_line_end],0 + volatile_error_source_context_ready: + add ecx,sizeof.Error + add ecx,[error_line_end] + sub ecx,[error_line_start] + mov edi,ecx + add ecx,[esi] + inc ecx + call malloc + add edi,eax + mov [ebx],eax + mov edx,eax + mov [edx+Error.message],edi + xor eax,eax + mov [edx+Error.next],eax + mov [edx+Error.flags],eax + lodsd + mov ecx,eax + rep movsb + xor al,al + stosb + mov esi,[source_context] + lea edi,[edx+sizeof.Error] + push edx + call clone_source_context + pop edx + jmp store_preprocessed_data + +discard_errors: + mov eax,[first_error] + test eax,eax + jnz discard_error + retn + discard_error: + add eax,sizeof.Error + call release_source_context + sub eax,sizeof.Error + mov ebx,[eax+Error.next] + call mfree + mov eax,ebx + test eax,eax + jnz discard_error + mov [first_error],eax + retn \ No newline at end of file diff --git a/x86_64_sse2_x87/fasm/source/expressions.inc b/x86_64_sse2_x87/fasm/source/expressions.inc new file mode 100644 index 0000000..bbc6b8f --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/expressions.inc @@ -0,0 +1,4202 @@ + +struct ExpressionTerm + attributes dd ? ; EXPR_# in low byte, plus any EXPRF_# + metadata dd ? + value dd ? +ends + +EXPR_NUMBER = 30h +EXPR_STRING = 22h +EXPR_FLOAT = 2Eh + +EXPR_SYMBOL_VALUE = 10h +EXPR_AREA_ADDRESS = 11h +EXPR_OPERATOR = 20h +EXPR_SYMBOL = 40h + +EXPR_MISSING_ARGUMENT = 29h +EXPR_MISSING_PARENTHESIS = 28h + +EXPRF_VALUE_IN_WORKSPACE = 100h +EXPRF_CALM_LITERAL = 200h + +convert_number: +; in: +; edx - 32-bit length followed by a string of that length +; out: +; cf set when number has no known prefix or suffix but is not a plain decimal number +; when cf = 0: +; edx - 32-bit length followed by binary data of that length, null when string did not represent a valid number +; when cf = 1: +; edx preserved +; preserves: esi + mov edi,[value_workspace.memory_start] + add edi,[value_position] + mov ecx,[edx] + add edx,4 + cmp byte [edx],'$' + je pascal_hexadecimal_number + cmp ecx,2 + jb check_number_suffix + cmp word [edx],'0x' + je c_hexadecimal_number + check_number_suffix: + movzx eax,byte [edx+ecx-1] + mov al,[characters+eax] + cmp al,'h' + je suffixed_hexadecimal_number + cmp al,'b' + je suffixed_binary_number + cmp al,'o' + je suffixed_octal_number + cmp al,'q' + je suffixed_octal_number + cmp al,'d' + jne decimal_number + or ah,1 + dec ecx + jz invalid_number + decimal_number: + mov ebx,ecx + check_decimal_digits: + mov al,[edx+ebx-1] + cmp al,27h + je check_next_decimal_digit + cmp al,'_' + je check_next_decimal_digit + cmp al,'0' + jb unknown_number + cmp al,'9' + ja unknown_number + check_next_decimal_digit: + dec ebx + jnz check_decimal_digits + push ecx edx + add ecx,12 + mov edx,value_workspace + call reserve_workspace + xor eax,eax + mov dword [edi+4],eax + add eax,4 + mov dword [edi],eax + pop edx ebx + convert_decimal_number: + mov al,[edx] + cmp al,27h + je skip_decimal_digit + cmp al,'_' + je skip_decimal_digit + sub al,'0' + mov ecx,[edi] + xor ecx,ecx + add_decimal_digit: + add ecx,4 + movzx eax,al + add [edi+ecx],eax + setc al + sets ah + cmp ecx,[edi] + jb add_decimal_digit + or ah,al + jz decimal_digit_added + xor ah,ah + add ecx,4 + mov [edi+ecx],eax + mov [edi],ecx + decimal_digit_added: + dec ebx + jz decimal_number_converted + next_decimal_digit: + inc edx + push ebx edx + mov ebx,10 + xor ecx,ecx + xor edx,edx + multiply_decimal_number: + add ecx,4 + mov eax,edx + xchg eax,[edi+ecx] + mul ebx + add [edi+ecx],eax + cmp ecx,[edi] + jb multiply_decimal_number + test edx,edx + jz decimal_number_multiplied + add ecx,4 + mov [edi+ecx],edx + mov [edi],ecx + decimal_number_multiplied: + pop edx ebx + xor eax,eax + jmp convert_decimal_number + skip_decimal_digit: + inc edx + dec ebx + jnz convert_decimal_number + decimal_number_converted: + lea edx,[edi+4] + mov ecx,[edi] + dec ecx + optimize_number: + movsx eax,byte [edx+ecx-1] + cmp ah,[edx+ecx] + jne number_optimized + loop optimize_number + cmp byte [edx],0 + je number_finished + number_optimized: + inc ecx + number_finished: + sub edx,4 + mov [edx],ecx + lea edi,[edx+4+ecx] + sub edi,[value_workspace.memory_start] + mov [value_position],edi + ; clc + retn + c_hexadecimal_number: + sub ecx,3 + jc invalid_number + add edx,2 + jmp hexadecimal_number + pascal_hexadecimal_number: + inc edx + suffixed_hexadecimal_number: + sub ecx,2 + jc invalid_number + hexadecimal_number: + push edx ecx + inc ecx + shr ecx,1 + add ecx,5 + mov edx,value_workspace + call reserve_workspace + pop ebx edx + push edi + xor eax,eax + stosd + mov [edi],al + xor cl,cl + hexadecimal_digit: + movzx eax,byte [edx+ebx] + cmp al,27h + je skip_hexadecimal_digit + cmp al,'_' + je skip_hexadecimal_digit + mov al,[characters+eax] + sub al,'0' + jc invalid_digit + cmp al,10 + jb hexadecimal_digit_ok + sub al,'a'-'0' + jc invalid_digit + add al,10 + cmp al,16 + jae invalid_digit + hexadecimal_digit_ok: + shl al,cl + or [edi],al + sub ebx,1 + jc number_converted + xor cl,4 + jnz hexadecimal_digit + inc edi + mov [edi],cl + jmp hexadecimal_digit + skip_hexadecimal_digit: + sub ebx,1 + jnc hexadecimal_digit + number_converted: + pop edx + inc edi + and byte [edi],0 + add edx,4 + mov ecx,edi + sub ecx,edx + jmp optimize_number + suffixed_binary_number: + sub ecx,2 + jc invalid_number + push edx ecx + add ecx,7 + shr ecx,3 + add ecx,5 + mov edx,value_workspace + call reserve_workspace + pop ebx edx + push edi + xor eax,eax + stosd + mov [edi],al + xor cl,cl + binary_digit: + mov al,[edx+ebx] + cmp al,27h + je skip_binary_digit + cmp al,'_' + je skip_binary_digit + sub al,'0' + jc invalid_digit + cmp al,2 + jae invalid_digit + shl al,cl + or [edi],al + sub ebx,1 + jc number_converted + inc cl + and cl,111b + jnz binary_digit + inc edi + mov [edi],cl + jmp binary_digit + skip_binary_digit: + sub ebx,1 + jnc binary_digit + jmp number_converted + suffixed_octal_number: + sub ecx,2 + jc invalid_number + push edx ecx + inc ecx + shr ecx,1 + add ecx,5 + mov edx,value_workspace + call reserve_workspace + pop ebx edx + push edi + xor eax,eax + stosd + mov [edi],al + xor cl,cl + octal_digit: + mov al,[edx+ebx] + cmp al,27h + je skip_octal_digit + cmp al,'_' + je skip_octal_digit + sub al,'0' + jc invalid_digit + cmp al,8 + jae invalid_digit + shl eax,cl + or [edi],al + sub ebx,1 + jc number_converted + add cl,3 + cmp cl,8 + jb octal_digit + sub cl,8 + inc edi + mov [edi],ah + xor eax,eax + jmp octal_digit + skip_octal_digit: + sub ebx,1 + jnc octal_digit + jmp number_converted + unknown_number: + test ah,ah + jnz invalid_number + sub edx,4 + stc + retn + invalid_digit: + pop edi + invalid_number: + xor edx,edx + mov al,30h + clc + retn + +convert_number_back: +; in: +; edx - 32-bit length followed by binary data of that length +; out: +; edx - 32-bit length followed by a string of that length +; note: +; the number is treated as unsigned +; returned string is in temporary storage and should be copied out of it immediately + mov edi,[value_workspace.memory_start] + add edi,[value_position] + mov ecx,[edx] + add ecx,2 + shl ecx,2 + mov esi,edx + mov edx,value_workspace + call reserve_workspace + lodsd + lea ebx,[edi+4+(eax+1)*4] + mov edx,edi + mov ecx,eax + shr ecx,2 + rep movsd + mov cl,al + and cl,11b + rep movsb + xchg eax,ecx + stosd + jecxz highest_dword_offset_ok + dec ecx + and ecx,not 11b + highest_dword_offset_ok: + mov esi,edx + mov edi,ebx + push edi + obtain_digit: + xor edx,edx + divide_highest_dwords: + mov eax,[esi+ecx] + call div10 + test eax,eax + jnz more_digits_to_come + sub ecx,4 + jnc divide_highest_dwords + pop ecx + cmp ecx,edi + je store_final_digit + test dl,dl + jz finish_number + store_final_digit: + add dl,'0' + dec edi + mov [edi],dl + finish_number: + sub ecx,edi + sub edi,4 + mov [edi],ecx + mov edx,edi + retn + more_digits_to_come: + mov ebx,ecx + divide_remaining_dwords: + mov [esi+ebx],eax + sub ebx,4 + jc store_digit + mov eax,[esi+ebx] + call div10 + jmp divide_remaining_dwords + store_digit: + add dl,'0' + dec edi + mov [edi],dl + jmp obtain_digit + div10: + ; cmp edx,10 + ; jae internal_error + push ebx ecx + push eax + mov ebx,eax + mov ecx,edx + shld edx,eax,2 + sub ebx,edx + sbb ecx,0 + mov eax,ebx + mov ebx,1999999Ah + mul ebx + mov eax,ecx + imul eax,ebx + add eax,edx + pop edx + imul ecx,eax,10 + sub edx,ecx + cmp edx,10 + jb div10_done + sub edx,10 + inc eax + div10_done: + pop ecx ebx + retn + +fit_value: +; in: +; edx - 32-bit length followed by numeric data +; ecx = number of bytes that the value has to fit into +; edi - buffer for the specified amount of bytes, null when just checking whether value fits in range +; out: +; sf set when value is negative +; cf set if value does not fit into extended range for specified size +; when cf = 0: +; edi - value fit into specified amount of bytes +; preserves: edx, esi, edi +; note: when value is zero, it fits even into zero byte range + mov ebx,ecx + cmp [edx],ecx + jbe term_in_range + xor al,al + cmp [edx+4+ecx],al + je check_redundant_bytes + dec al + cmp [edx+4+ecx],al + jne term_out_of_range + jecxz term_out_of_range + check_redundant_bytes: + inc ecx + cmp ecx,[edx] + je term_in_range + cmp [edx+4+ecx],al + je check_redundant_bytes + term_out_of_range: + mov ecx,[edx] + mov al,[edx+4+ecx-1] + test al,al + stc + retn + term_in_range: + test edi,edi + jz value_fit + mov ecx,[edx] + cmp ecx,ebx + jbe copy_value + mov ecx,ebx + copy_value: + lea eax,[edx+4] + xchg eax,esi + rep movsb + mov esi,eax + mov ecx,[edx] + movsx eax,byte [edx+4+ecx-1] + mov al,ah + mov ecx,ebx + sub ecx,[edx] + jbe value_ready + rep stosb + value_ready: + sub edi,ebx + value_fit: + test al,al + clc + retn + +start_decimal_converter: +; preserves: ebx + mov edx,value_workspace + mov edi,[value_position] + mov [converter_position],edi + add edi,[edx+Workspace.memory_start] + mov ecx,2*sizeof.FloatData + call reserve_workspace + lea ecx,[edi+2*sizeof.FloatData] + sub ecx,[edx+Workspace.memory_start] + mov [value_position],ecx + mov esi,edi + xor eax,eax + assert sizeof.FloatData and 11b = 0 + mov ecx,sizeof.FloatData shr 2 + rep stosd + mov edx,edi + mov ecx,sizeof.FloatData shr 2 + rep stosd + mov [edx+FloatData.exponent],7 + retn + +convert_decimal_digit: +; in: +; al = value of subsequent digit +; ecx = number of zero digits to insert before +; out: +; edi - FloatData holding the value corresponding to digits converted so far +; note: start_decimal_converter should be called before converting the first digit + mov esi,[value_workspace.memory_start] + add esi,[converter_position] + mov edi,esi + shl eax,24 + mov [esi+sizeof.FloatData+FloatData.mantissa],eax + jecxz multiply_by_ten + inc ecx + call multiply_float_by_power_of_ten + jmp add_subsequent_digit + multiply_by_ten: + mov ecx,10 + call multiply_float_by_unsigned_int + add_subsequent_digit: + mov esi,edi + lea ebx,[esi+sizeof.FloatData] + call add_floats + retn + +finish_decimal_conversion: +; out: +; edi - FloatData holding the converted value +; preserves: ebx, ecx, esi, edi +; note: this function should not be called before all the digits of a value have been processed by convert_decimal_digit + mov eax,[converter_position] + mov edi,[value_workspace.memory_start] + add edi,eax + add eax,2*sizeof.FloatData + cmp eax,[value_position] + jne decimal_conversion_finished + sub eax,sizeof.FloatData + mov [value_position],eax + decimal_conversion_finished: + retn + +keep_value: +; in: +; edx - numeric or string data returned by source parsing or expression evaluating functions +; preserves: ebx, esi, edi +; note: +; this function should be used in conjunction with get_kept_value to retain access to data +; while calling other parsing or expression evaluating functions + xor al,al + cmp edx,[value_workspace.memory_end] + jae keep_value_pointer + mov ecx,edx + sub ecx,[value_workspace.memory_start] + jc keep_value_pointer + mov edx,ecx + inc al + keep_value_pointer: + mov [kept_value],edx + mov [kept_value_in_workspace],al + retn + +get_kept_value: +; out: +; edx - numeric or string data previously passed to keep_value +; preserves: eax, ebx, ecx, esi, edi + mov edx,[kept_value] + cmp [kept_value_in_workspace],0 + je kept_value_ok + add edx,[value_workspace.memory_start] + kept_value_ok: + retn + +get_constant_value: +; in: +; esi = pointer into preprocessed line or the last embedded line +; out: +; esi = pointer advanced past the processed part of line +; al = type of value, zero when expression gave no result +; when al = 22h: +; edx - 32-bit length followed by string data +; when al = 30h: +; edx - 32-bit length followed by numeric data +; when al = 2Eh: +; edx - FloatData + call get_expression_value + jc return_empty_type + get_calculated_constant_value: + ; in: + ; edi - list of ExpressionTerm elements as returned by get_expression_value + ; out: + ; same as get_constant_value + ; preserves: edi + call forbid_variable_terms + call get_term_value + mov eax,[edi+ExpressionTerm.attributes] + assert EXPR_STRING = 22h + assert EXPR_NUMBER = 30h + assert EXPR_FLOAT = 2Eh + retn + return_empty_type: + xor al,al + retn + forbid_variable_terms: + mov eax,edi + detect_variable_terms: + add eax,sizeof.ExpressionTerm + cmp [eax+ExpressionTerm.attributes],0 + je expression_terms_ok + cmp [eax+ExpressionTerm.metadata],0 + je detect_variable_terms + mov edx,_misused_variable_term + call register_error + expression_terms_ok: + retn + +get_numeric_constant_value: +; in: +; esi = pointer into preprocessed line or the last embedded line +; out: +; esi = pointer advanced past the processed part of line +; edx - 32-bit length followed by numeric data, null when expression gave no result +; edi - ExpressionTerm, null when expression gave no result + call get_expression_value + jc empty_numeric_constant + call forbid_variable_terms + call get_numeric_term_value + retn + empty_numeric_constant: + xor edx,edx + xor edi,edi + retn + +get_expression_value: +; in: +; esi = pointer into preprocessed line or the last embedded line +; out: +; cf set if expression gave no result +; esi = pointer advanced past the processed part of line +; when cf = 0: +; edi - list of ExpressionTerm elements, the closing element has attributes set to zero + mov edi,[expression_workspace.memory_start] + and [leave_opening_parentheses],0 + call parse_expression + push esi + mov esi,[expression_workspace.memory_start] + mov edi,[calculation_workspace.memory_start] + call calculate_parsed_expression + call pop_terms + pop esi + retn + +convert_terms_to_numeric_value: +; in: +; edi - list of ExpressionTerm elements as returned by get_expression_value +; out: +; esi - value in VALTYPE_NUMERIC format +; ecx = length of value +; note: the returned value is placed in assembly workspace + mov edx,assembly_workspace + convert_terms_to_numeric_value_in_workspace: + ; in: + ; edx - Workspace + ; edi - list of ExpressionTerm elements as returned by get_expression_value + ; out: + ; esi - value in VALTYPE_NUMERIC format (same [edx+Workspace.memory_start]) + ; ecx = length of value + ; preserves: edx + mov ebx,[edx+Workspace.memory_start] + copy_numeric_term_values: + mov esi,edx + call get_numeric_term_value + xchg esi,edx + xchg ebx,edi + mov ecx,[esi] + add ecx,8 + call reserve_workspace + lodsd + stosd + mov ecx,eax + shr ecx,2 + rep movsd + mov cl,al + and cl,11b + rep movsb + next_numeric_term: + add ebx,sizeof.ExpressionTerm + cmp [ebx+ExpressionTerm.attributes],0 + je numeric_term_values_copied + mov eax,[ebx+ExpressionTerm.metadata] + test eax,eax + jz next_numeric_term + stosd + xchg ebx,edi + jmp copy_numeric_term_values + numeric_term_values_copied: + xor eax,eax + stosd + mov esi,[edx+Workspace.memory_start] + mov ecx,edi + sub ecx,esi + retn + +update_predicted_shift: +; in: +; edx - ValueDefinition that is going to be updated +; esi - new value in VALTYPE_NUMERIC fomat +; preserves: ebx, ecx, edx, esi + test edx,edx + jz zero_shift + cmp [edx+ValueDefinition.type],VALTYPE_NUMERIC + jne zero_shift + mov eax,[current_pass] + dec eax + cmp eax,[edx+ValueDefinition.pass] + jne zero_shift + call get_low_dword + mov [predicted_shift],eax + mov edi,[edx+ValueDefinition.value] + xchg esi,edi + call get_low_dword + sub [predicted_shift],eax + test byte [predicted_shift+3],11000000b + mov esi,edi + jnp zero_shift + retn + zero_shift: + mov [predicted_shift],0 + retn + get_low_dword: + mov eax,[esi] + cmp eax,1 + jb low_dword_ready + je dword_from_single_byte + cmp eax,3 + jb dword_from_two_bytes + je dword_from_three_bytes + mov eax,[esi+4] + low_dword_ready: + retn + dword_from_single_byte: + movsx eax,byte [esi+4] + retn + dword_from_two_bytes: + movsx eax,word [esi+4] + retn + dword_from_three_bytes: + mov eax,[esi+4-1] + sar eax,8 + retn + +get_area_value: +; in: +; edi - command sequence created by parse_expression +; out: +; cf set if expression does not yield a valid and accessible VALTYPE_AREA value +; when cf = 0: +; edx - ValueDefinition +; preserves: esi + cmp byte [edi],EXPR_SYMBOL_VALUE + jne no_plain_area_symbol + cmp dword [edi+3*4],0 + jne no_plain_area_symbol + mov edx,[edi+2*4] + test edx,edx + jz no_plain_area_symbol + cmp [edx+ValueDefinition.type],VALTYPE_AREA + jne no_plain_area_symbol + mov ebx,[edi+4] + call mark_symbol_as_used + clc + retn + no_plain_area_symbol: + push esi + mov esi,edi + mov edi,[calculation_workspace.memory_start] + call calculate_parsed_expression + jc invalid_area_expression + call pop_terms + jc invalid_area_expression + mov eax,edi + xor esi,esi + extract_variable_term: + add eax,sizeof.ExpressionTerm + cmp [eax+ExpressionTerm.attributes],0 + je variable_term_extracted + cmp [eax+ExpressionTerm.metadata],0 + je extract_variable_term + test esi,esi + jnz invalid_area_expression + mov esi,eax + jmp extract_variable_term + variable_term_extracted: + test esi,esi + jz invalid_area_expression + call get_term_value + xor ecx,ecx + mov edi,ecx + call fit_value + jc invalid_area_expression + mov edi,esi + call get_term_value + mov ecx,1 + mov edi,value + call fit_value + jc invalid_area_expression + cmp byte [value],1 + jne invalid_area_expression + mov ebx,[esi+ExpressionTerm.metadata] + mov edx,[ebx+SymbolTree_Leaf.definition] + test edx,edx + jz invalid_area_expression + cmp [edx+ValueDefinition.type],VALTYPE_AREA + jne invalid_area_expression + pop esi + clc + retn + invalid_area_expression: + pop esi + invalid_area_symbol: + stc + retn + +get_constituent_value: +; same as get_processed_value +; note: after any expression evaluation functions have been used, only this function should be used to retrieve constituents of line + xor al,al + xchg al,[current_constituent] + test al,al + jz get_processed_value + get_constituent_components: + mov ebx,[constituent_symbol] + mov edx,[constituent_value] + mov ecx,[constituent_whitespace] + clc + retn + +peek_at_constituent_value: +; same as get_constituent_value, but returned values are also kept in constituent variables +; note: +; the retrieved value is still going to be available to expression evaluation functions or to the next call of get_constituent_value +; to consume the value it is enough to set [current_constituent] to zero + cmp [current_constituent],0 + jne get_current_constituent + call get_processed_value + jc constituent_value_ok + mov [current_constituent],al + mov [constituent_symbol],ebx + mov [constituent_value],edx + mov [constituent_whitespace],ecx + constituent_value_ok: + retn + get_current_constituent: + mov al,[current_constituent] + jmp get_constituent_components + +parse_expression: +; in: +; esi = pointer into preprocessed line or the last embedded line +; edi = pointer to a place in expression workspace where the parsed command sequence should be stored +; [leave_opening_parentheses] = non-zero when parentheses opened in the beginning of expression that did not get closed should be returned to caller instead of being registered in parsed expression +; out: +; esi = pointer advanced past the processed part of line +; edi = pointer advanced past the created sequence in expression workspace +; ecx = number of parentheses opened in the beginning of expression that did not get closed (zero when [leave_opening_parentheses] was zero) + mov [expression_end],edi + sub edi,[expression_workspace.memory_start] + mov [expression_position],edi + mov eax,[operator_stack_base] + mov [operator_stack],eax + mov [operator_stack_position],eax + mov [operator_argument_expected],1 + get_expression_element: + mov edi,[expression_end] + mov edx,expression_workspace + mov ecx,[operator_stack_position] + sub ecx,[operator_stack_base] + add ecx,16 + call reserve_workspace + mov [expression_end],edi + call peek_at_constituent_value + mov edi,[expression_end] + jc expression_line_end + cmp al,'(' + je open_subexpression + cmp al,')' + je close_subexpression + cmp al,30h + je store_expression_number + cmp al,22h + je store_expression_string + cmp al,2Eh + je store_expression_float + cmp al,1Ah + je expression_symbol + movzx eax,al + shl eax,2 + add eax,[operator_table] + mov edx,[eax] + test edx,edx + jz terminate_expression + xor ebx,ebx + mov ecx,edx + jmp identify_operator + expression_symbol: + test edx,edx + jz store_expression_symbol + mov ecx,edx + identify_operator: + cmp [ecx+ValueDefinition.type],VALTYPE_NATIVE_COMMAND + jne inspect_expression_symbol + test [ecx+ValueDefinition.flags],VAL_UNARY + setnz al + xor al,[operator_argument_expected] + jz store_expression_operator + mov ecx,[ecx+ValueDefinition.previous] + test ecx,ecx + jnz identify_operator + inspect_expression_symbol: + cmp [edx+ValueDefinition.type],VALTYPE_NATIVE_COMPARATOR + je terminate_expression + store_expression_symbol: + cmp [operator_argument_expected],0 + je terminate_expression + mov eax,EXPR_SYMBOL_VALUE + stosd + mov eax,ebx + stosd + mov eax,edx + stosd + expression_symbol_stored: + mov [expression_end],edi + and [operator_argument_expected],0 + and [current_constituent],0 + jmp get_expression_element + store_expression_number: + cmp [operator_argument_expected],0 + je terminate_expression + mov eax,EXPR_NUMBER + value_pointer_in_expression: + cmp edx,[value_workspace.memory_end] + jae value_pointer_in_expression_ok + mov ebx,edx + sub ebx,[value_workspace.memory_start] + jc value_pointer_in_expression_ok + mov edx,ebx + or eax,EXPRF_VALUE_IN_WORKSPACE + value_pointer_in_expression_ok: + stosd + mov eax,edx + stosd + jmp expression_symbol_stored + store_expression_string: + cmp [operator_argument_expected],0 + je terminate_expression + mov eax,EXPR_STRING + jmp value_pointer_in_expression + store_expression_float: + cmp [operator_argument_expected],0 + je terminate_expression + mov eax,EXPR_FLOAT + jmp value_pointer_in_expression + store_expression_operator: + mov edx,[ecx+ValueDefinition.value] + mov cl,[ecx+ValueDefinition.attribute] + mov ebx,[operator_stack_position] + cmp [operator_argument_expected],0 + jne push_operator + establish_operator_precedence: + cmp ebx,[operator_stack] + je push_operator + mov ch,cl + and cl,OPERATOR_PRECEDENCE_MASK + cmp cl,[ebx-8] + ja push_operator + jb store_pending_operator + test ch,OPERATOR_RIGHT_ASSOCIATIVE + jnz push_operator + store_pending_operator: + sub ebx,8 + mov eax,EXPR_OPERATOR + stosd + mov eax,[ebx+4] + stosd + jmp establish_operator_precedence + push_operator: + call reserve_operator_stack + mov [ebx],cl + mov [ebx+4],edx + add ebx,8 + mov [operator_stack_position],ebx + mov [expression_end],edi + or [operator_argument_expected],1 + and [current_constituent],0 + jmp get_expression_element + open_subexpression: + cmp [operator_argument_expected],0 + je terminate_expression + mov ebx,[operator_stack_position] + call reserve_operator_stack + mov eax,[operator_stack] + sub eax,[operator_stack_base] + mov [ebx],eax + add ebx,4 + mov [operator_stack],ebx + mov [operator_stack_position],ebx + and [current_constituent],0 + jmp get_expression_element + close_subexpression: + mov eax,[operator_stack] + cmp eax,[operator_stack_base] + je terminate_expression + cmp [operator_argument_expected],0 + je subexpression_closed + mov eax,EXPR_MISSING_ARGUMENT + stosd + subexpression_closed: + call store_remaining_operators + mov [expression_end],edi + sub ebx,4 + mov eax,[ebx] + add eax,[operator_stack_base] + mov [operator_stack],eax + mov [operator_stack_position],ebx + and [current_constituent],0 + jmp get_expression_element + expression_line_end: + and [current_constituent],0 + terminate_expression: + cmp [operator_argument_expected],0 + je close_expression + mov eax,[operator_stack_position] + cmp eax,[operator_stack] + jne expression_terminated_prematurely + mov eax,edi + sub eax,[expression_workspace.memory_start] + cmp eax,[expression_position] + je close_expression + expression_terminated_prematurely: + mov eax,EXPR_MISSING_ARGUMENT + stosd + close_expression: + call store_remaining_operators + xor ecx,ecx + cmp ebx,[operator_stack_base] + jne forcibly_close_subexpressions + expression_parsed: + xor eax,eax + stosd + retn + forcibly_close_subexpressions: + sub ebx,4 + mov eax,[ebx] + add eax,[operator_stack_base] + mov [operator_stack],eax + cmp [leave_opening_parentheses],0 + je internal_parenthesis + cmp eax,ebx + je external_parenthesis + internal_parenthesis: + mov eax,EXPR_MISSING_PARENTHESIS + inc ecx + rep stosd + mov [operator_stack_position],ebx + call store_remaining_operators + cmp ebx,[operator_stack_base] + jne forcibly_close_subexpressions + jmp expression_parsed + external_parenthesis: + inc ecx + cmp ebx,[operator_stack_base] + jne forcibly_close_subexpressions + jmp expression_parsed + reserve_operator_stack: + mov eax,[operator_stack_end] + sub eax,8 + cmp ebx,eax + jbe operator_stack_ready + push edx + mov eax,[operator_stack_base] + sub ebx,eax + sub [operator_stack],eax + mov ecx,[operator_stack_end] + sub ecx,eax + add ecx,8 + call grow_stack + add ebx,eax + add [operator_stack],eax + mov [operator_stack_base],eax + add eax,ecx + mov [operator_stack_end],eax + pop edx + operator_stack_ready: + retn + store_remaining_operators: + mov ebx,[operator_stack_position] + store_operator: + cmp ebx,[operator_stack] + je remaining_operators_stored + sub ebx,8 + mov eax,EXPR_OPERATOR + stosd + mov eax,[ebx+4] + stosd + jmp store_operator + remaining_operators_stored: + retn + +calculate_parsed_expression: +; in: +; esi - command sequence created by parse_expression +; edi - top of the stack in the calculation workspace +; out: +; cf set if expression had structural errors +; esi = pointer moved past the processed sequence +; edi - top of the updated stack +; when cf = 0: +; eax = non-zero if result is considered not yet known for resolving purposes + mov [expression_end],esi + mov eax,edi + sub eax,[calculation_workspace.memory_start] + mov [calculation_position],eax + cmp dword [esi],0 + je invalid_expression + calculation_loop: + mov esi,[expression_end] + lodsd + cmp al,EXPR_OPERATOR + je execute_operator + cmp al,EXPR_NUMBER + je push_literal + cmp al,EXPR_STRING + je push_literal + cmp al,EXPR_FLOAT + je push_literal + cmp al,EXPR_SYMBOL_VALUE + je push_symbol_value + cmp al,EXPR_SYMBOL + je push_symbol_current_value + cmp al,EXPR_AREA_ADDRESS + je push_area_address + test eax,eax + jnz structural_error + ; clc + retn + execute_operator: + lodsd + mov [expression_end],esi + jmp eax + push_literal: + mov ebx,eax + mov edx,calculation_workspace + mov ecx,2*sizeof.ExpressionTerm + call reserve_workspace + lodsd + test ebx,EXPRF_CALM_LITERAL + jnz calm_literal_value + test ebx,EXPRF_VALUE_IN_WORKSPACE + jnz valid_literal_value + test eax,eax + jnz valid_literal_value + jmp invalid_number_in_expression + calm_literal_value: + add eax,[calm_literals] + valid_literal_value: + mov [edi+ExpressionTerm.attributes],ebx + mov [edi+ExpressionTerm.value],eax + xor eax,eax + mov [edi+ExpressionTerm.metadata],eax + mov [edi+sizeof.ExpressionTerm+ExpressionTerm.attributes],eax + add edi,2*sizeof.ExpressionTerm + mov [expression_end],esi + jmp calculation_loop + push_symbol_current_value: + ; occurring only in CALM expressions, entire expression workspace should be free to use + lodsd + test eax,eax + jz invalid_identifier_in_expression + mov ebx,eax + mov [expression_end],esi + call use_available_value + jc undefined_symbol_in_expression + mov al,[edx+ValueDefinition.type] + cmp al,VALTYPE_SYMBOLIC + jne identify_value_to_push + push esi edi + call clear_line_embeddings + xor esi,esi + xor ecx,ecx + call embed_symbolic_value + mov edi,[expression_workspace.memory_start] + and [leave_opening_parentheses],0 + call parse_expression + pop edi + call get_constituent_value + jnc invalid_subexpression + mov esi,[expression_workspace.memory_start] + push [calculation_position] + call calculate_parsed_expression + pop [calculation_position] + jc invalid_subexpression + pop [expression_end] + test eax,eax + jnz unknown_result + jmp calculation_loop + invalid_subexpression: + pop [expression_end] + mov edx,_invalid_expression + call register_error + jmp unknown_result + push_symbol_value: + lodsd + test eax,eax + jz invalid_identifier_in_expression + mov ebx,eax + lodsd + mov edx,eax + call mark_symbol_as_used + mov [expression_end],esi + test edx,edx + jz undefined_symbol_in_expression + mov al,[edx+ValueDefinition.type] + identify_value_to_push: + cmp al,VALTYPE_NUMERIC + je push_numeric_symbol + cmp al,VALTYPE_ELEMENT + je push_element + cmp al,VALTYPE_AREA + je push_element + cmp al,VALTYPE_STRING + je push_string + cmp al,VALTYPE_FLOAT + je push_float + cmp al,VALTYPE_PLAIN + je push_plain_symbol + cmp al,VALTYPE_NATIVE_FUNCTION + jne invalid_symbol_in_expression + jmp [edx+ValueDefinition.value] + push_numeric_symbol: + xor esi,esi + test [edx+ValueDefinition.flags],VAL_SHIFTABLE + jz predicted_shift_ok + mov eax,[edx+ValueDefinition.pass] + cmp eax,[current_pass] + je predicted_shift_ok + mov esi,[predicted_shift] + predicted_shift_ok: + mov ebx,[edx+ValueDefinition.value] + mov eax,ebx + add eax,[edx+ValueDefinition.value_length] + push eax edi + mov edx,calculation_workspace + mov ecx,2*sizeof.ExpressionTerm + call reserve_workspace + mov [edi+ExpressionTerm.attributes],EXPR_NUMBER + mov [edi+ExpressionTerm.value],ebx + test esi,esi + jz push_numeric_terms + or [next_pass_needed],1 + mov ecx,temporary_value + mov dword [ecx],4 + mov dword [ecx+4],esi + lea esi,[edi+sizeof.ExpressionTerm] + mov [esi+ExpressionTerm.attributes],EXPR_NUMBER + mov [esi+ExpressionTerm.value],ecx + push ebx + call add_term_values + pop ebx + push_numeric_terms: + add edi,sizeof.ExpressionTerm + mov edx,calculation_workspace + mov ecx,sizeof.ExpressionTerm + call reserve_workspace + mov eax,[ebx] + lea ebx,[ebx+4+eax] + mov eax,[ebx] + test eax,eax + jz numeric_terms_pushed + mov eax,[ebx] + mov [edi+ExpressionTerm.metadata],eax + add ebx,4 + mov [edi+ExpressionTerm.attributes],EXPR_NUMBER + mov [edi+ExpressionTerm.value],ebx + jmp push_numeric_terms + numeric_terms_pushed: + add ebx,4 + pop edx ecx + cmp ebx,ecx + jne symbol_metadata_ok + xor ebx,ebx + symbol_metadata_ok: + mov [edx+ExpressionTerm.metadata],ebx + xor eax,eax + mov [edi+ExpressionTerm.attributes],eax + add edi,sizeof.ExpressionTerm + jmp calculation_loop + push_plain_symbol: + mov ebx,[edx+ValueDefinition.value] + push_plain_value: + mov edx,calculation_workspace + mov ecx,2*sizeof.ExpressionTerm + call reserve_workspace + mov esi,edi + mov edx,value_workspace + mov edi,[edx+Workspace.memory_start] + add edi,[value_position] + mov ecx,4+4 + call reserve_workspace + xor eax,eax + test ebx,ebx + jz number_length_ok + bsr eax,ebx + inc al + shr al,3 + inc al + number_length_ok: + stosd + mov [edi],ebx + add eax,edi + sub eax,[value_workspace.memory_start] + xchg eax,[value_position] + mov edi,esi + mov [edi+ExpressionTerm.attributes],EXPR_NUMBER + EXPRF_VALUE_IN_WORKSPACE + mov [edi+ExpressionTerm.value],eax + xor eax,eax + mov [edi+ExpressionTerm.metadata],eax + mov [edi+sizeof.ExpressionTerm+ExpressionTerm.attributes],eax + add edi,2*sizeof.ExpressionTerm + jmp calculation_loop + push_string: + mov al,EXPR_STRING + push_value: + mov [term_type],al + mov ebx,[edx+ValueDefinition.value] + mov edx,calculation_workspace + mov ecx,2*sizeof.ExpressionTerm + call reserve_workspace + mov [edi+ExpressionTerm.value],ebx + xor eax,eax + mov [edi+ExpressionTerm.metadata],eax + mov [edi+sizeof.ExpressionTerm+ExpressionTerm.attributes],eax + mov al,[term_type] + mov [edi+ExpressionTerm.attributes],eax + add edi,2*sizeof.ExpressionTerm + jmp calculation_loop + push_float: + mov al,EXPR_FLOAT + jmp push_value + push_element: + mov edx,calculation_workspace + mov ecx,3*sizeof.ExpressionTerm + call reserve_workspace + mov eax,EXPR_NUMBER + mov [edi+ExpressionTerm.attributes],eax + mov [edi+sizeof.ExpressionTerm+ExpressionTerm.attributes],eax + mov [edi+ExpressionTerm.value],zero_value + mov [edi+sizeof.ExpressionTerm+ExpressionTerm.value],singular_value + mov [edi+sizeof.ExpressionTerm+ExpressionTerm.metadata],ebx + xor eax,eax + mov [edi+ExpressionTerm.metadata],eax + mov [edi+2*sizeof.ExpressionTerm+ExpressionTerm.attributes],eax + add edi,3*sizeof.ExpressionTerm + jmp calculation_loop + push_area_address: + lodsd + mov [expression_end],esi + jmp area_address_value + base_address_value: + mov eax,[current_area] + area_address_value: + mov ebx,[eax+ValueDefinition.value] + add ebx,sizeof.AreaHeader + push_numeric_value: + mov edx,calculation_workspace + mov ecx,sizeof.ExpressionTerm + call reserve_workspace + mov [edi+ExpressionTerm.attributes],EXPR_NUMBER + mov [edi+ExpressionTerm.value],ebx + and [edi+ExpressionTerm.metadata],0 + jmp push_variable_terms + truncated_address_value: + xor esi,esi + jmp push_current_address + current_address_value: + or esi,-1 + push_current_address: + mov edx,calculation_workspace + mov ecx,2*sizeof.ExpressionTerm + call reserve_workspace + mov eax,[current_area] + mov ebx,[eax+ValueDefinition.value] + mov ecx,[eax+ValueDefinition.value_length] + sub ecx,[ebx+AreaHeader.base_address_length] + sub ecx,sizeof.AreaHeader + and esi,[ebx+AreaHeader.uninitialized_length] + add ecx,esi + add ebx,sizeof.AreaHeader + mov esi,temporary_value + mov dword [esi],4 + mov [esi+4],ecx + mov [edi+ExpressionTerm.attributes],EXPR_NUMBER + mov [edi+ExpressionTerm.value],esi + and [edi+ExpressionTerm.metadata],0 + lea esi,[edi+sizeof.ExpressionTerm] + mov [esi+ExpressionTerm.attributes],EXPR_NUMBER + mov [esi+ExpressionTerm.value],ebx + call add_term_values + mov ebx,[esi+ExpressionTerm.value] + push_variable_terms: + mov eax,[ebx] + lea ebx,[ebx+4+eax] + convert_variable_term: + add edi,sizeof.ExpressionTerm + mov edx,calculation_workspace + mov ecx,sizeof.ExpressionTerm + call reserve_workspace + mov eax,[ebx] + test eax,eax + jz variable_terms_converted + mov [edi+ExpressionTerm.metadata],eax + add ebx,4 + mov [edi+ExpressionTerm.attributes],EXPR_NUMBER + mov [edi+ExpressionTerm.value],ebx + mov eax,[ebx] + lea ebx,[ebx+4+eax] + jmp convert_variable_term + variable_terms_converted: + mov [edi+ExpressionTerm.attributes],eax + add edi,sizeof.ExpressionTerm + jmp calculation_loop + truncated_position_value: + call prepare_to_push_computed_value + call get_output_length + jmp push_output_position + prepare_to_push_computed_value: + mov edx,calculation_workspace + mov ecx,2*sizeof.ExpressionTerm + call reserve_workspace + mov esi,edi + mov edx,value_workspace + mov edi,[edx+Workspace.memory_start] + add edi,[value_position] + mov ecx,4+8 + call reserve_workspace + retn + current_position_value: + call prepare_to_push_computed_value + call get_output_position + push_output_position: + mov edi,[value_workspace.memory_start] + add edi,[value_position] + push_computed_value: + mov ecx,eax + or ecx,edx + jz computed_value_length_ok + bsr ecx,edx + jnz long_computed_value + bsr ecx,eax + inc cl + shr cl,3 + inc cl + jmp computed_value_length_ok + long_computed_value: + inc cl + shr cl,3 + add cl,1+4 + computed_value_length_ok: + mov [edi],ecx + add edi,4 + stosd + mov eax,edx + stosd + add ecx,edi + sub ecx,[value_workspace.memory_start] + xchg ecx,[value_position] + mov edi,esi + mov [edi+ExpressionTerm.attributes],EXPR_NUMBER + EXPRF_VALUE_IN_WORKSPACE + mov [edi+ExpressionTerm.value],ecx + xor eax,eax + mov [edi+ExpressionTerm.metadata],eax + mov [edi+sizeof.ExpressionTerm+ExpressionTerm.attributes],eax + add edi,2*sizeof.ExpressionTerm + jmp calculation_loop + current_time_value: + call prepare_to_push_computed_value + call get_timestamp + jmp push_computed_value + current_line_number_value: + call prepare_to_push_computed_value + call get_file_source_entry + mov eax,[ebx+SourceEntry.line_number] + xor edx,edx + jmp push_computed_value + current_file_name_value: + mov edx,calculation_workspace + mov ecx,2*sizeof.ExpressionTerm + call reserve_workspace + push edi + call get_file_source_entry + jmp push_file_name_value + main_file_name_value: + mov edx,calculation_workspace + mov ecx,2*sizeof.ExpressionTerm + call reserve_workspace + push edi + mov ebx,[source_context] + lea ebx,[ebx+sizeof.SourceContext] + push_file_name_value: + mov esi,[ebx+SourceEntry.name] + xor ecx,ecx + cmp [ebx+SourceEntry.type],SOURCE_FILE + jne file_name_length_ok + mov ecx,[ebx+SourceEntry.name_length] + test ecx,ecx + jnz file_name_length_ok + mov edi,esi + xor al,al + dec ecx + repne scasb + add ecx,2 + neg ecx + file_name_length_ok: + mov ebx,ecx + add ecx,4 + mov edx,value_workspace + mov edi,[edx+Workspace.memory_start] + add edi,[value_position] + call reserve_workspace + mov eax,ebx + stosd + mov ecx,ebx + shr ecx,2 + rep movsd + mov ecx,ebx + and ecx,11b + rep movsb + mov eax,edi + sub eax,[value_workspace.memory_start] + xchg eax,[value_position] + pop edi + mov [edi+ExpressionTerm.attributes],EXPR_STRING + EXPRF_VALUE_IN_WORKSPACE + mov [edi+ExpressionTerm.value],eax + xor eax,eax + mov [edi+ExpressionTerm.metadata],eax + mov [edi+sizeof.ExpressionTerm+ExpressionTerm.attributes],eax + add edi,2*sizeof.ExpressionTerm + jmp calculation_loop + structural_error: + cmp eax,EXPR_MISSING_PARENTHESIS + je expression_missing_parenthesis + invalid_expression: + mov edx,_invalid_expression + call register_error + stc + retn + expression_missing_parenthesis: + mov edx,_missing_closing_parenthesis + call register_error + stc + retn + undefined_symbol_in_expression: + cmp [next_pass_needed],0 + jne unknown_result + mov edx,_undefined_symbol + jmp error_causing_unknown_result + invalid_identifier_in_expression: + mov edx,_invalid_identifier + jmp error_causing_unknown_result + invalid_number_in_expression: + mov edx,_invalid_number + jmp error_causing_unknown_result + invalid_symbol_in_expression: + mov edx,_invalid_symbol_value + error_causing_unknown_result: + call register_error + unknown_result: + mov eax,edi + mov edi,[calculation_position] + add edi,[calculation_workspace.memory_start] + cmp edi,eax + jb unknown_result_terms_ready + mov edi,eax + mov edx,calculation_workspace + mov ecx,2*sizeof.ExpressionTerm + call reserve_workspace + xor eax,eax + mov [edi+ExpressionTerm.metadata],eax + mov [edi+sizeof.ExpressionTerm+ExpressionTerm.attributes],eax + unknown_result_terms_ready: + mov [edi+ExpressionTerm.attributes],EXPR_NUMBER + mov [edi+ExpressionTerm.value],zero_value + add edi,2*sizeof.ExpressionTerm + mov esi,[expression_end] + skip_expression: + lodsd + cmp al,EXPR_OPERATOR + je skip_dword + cmp al,EXPR_NUMBER + je skip_dword + cmp al,EXPR_STRING + je skip_dword + cmp al,EXPR_FLOAT + je skip_dword + cmp al,EXPR_SYMBOL_VALUE + je skip_two_dwords + test eax,eax + jnz invalid_expression + inc eax + ; clc + retn + skip_two_dwords: + add esi,4 + skip_dword: + add esi,4 + jmp skip_expression + +pop_terms: +; in: +; edi = top of the stack +; out: +; edi = new top of the stack, at the same time a pointer to the first term of retrieved linear polynomial +; cf set when there were no more entries on the stack +; preserves: eax, ebx, ecx, edx, esi + cmp edi,[calculation_workspace.memory_start] + je nothing_to_pop + sub edi,sizeof.ExpressionTerm + find_first_term: + cmp edi,[calculation_workspace.memory_start] + je first_term_found + cmp [edi-sizeof.ExpressionTerm+ExpressionTerm.attributes],0 + je first_term_found + sub edi,sizeof.ExpressionTerm + jmp find_first_term + first_term_found: + clc + retn + nothing_to_pop: + and [edi+ExpressionTerm.metadata],0 + and [edi+sizeof.ExpressionTerm+ExpressionTerm.attributes],0 + mov [edi+ExpressionTerm.attributes],EXPR_NUMBER + mov [edi+ExpressionTerm.value],zero_value + stc + retn + +calculate_to_number: + call pop_terms + cmp byte [edi+ExpressionTerm.attributes],EXPR_FLOAT + je single_term_result + make_terms_numeric: + mov eax,[edi+ExpressionTerm.attributes] + test eax,eax + jz all_terms_numeric + call get_numeric_term_value + add edi,sizeof.ExpressionTerm + jmp make_terms_numeric + all_terms_numeric: + add edi,sizeof.ExpressionTerm + jmp calculation_loop + +calculate_to_string: + call pop_terms + mov al,EXPR_STRING + xchg al,byte [edi+ExpressionTerm.attributes] + cmp al,EXPR_FLOAT + jne single_term_result + mov edx,_invalid_value + call register_error + mov [edi+ExpressionTerm.value],zero_value + mov [edi+ExpressionTerm.attributes],EXPR_STRING + and [edi+sizeof.ExpressionTerm+ExpressionTerm.attributes],0 + add edi,2*sizeof.ExpressionTerm + jmp calculation_loop + +calculate_to_float: + call pop_terms + call get_float_term_value + jmp single_term_result + +extract_integer_part: + call pop_terms + call truncate_term_value + jmp single_term_result + +calculate_neg: + call pop_terms + cmp byte [edi+ExpressionTerm.attributes],EXPR_FLOAT + je negate_float_term + call negate_term_value + add edi,sizeof.ExpressionTerm + negate_element_terms: + cmp [edi+ExpressionTerm.attributes],0 + je all_terms_negated + cmp [edi+ExpressionTerm.metadata],0 + je negate_next_term + call negate_term_value + negate_next_term: + add edi,sizeof.ExpressionTerm + jmp negate_element_terms + all_terms_negated: + add edi,sizeof.ExpressionTerm + jmp calculation_loop + negate_float_term: + call prepare_altered_float_term + xor [ebx+FloatData.attributes],FLOAT_NEGATIVE + single_term_result: + add edi,sizeof.ExpressionTerm + mov eax,edi + call check_for_excess_terms + jnc single_term_result_ok + mov edx,_misused_variable_term + call register_error + single_term_result_ok: + and [edi+ExpressionTerm.attributes],0 + add edi,sizeof.ExpressionTerm + jmp calculation_loop + check_for_excess_terms: + cmp [eax+ExpressionTerm.attributes],0 + je no_excess_terms + cmp [eax+ExpressionTerm.metadata],0 + jne excess_terms + add eax,sizeof.ExpressionTerm + jne check_for_excess_terms + no_excess_terms: + clc + retn + excess_terms: + stc + retn + prepare_altered_float_term: + mov ebx,edi + mov edx,value_workspace + mov edi,[edx+Workspace.memory_start] + add edi,[value_position] + mov ecx,sizeof.FloatData + call reserve_workspace + xchg edi,ebx + call get_term_value + xchg edi,ebx + mov esi,edx + assert sizeof.FloatData and 11b = 0 + mov ecx,sizeof.FloatData shr 2 + rep movsd + mov eax,edi + sub eax,[value_workspace.memory_start] + xchg eax,[value_position] + sub edi,sizeof.FloatData + xchg edi,ebx + mov [edi+ExpressionTerm.value],eax + or [edi+ExpressionTerm.attributes],EXPRF_VALUE_IN_WORKSPACE + retn + +calculate_add: + call pop_terms + mov esi,edi + call pop_terms + cmp byte [esi+ExpressionTerm.attributes],EXPR_FLOAT + je add_float_terms + cmp byte [edi+ExpressionTerm.attributes],EXPR_FLOAT + je add_float_terms + cmp [edi+ExpressionTerm.metadata],0 + jne add_constant_terms + mov eax,[esi+ExpressionTerm.metadata] + mov [edi+ExpressionTerm.metadata],eax + add_constant_terms: + call add_term_values + add esi,sizeof.ExpressionTerm + add_variable_terms: + cmp [esi+ExpressionTerm.attributes],0 + je all_terms_added + mov eax,[esi+ExpressionTerm.metadata] + test eax,eax + jz add_next_term + push edi + find_element_to_add_to: + add edi,sizeof.ExpressionTerm + cmp [edi+ExpressionTerm.attributes],0 + je attach_new_element + cmp eax,[edi+ExpressionTerm.metadata] + jne find_element_to_add_to + push esi + call add_term_values + jnz variable_term_added + and [edi+ExpressionTerm.metadata],0 + variable_term_added: + pop esi edi + add_next_term: + add esi,sizeof.ExpressionTerm + jmp add_variable_terms + attach_new_element: + assert sizeof.ExpressionTerm and 11b = 0 + mov ecx,sizeof.ExpressionTerm shr 2 + rep movsd + and [edi+ExpressionTerm.attributes],0 + pop edi + jmp add_variable_terms + all_terms_added: + add edi,sizeof.ExpressionTerm + cmp [edi+ExpressionTerm.attributes],0 + jne all_terms_added + add edi,sizeof.ExpressionTerm + jmp calculation_loop + add_float_terms: + mov eax,add_floats + operation_on_float_terms: + push esi edi eax + mov ebx,edi + mov edx,value_workspace + mov edi,[edx+Workspace.memory_start] + add edi,[value_position] + mov ecx,sizeof.FloatData + call reserve_workspace + lea ecx,[edi+sizeof.FloatData] + sub ecx,[edx+Workspace.memory_start] + mov [value_position],ecx + xchg edi,ebx + call get_float_term_value + xchg edi,esi + call get_float_term_value + mov edi,esi + mov esi,edx + call get_term_value + mov edi,ebx + mov ebx,edx + pop eax + call eax + mov ebx,edi + sub ebx,[value_workspace.memory_start] + test [edi+FloatData.attributes],FLOAT_INFINITE or FLOAT_INDETERMINATE or FLOAT_UNDERFLOW + jz operation_on_float_terms_ok + mov edx,_indeterminate_result + call register_error + xor eax,eax + mov [edi+FloatData.attributes],eax + add edi,FloatData.mantissa + mov ecx,MANTISSA_SEGMENTS + rep stosd + operation_on_float_terms_ok: + pop edi esi + mov [edi+ExpressionTerm.value],ebx + mov [edi+ExpressionTerm.attributes],EXPR_FLOAT + EXPRF_VALUE_IN_WORKSPACE + and [edi+ExpressionTerm.metadata],0 + finish_calculation_on_single_terms: + add edi,sizeof.ExpressionTerm + mov eax,edi + call check_for_excess_terms + jc single_terms_violation + lea eax,[esi+sizeof.ExpressionTerm] + call check_for_excess_terms + jnc single_terms_ok + single_terms_violation: + mov edx,_misused_variable_term + call register_error + single_terms_ok: + and [edi+ExpressionTerm.attributes],0 + add edi,sizeof.ExpressionTerm + jmp calculation_loop + +calculate_sub: + call pop_terms + mov esi,edi + call pop_terms + cmp byte [esi+ExpressionTerm.attributes],EXPR_FLOAT + je subtract_float_terms + cmp byte [edi+ExpressionTerm.attributes],EXPR_FLOAT + je subtract_float_terms + cmp [edi+ExpressionTerm.metadata],0 + jne subtract_constant_terms + mov eax,[esi+ExpressionTerm.metadata] + mov [edi+ExpressionTerm.metadata],eax + subtract_constant_terms: + call subtract_term_values + add esi,sizeof.ExpressionTerm + subtract_variable_terms: + cmp [esi+ExpressionTerm.attributes],0 + je all_terms_subtracted + mov eax,[esi+ExpressionTerm.metadata] + test eax,eax + jz subtract_next_term + push edi + find_element_to_subtract_from: + add edi,sizeof.ExpressionTerm + cmp [edi+ExpressionTerm.attributes],0 + je attach_negated_element + cmp eax,[edi+ExpressionTerm.metadata] + jne find_element_to_subtract_from + push esi + call subtract_term_values + jnz variable_term_subtracted + and [edi+ExpressionTerm.metadata],0 + variable_term_subtracted: + pop esi edi + subtract_next_term: + add esi,sizeof.ExpressionTerm + jmp subtract_variable_terms + attach_negated_element: + assert sizeof.ExpressionTerm and 11b = 0 + mov ecx,sizeof.ExpressionTerm shr 2 + rep movsd + sub edi,sizeof.ExpressionTerm + call negate_term_value + add edi,sizeof.ExpressionTerm + and [edi+ExpressionTerm.attributes],0 + pop edi + jmp subtract_variable_terms + all_terms_subtracted: + add edi,sizeof.ExpressionTerm + cmp [edi+ExpressionTerm.attributes],0 + jne all_terms_subtracted + add edi,sizeof.ExpressionTerm + jmp calculation_loop + subtract_float_terms: + mov eax,subtract_floats + jmp operation_on_float_terms + +calculate_mul: + call pop_terms + mov esi,edi + call pop_terms + cmp byte [esi+ExpressionTerm.attributes],EXPR_FLOAT + je multiply_float_terms + cmp byte [edi+ExpressionTerm.attributes],EXPR_FLOAT + je multiply_float_terms + cmp [esi+sizeof.ExpressionTerm+ExpressionTerm.attributes],0 + jne multiply_by_destination + multiply_terms: + call multiply_term_values + jnz multiply_next_term + and [edi+ExpressionTerm.metadata],0 + multiply_next_term: + add edi,sizeof.ExpressionTerm + cmp [edi+ExpressionTerm.attributes],0 + je terms_multiplied + jmp multiply_terms + terms_multiplied: + add edi,sizeof.ExpressionTerm + jmp calculation_loop + multiply_by_destination: + and [esi+ExpressionTerm.metadata],0 + cmp [edi+sizeof.ExpressionTerm+ExpressionTerm.attributes],0 + je duplicate_destination_constant_term + mov edx,_nonlinear_polynomial + call register_error + duplicate_destination_constant_term: + mov eax,[edi+ExpressionTerm.attributes] + mov edx,[edi+ExpressionTerm.value] + mov [edi+sizeof.ExpressionTerm+ExpressionTerm.attributes],eax + mov [edi+sizeof.ExpressionTerm+ExpressionTerm.value],edx + multiply_source_terms: + mov eax,[esi+ExpressionTerm.metadata] + mov [edi+ExpressionTerm.metadata],eax + call multiply_term_values + jnz multiply_next_source_term + and [edi+ExpressionTerm.metadata],0 + multiply_next_source_term: + add esi,sizeof.ExpressionTerm + add edi,sizeof.ExpressionTerm + cmp [esi+ExpressionTerm.attributes],0 + je source_terms_multiplied + cmp [esi+sizeof.ExpressionTerm+ExpressionTerm.attributes],0 + je multiply_source_terms + jmp duplicate_destination_constant_term + source_terms_multiplied: + and [edi+ExpressionTerm.attributes],0 + jmp terms_multiplied + multiply_float_terms: + mov eax,multiply_floats + jmp operation_on_float_terms + +calculate_div: + call pop_terms + mov eax,edi + call check_for_excess_terms + jnc divisor_ok + mov edx,_misused_variable_term + call register_error + divisor_ok: + mov esi,edi + call pop_terms + cmp byte [esi+ExpressionTerm.attributes],EXPR_FLOAT + je divide_float_terms + cmp byte [edi+ExpressionTerm.attributes],EXPR_FLOAT + je divide_float_terms + and [edi+ExpressionTerm.metadata],0 + mov eax,[esi+ExpressionTerm.attributes] + mov edx,[esi+ExpressionTerm.value] + mov [esi+sizeof.ExpressionTerm+ExpressionTerm.attributes],eax + mov [esi+sizeof.ExpressionTerm+ExpressionTerm.value],edx + call divide_term_values + divide_variable_term: + add edi,sizeof.ExpressionTerm + cmp [edi+ExpressionTerm.attributes],0 + je division_done + cmp [edi+ExpressionTerm.metadata],0 + je divide_variable_term + mov eax,[esi+sizeof.ExpressionTerm+ExpressionTerm.attributes] + mov edx,[esi+sizeof.ExpressionTerm+ExpressionTerm.value] + mov [esi+ExpressionTerm.attributes],eax + mov [esi+ExpressionTerm.value],edx + call divide_term_values + jz divide_variable_term + mov edx,_subdivided_variable_term + call register_error + jmp divide_variable_term + division_done: + add edi,sizeof.ExpressionTerm + jmp calculation_loop + divide_float_terms: + mov eax,divide_floats + jmp operation_on_float_terms + +calculate_mod: + call pop_terms + mov esi,edi + call pop_terms + call divide_term_values + and [esi+ExpressionTerm.metadata],0 + assert sizeof.ExpressionTerm and 11b = 0 + mov ecx,sizeof.ExpressionTerm shr 2 + rep movsd + mov eax,edi + call check_for_excess_terms + jc mod_variable_term + mov eax,esi + call check_for_excess_terms + jnc mod_terms_ok + mod_variable_term: + mov edx,_misused_variable_term + call register_error + mod_terms_ok: + and [edi+ExpressionTerm.attributes],0 + add edi,sizeof.ExpressionTerm + jmp calculation_loop + +calculate_not: + call pop_terms + call invert_term_value_bits + jmp single_term_result + +calculate_xor: + call pop_terms + mov esi,edi + call pop_terms + call bitwise_add_term_values + finish_bitwise_calculation: + cmp [edi+ExpressionTerm.metadata],0 + jne finish_calculation_on_single_terms + mov eax,[esi+ExpressionTerm.metadata] + mov [edi+ExpressionTerm.metadata],eax + jmp finish_calculation_on_single_terms + +calculate_and: + call pop_terms + mov esi,edi + call pop_terms + call bitwise_multiply_term_values + jmp finish_bitwise_calculation + +calculate_or: + call pop_terms + mov esi,edi + call pop_terms + call bitwise_inclusive_or_of_term_values + jmp finish_bitwise_calculation + +calculate_shl: + call pop_terms + call get_numeric_term_value + mov ecx,5 + call fit_value + js negative_shift_left + jc huge_shift_left + shift_left: + mov esi,edi + check_shift_left_argument: + add edi,sizeof.ExpressionTerm + cmp [edi+ExpressionTerm.attributes],0 + je shift_left_argument_ok + cmp [edi+ExpressionTerm.metadata],0 + je check_shift_left_argument + mov edx,_misused_variable_term + call register_error + shift_left_argument_ok: + mov edi,esi + call pop_terms + and [edi+ExpressionTerm.metadata],0 + cmp byte [edi+ExpressionTerm.attributes],EXPR_FLOAT + je shift_floating_point_left + shift_term_left: + mov eax,[esi] + mov dl,[esi+4] + call shift_term_value_left + shift_variable_term_left: + add edi,sizeof.ExpressionTerm + cmp [edi+ExpressionTerm.attributes],0 + je shift_done + cmp [edi+ExpressionTerm.metadata],0 + je shift_variable_term_left + jmp shift_term_left + shift_done: + add edi,sizeof.ExpressionTerm + jmp calculation_loop + negative_shift_left: + jc huge_shift_right + not byte [edi+4] + neg dword [edi] + jc shift_right + inc byte [edi+4] + jnz shift_right + huge_shift_right: + or eax,-1 + mov [edi],eax + mov [edi+4],al + jmp shift_right + shift_floating_point_left: + push esi + call prepare_altered_float_term + pop esi + xchg esi,ebx + call get_float_unnormalization + jc single_term_result + mov eax,[esi+FloatData.exponent] + cdq + add eax,[ebx] + movzx ebx,byte [ebx+4] + adc ebx,edx + mov [esi+FloatData.exponent],eax + cdq + cmp ebx,edx + jne floating_point_over_boundary + test ecx,ecx + jz single_term_result + push edi + mov edi,esi + and [mantissa_tail],0 + call normalize_float + pop edi + jmp single_term_result + floating_point_over_boundary: + jecxz floating_point_out_of_range + test ebx,ebx + jnz floating_point_out_of_range + mov ecx,7FFFFFFFh + sub eax,ecx + mov [esi+FloatData.exponent],ecx + push eax edi + mov edi,esi + and [mantissa_tail],0 + call normalize_float + pop edi ecx + mov eax,[esi+FloatData.exponent] + cdq + add eax,ecx + js floating_point_out_of_range + adc edx,0 + jnz floating_point_out_of_range + mov [esi+FloatData.exponent],eax + jmp single_term_result + floating_point_out_of_range: + mov edx,_value_out_of_range + call register_error + jmp single_term_result + +calculate_shr: + call pop_terms + call get_numeric_term_value + mov ecx,5 + call fit_value + js negative_shift_right + jc huge_shift_right + shift_right: + mov esi,edi + check_shift_right_argument: + add edi,sizeof.ExpressionTerm + cmp [edi+ExpressionTerm.attributes],0 + je shift_right_argument_ok + cmp [edi+ExpressionTerm.metadata],0 + je check_shift_right_argument + mov edx,_misused_variable_term + call register_error + shift_right_argument_ok: + mov edi,esi + call pop_terms + and [edi+ExpressionTerm.metadata],0 + cmp byte [edi+ExpressionTerm.attributes],EXPR_FLOAT + je shift_floating_point_right + shift_term_right: + mov eax,[esi] + mov dl,[esi+4] + call shift_term_value_right + shift_variable_term_right: + add edi,sizeof.ExpressionTerm + cmp [edi+ExpressionTerm.attributes],0 + je shift_done + cmp [edi+ExpressionTerm.metadata],0 + je shift_variable_term_right + call get_term_value + mov ecx,[esi] + mov bl,cl + mov al,[esi+4] + shrd ecx,eax,3 + shr al,3 + jnz not_exact_shift + cmp ecx,[edx] + jae not_exact_shift + xchg edi,edx + add edi,4 + xor al,al + repe scasb + xchg edi,edx + jne not_exact_shift + mov cl,bl + and cl,8-1 + jz shift_term_right + mov al,1 + shl al,cl + dec al + test [edx],al + jz shift_term_right + not_exact_shift: + mov edx,_subdivided_variable_term + call register_error + jmp shift_variable_term_right + negative_shift_right: + jc huge_shift_left + not byte [edi+4] + neg dword [edi] + jc shift_left + inc byte [edi+4] + jnz shift_left + huge_shift_left: + or eax,-1 + mov [edi],eax + mov [edi+4],al + jmp shift_left + shift_floating_point_right: + push esi + call prepare_altered_float_term + pop esi + xchg esi,ebx + call get_float_unnormalization + jc single_term_result + mov eax,[esi+FloatData.exponent] + cdq + sub eax,[ebx] + movzx ecx,byte [ebx+4] + sbb edx,ecx + mov ecx,edx + mov [esi+FloatData.exponent],eax + cdq + cmp ecx,edx + je single_term_result + cmp ecx,-1 + jne floating_point_out_of_range + mov [esi+FloatData.exponent],-80000000h + mov ecx,eax + neg ecx + push edi + mov edi,esi + and [mantissa_tail],0 + call unnormalize_float + pop edi + mov [esi+FloatData.exponent],-80000000h + call get_float_unnormalization + jc floating_point_out_of_range + jmp single_term_result + +calculate_bsf: + call pop_terms + mov esi,edi + mov edi,[value_position] + add edi,[value_workspace.memory_start] + mov ecx,10 + mov edx,value_workspace + call reserve_workspace + call find_first_set_bit_in_term_value + jz bit_scan_result_undefined + movzx edx,dl + bit_scan_result_ready: + mov ebx,edi + and dword [edi],0 + add edi,4 + stosd + mov ax,dx + stosw + mov ecx,6 + optimize_result_value: + cmp [edi-1],dh + jne result_value_ready + movsx eax,byte [edi-2] + cmp ah,dh + jne result_value_ready + dec edi + loop optimize_result_value + result_value_ready: + mov [ebx],ecx + sub edi,[value_workspace.memory_start] + mov [value_position],edi + sub ebx,[value_workspace.memory_start] + mov edi,esi + mov [edi+ExpressionTerm.value],ebx + mov [edi+ExpressionTerm.attributes],EXPR_NUMBER + EXPRF_VALUE_IN_WORKSPACE + and [edi+ExpressionTerm.metadata],0 + jmp single_term_result + +calculate_bsr: + call pop_terms + mov esi,edi + mov edi,[value_position] + add edi,[value_workspace.memory_start] + mov ecx,10 + mov edx,value_workspace + call reserve_workspace + cmp byte [esi+ExpressionTerm.attributes],EXPR_FLOAT + je extract_float_exponent + call find_last_set_bit_in_term_value + jc bit_scan_result_undefined + movzx edx,dl + jmp bit_scan_result_ready + bit_scan_result_undefined: + mov edi,esi + mov edx,_indeterminate_result + call register_error + mov [edi+ExpressionTerm.value],zero_value + mov [edi+ExpressionTerm.attributes],EXPR_NUMBER + and [edi+ExpressionTerm.metadata],0 + jmp single_term_result + extract_float_exponent: + mov edi,esi + mov [destination_term],edi + call get_term_value + mov esi,edx + call get_float_unnormalization + jc exponent_indeterminate + mov eax,[esi+FloatData.exponent] + cdq + sub eax,ecx + sbb edx,0 + exponent_extracted: + mov edi,[value_position] + add edi,[value_workspace.memory_start] + mov esi,[destination_term] + jmp bit_scan_result_ready + exponent_indeterminate: + mov edx,_indeterminate_result + call register_error + xor eax,eax + xor edx,edx + jmp exponent_extracted + +calculate_bswap: + call pop_terms + call get_numeric_term_value + mov ecx,4 + call fit_value + js bswap_argument_out_of_range + jc bswap_argument_out_of_range + mov esi,edi + mov eax,edi + call check_for_excess_terms + jnc bswap_argument_ok + mov edx,_misused_variable_term + call register_error + bswap_argument_ok: + call pop_terms + and [edi+ExpressionTerm.metadata],0 + mov ebx,[esi] + call reverse_term_value_bytes + jmp single_term_result + bswap_argument_out_of_range: + mov edx,_value_out_of_range + call register_error + call pop_terms + jmp single_term_result + +count_bytes: + call pop_terms + call get_string_term_value + mov esi,edi + add esi,sizeof.ExpressionTerm + mov eax,esi + call check_for_excess_terms + mov ebx,[edx] + jnc push_plain_value + mov edx,_misused_variable_term + call register_error + jmp push_plain_value + +count_elements: + call pop_terms + mov edx,[edi+ExpressionTerm.metadata] + xor ebx,ebx + mov edx,edi + count_variable_terms: + add edx,sizeof.ExpressionTerm + cmp [edx+ExpressionTerm.attributes],0 + je push_plain_value + cmp [edx+ExpressionTerm.metadata],0 + je count_variable_terms + inc ebx + jmp count_variable_terms + +extract_element_reverse: + xor eax,eax +extract_element: + call get_indexed_term + jc replace_with_zero + jecxz replace_with_constant_unit + mov ebx,[ebx+ExpressionTerm.metadata] + jmp push_element + get_indexed_term: + call pop_terms + mov esi,edi + call pop_terms + mov ebx,edi + test eax,eax + jnz process_term_indexing + xchg ebx,esi + process_term_indexing: + mov edx,esi + check_term_index: + add edx,sizeof.ExpressionTerm + cmp [edx+ExpressionTerm.attributes],0 + je term_index_ok + cmp [edx+ExpressionTerm.metadata],0 + je check_term_index + mov edx,_misused_variable_term + call register_error + term_index_ok: + push ebx + xchg edi,esi + call get_numeric_term_value + mov ecx,4 + call fit_value + xchg esi,edi + pop ebx + js no_such_term + jc no_such_term + xor ecx,ecx + find_indexed_term: + cmp ecx,[esi] + je term_found + find_next_term: + add ebx,sizeof.ExpressionTerm + cmp [ebx+ExpressionTerm.attributes],0 + je no_such_term + cmp [ebx+ExpressionTerm.metadata],0 + je find_next_term + inc ecx + jmp find_indexed_term + term_found: + clc + retn + no_such_term: + stc + retn + replace_with_area_metadata: + mov edx,area_metadata + mov eax,EXPR_STRING + jmp replace_with_simple_constant + replace_with_constant_unit: + mov edx,singular_value + jmp replace_with_simple_number + replace_with_zero: + mov edx,zero_value + replace_with_simple_number: + mov eax,EXPR_NUMBER + replace_with_simple_constant: + mov [edi+ExpressionTerm.attributes],eax + mov [edi+ExpressionTerm.value],edx + xor eax,eax + mov [edi+ExpressionTerm.metadata],eax + mov [edi+sizeof.ExpressionTerm+ExpressionTerm.attributes],eax + add edi,2*sizeof.ExpressionTerm + jmp calculation_loop + +extract_scale_reverse: + xor eax,eax +extract_scale: + call get_indexed_term + jc replace_with_zero + mov eax,[ebx+ExpressionTerm.attributes] + mov edx,[ebx+ExpressionTerm.value] + mov [edi+ExpressionTerm.attributes],eax + mov [edi+ExpressionTerm.value],edx + and [edi+sizeof.ExpressionTerm+ExpressionTerm.attributes],0 + add edi,2*sizeof.ExpressionTerm + jmp calculation_loop + +extract_metadata_reverse: + xor eax,eax +extract_metadata: + call get_indexed_term + jc replace_with_zero + mov ebx,[ebx+ExpressionTerm.metadata] + test ebx,ebx + jz replace_with_zero + test ecx,ecx + jz push_numeric_value + mov edx,[ebx+SymbolTree_Leaf.definition] + test edx,edx + jz replace_with_zero + cmp [edx+ValueDefinition.type],VALTYPE_AREA + je replace_with_area_metadata + cmp [edx+ValueDefinition.type],VALTYPE_ELEMENT + jne replace_with_zero + mov ecx,[edx+ValueDefinition.value_length] + jecxz replace_with_zero + jmp push_numeric_symbol + +extract_size: + call pop_terms + mov ebx,[edi+ExpressionTerm.metadata] + test ebx,ebx + jz replace_with_zero + jmp push_numeric_value + +get_term_value: +; in: edi - ExpressionTerm +; out: edx - 32-bit length followed by data +; preserves: eax, ebx, ecx, esi, edi + mov edx,[edi+ExpressionTerm.value] + test [edi+ExpressionTerm.attributes],EXPRF_VALUE_IN_WORKSPACE + jz value_pointer_ready + add edx,[value_workspace.memory_start] + value_pointer_ready: + retn + +get_numeric_term_value: +; in: edi - ExpressionTerm +; out: edx - 32-bit length followed by numeric data +; preserves: ebx, esi, edi +; note: +; term is converted to numeric and the value workspace may be modified in process +; other pointers to term values may become invalidated + mov eax,[edi+ExpressionTerm.attributes] + mov edx,[edi+ExpressionTerm.value] + test eax,EXPRF_VALUE_IN_WORKSPACE + jz convert_to_numeric + add edx,[value_workspace.memory_start] + convert_to_numeric: + cmp al,EXPR_FLOAT + je wrong_numeric_type + cmp al,EXPR_NUMBER + je value_pointer_ready + mov eax,[edx] + test byte [edx+4+eax-1],80h + jnz extend_to_unsigned_value + mov byte [edi+ExpressionTerm.attributes],EXPR_NUMBER + retn + wrong_numeric_type: + mov edx,_invalid_value + call register_error + mov edx,zero_value + mov [edi+ExpressionTerm.value],edx + mov [edi+ExpressionTerm.attributes],EXPR_NUMBER + retn + extend_to_unsigned_value: + push esi edi + mov esi,edi + mov edi,[value_position] + add edi,[value_workspace.memory_start] + mov ecx,[edx] + add ecx,5 + mov edx,value_workspace + call reserve_workspace + jnc pointer_for_unsigned_value_ready + mov edi,[value_position] + add edi,[value_workspace.memory_start] + pointer_for_unsigned_value_ready: + mov eax,EXPR_NUMBER + EXPRF_VALUE_IN_WORKSPACE + xchg eax,[esi+ExpressionTerm.attributes] + mov edx,[value_position] + xchg edx,[esi+ExpressionTerm.value] + mov esi,edx + test eax,EXPRF_VALUE_IN_WORKSPACE + jz make_unsigned_value + add esi,[value_workspace.memory_start] + make_unsigned_value: + mov edx,edi + lodsd + stosd + mov ecx,eax + shr ecx,2 + rep movsd + mov cl,al + and cl,11b + rep movsb + xor al,al + stosb + sub edi,[value_workspace.memory_start] + mov [value_position],edi + inc dword [edx] + pop edi esi + retn + +get_string_term_value: +; in: edi - ExpressionTerm +; out: edx - 32-bit length followed by string data +; preserves: ebx, esi, edi + mov eax,[edi+ExpressionTerm.attributes] + cmp al,EXPR_STRING + je string_value_available + mov edx,_invalid_value + call register_error + mov [edi+ExpressionTerm.attributes],EXPR_STRING + cmp al,EXPR_NUMBER + je string_value_available + mov edx,zero_value + mov [edi+ExpressionTerm.value],edx + retn + string_value_available: + mov edx,[edi+ExpressionTerm.value] + test eax,EXPRF_VALUE_IN_WORKSPACE + jz string_value_pointer_ready + add edx,[value_workspace.memory_start] + string_value_pointer_ready: + retn + +get_float_term_value: +; in: edi - ExpressionTerm +; out: edx - FloatData +; preserves: ebx, esi, edi +; note: +; term is converted to float and the value workspace may be modified in process +; other pointers to term values may become invalidated + mov eax,[edi+ExpressionTerm.attributes] + mov edx,[edi+ExpressionTerm.value] + test eax,EXPRF_VALUE_IN_WORKSPACE + jz convert_to_float + add edx,[value_workspace.memory_start] + convert_to_float: + cmp al,EXPR_FLOAT + je value_pointer_ready + cmp al,EXPR_STRING + je convert_positive_to_float + mov eax,[edx] + test byte [edx+4+eax-1],80h + jnz convert_negative_to_float + convert_positive_to_float: + push ebx esi + mov ebx,edi + mov edx,value_workspace + mov edi,[edx+Workspace.memory_start] + add edi,[value_position] + mov ecx,sizeof.FloatData + call reserve_workspace + xchg edi,ebx + call get_term_value + mov esi,edx + lea eax,[ebx+sizeof.FloatData] + sub eax,[value_workspace.memory_start] + xchg eax,[value_position] + mov [edi+ExpressionTerm.value],eax + mov [edi+ExpressionTerm.attributes],EXPR_FLOAT + EXPRF_VALUE_IN_WORKSPACE + push edi + mov edi,ebx + call number_to_float + mov edx,edi + pop edi esi ebx + retn + convert_negative_to_float: + push ebx + call negate_term_value + pop ebx + call convert_positive_to_float + or [edx+FloatData.attributes],FLOAT_NEGATIVE + retn + +negate_term_value: +; in: +; edi - ExpressionTerm +; out: +; zf set if result is zero +; preserves: esi, edi + push esi + mov [destination_term],edi + call get_numeric_term_value + mov esi,edx + mov edi,[value_position] + add edi,[value_workspace.memory_start] + mov ecx,[esi] + add ecx,5 + mov edx,value_workspace + call reserve_workspace + jnc pointers_ready_for_negation + mov edi,[destination_term] + call get_term_value + mov esi,edx + mov edi,[value_position] + add edi,[value_workspace.memory_start] + pointers_ready_for_negation: + mov edx,[esi] + and edx,not 11b + mov eax,1 + xor ecx,ecx + add esi,4 + add edi,4 + negate_dwords: + cmp ecx,edx + je dwords_negated + mov ebx,[esi+ecx] + not ebx + add eax,ebx + mov [edi+ecx],eax + setc al + movzx eax,al + add ecx,4 + jmp negate_dwords + dwords_negated: + mov edx,[esi-4] + negate_bytes: + cmp ecx,edx + je bytes_negated + mov bl,[esi+ecx] + not bl + add al,bl + mov [edi+ecx],al + setc al + inc ecx + jmp negate_bytes + bytes_negated: + movsx ebx,byte [esi+ecx-1] + not bh + add al,bh + mov [edi+ecx],al + pop esi + value_calculated: + jecxz check_for_zero + optimize_value: + movsx edx,byte [edi+ecx-1] + cmp dh,[edi+ecx] + jne value_optimized + loop optimize_value + check_for_zero: + cmp byte [edi],0 + je finish_value + value_optimized: + inc ecx + finish_value: + sub edi,4 + mov [edi],ecx + value_finished: + mov edx,edi + sub edx,[value_workspace.memory_start] + lea eax,[edx+4+ecx] + mov [value_position],eax + mov edi,[destination_term] + mov [edi+ExpressionTerm.value],edx + mov [edi+ExpressionTerm.attributes],EXPR_NUMBER + EXPRF_VALUE_IN_WORKSPACE + test ecx,ecx + retn + +add_term_values: +; in: +; esi - source ExpressionTerm +; edi - destination ExpressionTerm +; out: +; zf set if result is zero +; preserves: esi, edi + call prepare_pointers_for_summation + mov edx,[ebx] + cmp edx,[esi] + jb shorter_addend_selected + mov edx,[esi] + xchg ebx,esi + shorter_addend_selected: + and edx,not 11b + add ebx,4 + add esi,4 + add edi,4 + xor al,al + xor ecx,ecx + add_dwords: + cmp ecx,edx + je dwords_added + neg al + mov eax,[esi+ecx] + adc eax,[ebx+ecx] + mov [edi+ecx],eax + setc al + add ecx,4 + jmp add_dwords + dwords_added: + mov edx,[ebx-4] + add_bytes: + cmp ecx,edx + je bytes_added + neg al + mov al,[esi+ecx] + adc al,[ebx+ecx] + mov [edi+ecx],al + setc al + inc ecx + jmp add_bytes + bytes_added: + movsx ebx,byte [ebx+ecx-1] + sar ebx,8 + mov edx,[esi-4] + sub edx,ecx + and edx,not 11b + add edx,ecx + carry_dwords: + cmp ecx,edx + je dwords_carried + neg al + mov eax,[esi+ecx] + adc eax,ebx + mov [edi+ecx],eax + setc al + add ecx,4 + jmp carry_dwords + dwords_carried: + mov edx,[esi-4] + carry_bytes: + cmp ecx,edx + je bytes_carried + neg al + mov al,[esi+ecx] + adc al,bl + mov [edi+ecx],al + setc al + inc ecx + jmp carry_bytes + bytes_carried: + movsx edx,byte [esi+ecx-1] + neg al + mov al,dh + adc al,bl + mov [edi+ecx],al + mov esi,[source_term] + jmp value_calculated + prepare_pointers_for_summation: + mov [source_term],esi + mov [destination_term],edi + call get_numeric_term_value + mov ebx,[edx] + mov edi,esi + call get_numeric_term_value + mov ecx,[edx] + cmp ecx,ebx + jae sum_length_estimated + mov ecx,ebx + sum_length_estimated: + add ecx,5 + mov edi,[value_position] + add edi,[value_workspace.memory_start] + mov edx,value_workspace + call reserve_workspace + mov edi,[destination_term] + call get_term_value + mov ebx,edx + mov edi,esi + call get_term_value + mov esi,edx + mov edi,[value_position] + add edi,[value_workspace.memory_start] + retn + +subtract_term_values: +; in: +; esi - source ExpressionTerm +; edi - destination ExpressionTerm +; out: +; zf set if result is zero +; preserves: esi, edi + call prepare_pointers_for_summation + mov edx,[esi] + cmp edx,[ebx] + jb shorter_length_selected + mov edx,[ebx] + shorter_length_selected: + push edx + and edx,not 11b + add ebx,4 + add esi,4 + add edi,4 + xor al,al + xor ecx,ecx + subtract_dwords: + cmp ecx,edx + je dwords_subtracted + neg al + mov eax,[ebx+ecx] + sbb eax,[esi+ecx] + mov [edi+ecx],eax + setc al + add ecx,4 + jmp subtract_dwords + dwords_subtracted: + pop edx + subtract_bytes: + cmp ecx,edx + je bytes_subtracted + neg al + mov al,[ebx+ecx] + sbb al,[esi+ecx] + mov [edi+ecx],al + setc al + inc ecx + jmp subtract_bytes + bytes_subtracted: + cmp ecx,[ebx-4] + je minuend_ended + xchg esi,ebx + movsx ebx,byte [ebx+ecx-1] + sar ebx,8 + mov edx,[esi-4] + sub edx,ecx + and edx,not 11b + add edx,ecx + borrow_dwords: + cmp ecx,edx + je dwords_borrowed + neg al + mov eax,[esi+ecx] + sbb eax,ebx + mov [edi+ecx],eax + setc al + add ecx,4 + jmp borrow_dwords + dwords_borrowed: + mov edx,[esi-4] + borrow_bytes: + cmp ecx,edx + je bytes_borrowed + neg al + mov al,[esi+ecx] + sbb al,bl + mov [edi+ecx],al + setc al + inc ecx + jmp borrow_bytes + bytes_borrowed: + movsx edx,byte [esi+ecx-1] + neg al + mov al,dh + sbb al,bl + mov [edi+ecx],al + mov esi,[source_term] + jmp value_calculated + minuend_ended: + movsx ebx,byte [ebx+ecx-1] + sar ebx,8 + mov edx,[esi-4] + sub edx,ecx + and edx,not 11b + add edx,ecx + subtract_supernumerary_dwords: + cmp ecx,edx + je supernumerary_dwords_subtracted + neg al + mov eax,ebx + sbb eax,[esi+ecx] + mov [edi+ecx],eax + setc al + add ecx,4 + jmp subtract_supernumerary_dwords + supernumerary_dwords_subtracted: + mov edx,[esi-4] + subtract_supernumerary_bytes: + cmp ecx,edx + je supernumerary_bytes_subtracted + neg al + mov al,bl + sbb al,[esi+ecx] + mov [edi+ecx],al + setc al + inc ecx + jmp subtract_supernumerary_bytes + supernumerary_bytes_subtracted: + movsx edx,byte [esi+ecx-1] + neg al + mov al,bl + sbb al,dh + mov [edi+ecx],al + mov esi,[source_term] + jmp value_calculated + +multiply_term_values: +; in: +; esi - source ExpressionTerm +; edi - destination ExpressionTerm +; out: +; zf set if result is zero +; preserves: esi, edi + mov [source_term],esi + mov [destination_term],edi + call get_numeric_term_value + mov edi,multiplier + mov ecx,4 + call fit_value + jnc multiply_by_dword + mov edi,esi + call get_numeric_term_value + mov esi,[destination_term] + mov edi,multiplier + mov ecx,4 + call fit_value + jnc multiply_by_dword + mov edi,[source_term] + call get_term_value + mov ebx,[edx] + mov edi,[destination_term] + call get_term_value + mov eax,[edx] + mov ecx,ebx + add ecx,eax + cmp ebx,eax + jae split_length_selected + xchg ebx,eax + split_length_selected: + shr ebx,1 + add ecx,18 + mov edi,[value_position] + add edi,[value_workspace.memory_start] + mov edx,value_workspace + call reserve_workspace + mov eax,edi + mov edi,[source_term] + push edi + call get_term_value + mov edi,eax + mov esi,edx + call split_value + mov eax,edi + mov edi,[destination_term] + push edi + call get_term_value + mov edi,eax + mov esi,edx + call split_value + sub edi,[value_workspace.memory_start] + mov edx,edi + mov edi,[free_temporary_terms] + add [free_temporary_terms],6*sizeof.ExpressionTerm + mov eax,EXPR_NUMBER + EXPRF_VALUE_IN_WORKSPACE + xchg edx,[value_position] + push edx + mov esi,[value_workspace.memory_start] + mov ecx,4 + create_temporary_terms: + mov [edi+ExpressionTerm.attributes],eax + mov [edi+ExpressionTerm.value],edx + add edx,[esi+edx] + add edx,4 + add edi,sizeof.ExpressionTerm + loop create_temporary_terms + mov ecx,2 + duplicate_temporary_terms: + mov [edi+ExpressionTerm.attributes],eax + mov edx,[edi-4*sizeof.ExpressionTerm+ExpressionTerm.value] + mov [edi+ExpressionTerm.value],edx + add edi,sizeof.ExpressionTerm + loop duplicate_temporary_terms + push ebx + sub edi,6*sizeof.ExpressionTerm + lea esi,[edi+2*sizeof.ExpressionTerm] + call multiply_term_values + add esi,sizeof.ExpressionTerm + add edi,sizeof.ExpressionTerm + call multiply_term_values + add edi,sizeof.ExpressionTerm + call add_term_values + add esi,2*sizeof.ExpressionTerm + add edi,2*sizeof.ExpressionTerm + call add_term_values + mov esi,edi + sub edi,2*sizeof.ExpressionTerm + call multiply_term_values + add edi,sizeof.ExpressionTerm + mov eax,[esi-3*sizeof.ExpressionTerm+ExpressionTerm.attributes] + mov edx,[esi-3*sizeof.ExpressionTerm+ExpressionTerm.value] + mov [esi+ExpressionTerm.attributes],eax + mov [esi+ExpressionTerm.value],edx + mov eax,[edi-3*sizeof.ExpressionTerm+ExpressionTerm.attributes] + mov edx,[edi-3*sizeof.ExpressionTerm+ExpressionTerm.value] + mov [edi+ExpressionTerm.attributes],eax + mov [edi+ExpressionTerm.value],edx + call add_term_values + sub edi,sizeof.ExpressionTerm + sub esi,sizeof.ExpressionTerm + call subtract_term_values + mov eax,[esp] + xor dl,dl + shld edx,eax,3 + shl eax,3 + call shift_term_value_left + mov esi,edi + sub edi,2*sizeof.ExpressionTerm + call add_term_values + mov esi,edi + add edi,sizeof.ExpressionTerm + pop eax + xor dl,dl + shld edx,eax,4 + shl eax,4 + call shift_term_value_left + pop [value_position] + mov eax,[edi+ExpressionTerm.attributes] + mov edx,[edi+ExpressionTerm.value] + pop edi + mov [edi+ExpressionTerm.attributes],eax + mov [edi+ExpressionTerm.value],edx + call add_term_values + mov [free_temporary_terms],esi + pop esi + test edi,edi + retn + split_value: + cmp ebx,[esi] + jae too_small_to_split + mov eax,ebx + inc eax + stosd + mov ecx,ebx + lodsd + shr ecx,2 + rep movsd + mov ecx,ebx + and ecx,11b + rep movsb + mov ecx,eax + xor al,al + stosb + sub ecx,ebx + mov eax,ecx + stosd + shr ecx,2 + rep movsd + mov ecx,eax + and ecx,11b + rep movsb + retn + too_small_to_split: + lodsd + mov ecx,eax + stosd + shr ecx,2 + rep movsd + mov ecx,eax + and ecx,11b + rep movsb + xor eax,eax + stosd + retn + multiply_by_dword: + sets al + mov [multiplier_sign],al + movzx eax,al + or eax,[multiplier] + jz zero_product + mov ebx,esi + mov edi,esi + call get_numeric_term_value + mov esi,edx + mov ecx,[esi] + add ecx,16 + mov edi,[value_position] + add edi,[value_workspace.memory_start] + mov edx,value_workspace + call reserve_workspace + jnc pointers_ready_for_multiplication_by_dword + mov edi,ebx + call get_term_value + mov esi,edx + mov edi,[value_position] + add edi,[value_workspace.memory_start] + pointers_ready_for_multiplication_by_dword: + mov eax,[esi] + and eax,not 11b + mov [edi],eax + add esi,4 + add edi,4 + xor ebx,ebx + xor ecx,ecx + multiply_dwords_by_dword: + cmp ecx,[edi-4] + je dwords_multiplied_by_dword + mov eax,[esi+ecx] + mul [multiplier] + add eax,ebx + adc edx,0 + mov ebx,edx + mov [edi+ecx],eax + add ecx,4 + jmp multiply_dwords_by_dword + dwords_multiplied_by_dword: + mov edx,[esi-4] + and edx,11b + jz only_sign_in_last_dword + cmp edx,2 + jb single_byte_in_last_dword + je two_bytes_in_last_dword + movsx eax,byte [esi+ecx+2] + shl eax,16 + mov ax,[esi+ecx] + jmp last_dword_ready + two_bytes_in_last_dword: + movsx eax,word [esi+ecx] + jmp last_dword_ready + single_byte_in_last_dword: + movsx eax,byte [esi+ecx] + jmp last_dword_ready + only_sign_in_last_dword: + movsx eax,byte [esi+ecx-1] + sar eax,8 + last_dword_ready: + mov [edi-4],eax + mul [multiplier] + add eax,ebx + adc edx,0 + mov [edi+ecx],eax + xor eax,eax + mov ebx,[esi-4] + test byte [esi+ebx-1],80h + jz product_extension + sub edx,[multiplier] + sbb eax,0 + product_extension: + mov [edi+ecx+4],edx + add ecx,8 + mov [edi+ecx],eax + cmp [multiplier_sign],0 + je product_ready + mov edx,[esi-4] + and edx,not 11b + xor ecx,ecx + xor al,al + adjust_product: + cmp ecx,edx + je finish_product_adjustment + neg al + mov eax,[esi+ecx] + sbb [edi+ecx+4],eax + setc al + add ecx,4 + jmp adjust_product + finish_product_adjustment: + neg al + mov eax,[edi-4] + cdq + sbb [edi+ecx+4],eax + sbb [edi+ecx+8],edx + add ecx,8 + product_ready: + mov esi,[source_term] + jmp value_calculated + zero_product: + ; zf already set + mov esi,[source_term] + mov edi,[destination_term] + mov [edi+ExpressionTerm.value],zero_value + mov [edi+ExpressionTerm.attributes],EXPR_NUMBER + retn + +divide_term_values: +; in: +; esi - divisor ExpressionTerm, to be replaced with remainder +; edi - dividend ExpressionTerm, to be replaced with quotient +; out: +; zf set if remainder is zero +; preserves: esi, edi + mov [source_term],esi + mov [destination_term],edi + call get_numeric_term_value + mov eax,[edx] + test byte [edx+4+eax-1],80h + jnz negative_dividend + xchg edi,esi + call get_numeric_term_value + mov eax,[edx] + test byte [edx+4+eax-1],80h + jnz negative_divisor + mov edi,divisor + mov ecx,4 + call fit_value + jnc divide_by_dword + xor eax,eax + mov ebx,[edx] + find_divisor_length: + mov al,[edx+4+ebx-1] + test al,al + jnz divisor_length_ok + dec ebx + jmp find_divisor_length + divisor_length_ok: + bsr ecx,eax + inc cl + mov [long_divisor_length],ebx + mov [high_bits_count],cl + mov ebx,[edx+4+ebx-5] + shrd ebx,eax,cl + inc ebx + mov [divisor],ebx + mov eax,[source_term] + mov [long_divisor_term],eax + mov eax,[destination_term] + mov [long_dividend_term],eax + mov edi,[free_temporary_terms] + add [free_temporary_terms],4*sizeof.ExpressionTerm + mov [division_temporary_terms],edi + xor eax,eax + mov [edi+ExpressionTerm.attributes],eax + compare_dividend_with_divisor: + mov esi,edx + mov edi,[long_dividend_term] + call get_term_value + xor eax,eax + mov ebx,[edx] + find_dividend_length: + mov al,[edx+4+ebx-1] + test al,al + jnz dividend_length_ok + dec ebx + jmp find_dividend_length + dividend_length_ok: + cmp ebx,[long_divisor_length] + ja long_division + jb dividend_to_remainder + bsr ecx,eax + inc cl + cmp cl,[high_bits_count] + ja long_division + jb dividend_to_remainder + compare_with_divisor_bytes: + mov al,[edx+4+ebx-1] + cmp al,[esi+4+ebx-1] + ja subtract_divisor + jb dividend_to_remainder + dec ebx + jnz compare_with_divisor_bytes + mov esi,[long_divisor_term] + mov edi,[long_dividend_term] + mov eax,EXPR_NUMBER + mov [esi+ExpressionTerm.attributes],eax + mov [esi+ExpressionTerm.value],zero_value + mov [edi+ExpressionTerm.attributes],eax + mov [edi+ExpressionTerm.value],singular_value + jmp accomodate_quotient + dividend_to_remainder: + mov esi,[division_temporary_terms] + mov [free_temporary_terms],esi + mov eax,[esi+ExpressionTerm.attributes] + mov edx,[esi+ExpressionTerm.value] + test eax,eax + jnz quotient_ready + mov eax,EXPR_NUMBER + mov edx,zero_value + quotient_ready: + mov esi,[long_divisor_term] + mov edi,[long_dividend_term] + xchg eax,[edi+ExpressionTerm.attributes] + xchg edx,[edi+ExpressionTerm.value] + mov [esi+ExpressionTerm.attributes],eax + mov [esi+ExpressionTerm.value],edx + jmp test_remainder + subtract_divisor: + mov esi,[long_divisor_term] + mov edi,[long_dividend_term] + call subtract_term_values + mov eax,EXPR_NUMBER + mov edx,singular_value + xchg eax,[edi+ExpressionTerm.attributes] + xchg edx,[edi+ExpressionTerm.value] + mov [esi+ExpressionTerm.attributes],eax + mov [esi+ExpressionTerm.value],edx + accomodate_quotient: + mov esi,[division_temporary_terms] + mov [free_temporary_terms],esi + cmp [esi+ExpressionTerm.attributes],0 + je quotient_complete + call add_term_values + quotient_complete: + mov esi,[long_divisor_term] + test_remainder: + cmp dword [esi],0 + retn + long_division: + mov edi,[division_temporary_terms] + add edi,sizeof.ExpressionTerm + mov esi,[long_dividend_term] + mov eax,[esi+ExpressionTerm.attributes] + mov edx,[esi+ExpressionTerm.value] + mov [edi+ExpressionTerm.attributes],eax + mov [edi+ExpressionTerm.value],edx + mov ecx,8 + cmp [divisor],0 + je shift_dividend_right + add cl,32 + shift_dividend_right: + xor edx,edx + mov eax,[long_divisor_length] + shld edx,eax,3 + shl eax,3 + sub cl,[high_bits_count] + sub eax,ecx + sbb edx,0 + call shift_term_value_right + cmp [divisor],0 + je quotient_approximation_ready + mov esi,edi + mov [destination_term],esi + add edi,sizeof.ExpressionTerm + mov [source_term],edi + call divide_by_dword + quotient_approximation_ready: + mov esi,[division_temporary_terms] + cmp [esi+ExpressionTerm.attributes],0 + jne accumulate_quotient + mov eax,[edi+ExpressionTerm.attributes] + mov edx,[edi+ExpressionTerm.value] + mov [esi+ExpressionTerm.attributes],eax + mov [esi+ExpressionTerm.value],edx + jmp calculate_remainder + accumulate_quotient: + xchg esi,edi + call add_term_values + mov edi,esi + calculate_remainder: + mov esi,[long_divisor_term] + call multiply_term_values + mov esi,edi + mov edi,[long_dividend_term] + call subtract_term_values + mov edi,[long_divisor_term] + call get_term_value + jmp compare_dividend_with_divisor + negative_divisor: + call negate_term_value + xchg edi,esi + call divide_term_values + pushf + call negate_term_value + popf + retn + negative_dividend: + call negate_term_value + xchg edi,esi + call get_numeric_term_value + mov eax,[edx] + test byte [edx+4+eax-1],80h + jnz negative_dividend_and_divisor + xchg edi,esi + call divide_term_values + call negate_term_value + jmp negative_remainder + negative_dividend_and_divisor: + call negate_term_value + xchg edi,esi + call divide_term_values + negative_remainder: + xchg edi,esi + call negate_term_value + xchg edi,esi + retn + divide_by_dword: + cmp [divisor],0 + je division_by_zero + mov ebx,esi + mov edi,esi + call get_term_value + mov esi,edx + mov ecx,[esi] + add ecx,4+9 + mov edi,[value_position] + add edi,[value_workspace.memory_start] + mov edx,value_workspace + call reserve_workspace + jnc pointers_ready_for_division_by_dword + mov edi,ebx + call get_term_value + mov esi,edx + mov edi,[value_position] + add edi,[value_workspace.memory_start] + pointers_ready_for_division_by_dword: + mov eax,[esi] + add esi,4 + add edi,4 + mov ecx,eax + and ecx,not 11b + xor edx,edx + and eax,11b + jz first_dword_for_division_ready + cmp eax,2 + jb single_byte_in_first_dword + je two_bytes_in_first_dword + mov dl,[esi+ecx+2] + shl edx,16 + two_bytes_in_first_dword: + mov dh,[esi+ecx+1] + single_byte_in_first_dword: + mov dl,[esi+ecx] + first_dword_for_division_ready: + mov eax,edx + xor edx,edx + div [divisor] + mov [edi+ecx],eax + divide_dwords: + sub ecx,4 + jc dwords_divided + mov eax,[esi+ecx] + div [divisor] + mov [edi+ecx],eax + jmp divide_dwords + dwords_divided: + mov ecx,[esi-4] + and ecx,not 11b + add ecx,4 + call optimize_positive_value + lea ebx,[edi-4] + mov [ebx],ecx + lea edi,[edi+ecx+4] + mov ecx,5 + mov [edi],edx + mov [edi+4],ch + call optimize_positive_value + lea eax,[edi-4] + mov [eax],ecx + sub ebx,[value_workspace.memory_start] + mov edi,[destination_term] + mov [edi+ExpressionTerm.value],ebx + mov [edi+ExpressionTerm.attributes],EXPR_NUMBER + EXPRF_VALUE_IN_WORKSPACE + sub eax,[value_workspace.memory_start] + mov esi,[source_term] + mov [esi+ExpressionTerm.value],eax + mov [esi+ExpressionTerm.attributes],EXPR_NUMBER + EXPRF_VALUE_IN_WORKSPACE + add eax,4 + add eax,ecx + mov [value_position],eax + test ecx,ecx + retn + optimize_positive_value: + mov al,[edi+ecx-1] + test al,al + js zero_extension_needed + jnz positive_value_optimized + dec ecx + jnz optimize_positive_value + positive_value_optimized: + retn + zero_extension_needed: + inc ecx + retn + division_by_zero: + mov edx,_indeterminate_result + call register_error + xor eax,eax + jmp zero_product + +invert_term_value_bits: +; in: +; edi - ExpressionTerm +; out: +; zf set if result is zero +; preserves: esi, edi + mov [destination_term],edi + call get_numeric_term_value + mov esi,edx + mov edi,[value_position] + add edi,[value_workspace.memory_start] + mov ecx,[esi] + add ecx,4 + mov edx,value_workspace + call reserve_workspace + jnc pointers_ready_for_bit_inversion + mov edi,[destination_term] + call get_term_value + mov esi,edx + mov edi,[value_position] + add edi,[value_workspace.memory_start] + pointers_ready_for_bit_inversion: + mov edx,[esi] + xor ecx,ecx + test edx,edx + jz invert_zero + and edx,not 11b + add esi,4 + add edi,4 + invert_bits_in_dwords: + cmp ecx,edx + je bits_in_dwords_inverted + mov eax,[esi+ecx] + not eax + mov [edi+ecx],eax + add ecx,4 + jmp invert_bits_in_dwords + bits_in_dwords_inverted: + mov edx,[esi-4] + invert_bits_in_bytes: + cmp ecx,edx + je bits_in_bytes_inverted + mov al,[esi+ecx] + not al + mov [edi+ecx],al + inc ecx + jmp invert_bits_in_bytes + bits_in_bytes_inverted: + sub edi,4 + mov [edi],ecx + jmp value_finished + invert_zero: + inc ecx + mov [edi],ecx + neg cl + mov [edi+4],cl + jmp value_finished + +bitwise_add_term_values: +; in: +; esi - source ExpressionTerm +; edi - destination ExpressionTerm +; out: +; zf set if result is zero +; preserves: esi, edi + call set_up_bitwise_operation + jc zero_product + bitwise_add_dwords: + cmp ecx,edx + je dwords_added_bitwise + mov eax,[esi+ecx] + xor eax,[ebx+ecx] + mov [edi+ecx],eax + add ecx,4 + jmp bitwise_add_dwords + dwords_added_bitwise: + mov edx,[ebx-4] + bitwise_add_bytes: + cmp ecx,edx + je bytes_added_bitwise + mov al,[esi+ecx] + xor al,[ebx+ecx] + mov [edi+ecx],al + inc ecx + jmp bitwise_add_bytes + bytes_added_bitwise: + movsx ebx,byte [ebx+ecx-1] + sar ebx,8 + mov edx,[esi-4] + sub edx,ecx + and edx,not 11b + add edx,ecx + bitwise_add_supernumerary_dwords: + cmp ecx,edx + je supernumerary_dwords_added_bitwise + mov eax,[esi+ecx] + xor eax,ebx + mov [edi+ecx],eax + add ecx,4 + jmp bitwise_add_supernumerary_dwords + supernumerary_dwords_added_bitwise: + mov edx,[esi-4] + bitwise_add_supernumerary_bytes: + cmp ecx,edx + je supernumerary_bytes_added_bitwise + mov al,[esi+ecx] + xor al,bl + mov [edi+ecx],al + inc ecx + jmp bitwise_add_supernumerary_bytes + supernumerary_bytes_added_bitwise: + dec ecx + mov esi,[source_term] + jmp value_calculated + set_up_bitwise_operation: + mov [source_term],esi + mov [destination_term],edi + call get_numeric_term_value + mov ebx,[edx] + mov edi,esi + call get_numeric_term_value + mov ecx,[edx] + mov eax,ecx + or eax,ebx + jz bitwise_zero_with_zero + cmp ecx,ebx + jae bitwise_result_length_estimated + mov ecx,ebx + bitwise_result_length_estimated: + add ecx,4 + mov edi,[value_position] + add edi,[value_workspace.memory_start] + mov edx,value_workspace + call reserve_workspace + mov edi,[destination_term] + call get_term_value + mov ebx,edx + mov edi,esi + call get_term_value + mov esi,edx + mov edi,[value_position] + add edi,[value_workspace.memory_start] + mov edx,[ebx] + cmp edx,[esi] + jb shorter_value_selected + mov edx,[esi] + xchg ebx,esi + shorter_value_selected: + and edx,not 11b + add ebx,4 + add esi,4 + add edi,4 + xor al,al + xor ecx,ecx + retn + bitwise_zero_with_zero: + stc + retn + +bitwise_multiply_term_values: +; in: +; esi - source ExpressionTerm +; edi - destination ExpressionTerm +; out: +; zf set if result is zero +; preserves: esi, edi + call set_up_bitwise_operation + jc zero_product + bitwise_multiply_dwords: + cmp ecx,edx + je dwords_multiplied_bitwise + mov eax,[esi+ecx] + and eax,[ebx+ecx] + mov [edi+ecx],eax + add ecx,4 + jmp bitwise_multiply_dwords + dwords_multiplied_bitwise: + mov edx,[ebx-4] + bitwise_multiply_bytes: + cmp ecx,edx + je bytes_multiplied_bitwise + mov al,[esi+ecx] + and al,[ebx+ecx] + mov [edi+ecx],al + inc ecx + jmp bitwise_multiply_bytes + bytes_multiplied_bitwise: + movsx ebx,byte [ebx+ecx-1] + sar ebx,8 + mov edx,[esi-4] + sub edx,ecx + and edx,not 11b + add edx,ecx + bitwise_multiply_supernumerary_dwords: + cmp ecx,edx + je supernumerary_dwords_multiplied_bitwise + mov eax,[esi+ecx] + and eax,ebx + mov [edi+ecx],eax + add ecx,4 + jmp bitwise_multiply_supernumerary_dwords + supernumerary_dwords_multiplied_bitwise: + mov edx,[esi-4] + bitwise_multiply_supernumerary_bytes: + cmp ecx,edx + je supernumerary_bytes_multiplied_bitwise + mov al,[esi+ecx] + and al,bl + mov [edi+ecx],al + inc ecx + jmp bitwise_multiply_supernumerary_bytes + supernumerary_bytes_multiplied_bitwise: + dec ecx + mov esi,[source_term] + jmp value_calculated + +bitwise_inclusive_or_of_term_values: +; in: +; esi - source ExpressionTerm +; edi - destination ExpressionTerm +; out: +; zf set if result is zero +; preserves: esi, edi + call set_up_bitwise_operation + jc zero_product + inclusive_or_of_dwords: + cmp ecx,edx + je performed_inclusive_or_of_dwords + mov eax,[esi+ecx] + or eax,[ebx+ecx] + mov [edi+ecx],eax + add ecx,4 + jmp inclusive_or_of_dwords + performed_inclusive_or_of_dwords: + mov edx,[ebx-4] + inclusive_or_of_bytes: + cmp ecx,edx + je performed_inclusive_or_of_bytes + mov al,[esi+ecx] + or al,[ebx+ecx] + mov [edi+ecx],al + inc ecx + jmp inclusive_or_of_bytes + performed_inclusive_or_of_bytes: + movsx ebx,byte [ebx+ecx-1] + sar ebx,8 + mov edx,[esi-4] + sub edx,ecx + and edx,not 11b + add edx,ecx + inclusive_or_of_supernumerary_dwords: + cmp ecx,edx + je performed_inclusive_or_of_supernumerary_dwords + mov eax,[esi+ecx] + or eax,ebx + mov [edi+ecx],eax + add ecx,4 + jmp inclusive_or_of_supernumerary_dwords + performed_inclusive_or_of_supernumerary_dwords: + mov edx,[esi-4] + inclusive_or_of_supernumerary_bytes: + cmp ecx,edx + je performed_inclusive_or_of_supernumerary_bytes + mov al,[esi+ecx] + or al,bl + mov [edi+ecx],al + inc ecx + jmp inclusive_or_of_supernumerary_bytes + performed_inclusive_or_of_supernumerary_bytes: + dec ecx + mov esi,[source_term] + jmp value_calculated + +shift_term_value_left: +; in: +; edi - ExpressionTerm +; dl:eax = number of bits +; out: +; zf set if result is zero +; preserves: esi, edi + mov ebx,eax + and al,8-1 + mov [bit_shift],al + shrd ebx,edx,3 + shr dl,3 + mov [shift_overflow],dl + mov [destination_term],edi + call get_numeric_term_value + mov eax,[edx] + check_for_shifted_zero: + test eax,eax + jz shift_left_ok + cmp byte [edx+4+eax-1],0 + jne reserve_memory_for_shift_left + dec eax + jmp check_for_shifted_zero + reserve_memory_for_shift_left: + cmp [shift_overflow],0 + jne out_of_memory + push esi + mov esi,edx + mov edi,[value_position] + add edi,[value_workspace.memory_start] + mov ecx,[esi] + add ecx,ebx + jc out_of_memory + add ecx,5 + jc out_of_memory + mov edx,value_workspace + call reserve_workspace + jnc pointers_ready_for_shift_left + mov edi,[destination_term] + call get_term_value + mov esi,edx + mov edi,[value_position] + add edi,[value_workspace.memory_start] + pointers_ready_for_shift_left: + add edi,4 + mov ecx,ebx + push ecx + shr ecx,2 + xor eax,eax + rep stosd + mov ecx,ebx + and ecx,11b + xor al,al + rep stosb + mov cl,[bit_shift] + mov edx,[esi] + add esi,4 + xor ebx,ebx + shift_bytes_left: + cmp ebx,edx + je bytes_shifted_left + mov ah,[esi+ebx] + shl eax,cl + mov [edi+ebx],ah + mov al,[esi+ebx] + inc ebx + jmp shift_bytes_left + bytes_shifted_left: + movsx eax,byte [esi+ebx-1] + shl eax,cl + mov [edi+ebx],ah + pop ecx + sub edi,ecx + add ecx,ebx + pop esi + jmp value_calculated + shift_left_ok: + retn + +shift_term_value_right: +; in: +; edi - ExpressionTerm +; dl:eax = number of bits +; out: +; zf set if result is zero +; preserves: esi, edi + push esi + mov ebx,eax + and al,8-1 + mov [bit_shift],al + shrd ebx,edx,3 + shr dl,3 + jz byte_shift_ok + or ebx,-1 + byte_shift_ok: + mov [destination_term],edi + call get_numeric_term_value + mov esi,edx + mov edi,[value_position] + add edi,[value_workspace.memory_start] + mov ecx,[esi] + sub ecx,ebx + jnc size_of_shift_result_estimated + xor ecx,ecx + size_of_shift_result_estimated: + add ecx,5 + mov edx,value_workspace + call reserve_workspace + jnc pointers_ready_for_shift_right + mov edi,[destination_term] + call get_term_value + mov esi,edx + mov edi,[value_position] + add edi,[value_workspace.memory_start] + pointers_ready_for_shift_right: + add edi,4 + mov cl,[bit_shift] + mov edx,[esi] + sub edx,ebx + jbe value_dissipated + lea esi,[esi+4+ebx] + xor ebx,ebx + shift_bytes_right: + inc ebx + cmp ebx,edx + je bytes_shifted_right + mov ax,[esi+ebx-1] + shr ax,cl + mov [edi+ebx-1],al + jmp shift_bytes_right + bytes_shifted_right: + dec ebx + movsx eax,byte [esi+ebx] + shr eax,cl + mov [edi+ebx],al + mov ecx,ebx + pop esi + jmp value_calculated + value_dissipated: + mov edx,[esi] + movsx eax,byte [esi+4+edx-1] + mov [edi],ah + xor ecx,ecx + pop esi + jmp value_calculated + +find_first_set_bit_in_term_value: +; in: +; esi - ExpressionTerm +; out: +; zf set when value is zero +; when zf = 0: +; dl:eax = index of first set bit +; preserves: esi, edi + xchg edi,esi + call get_numeric_term_value + xchg edi,esi + mov ebx,edx + mov edx,[ebx] + and edx,not 11b + xor ecx,ecx + add ebx,4 + find_first_set_bit_in_dwords: + cmp ecx,edx + je no_set_bit_found_in_dwords + bsf eax,[ebx+ecx] + jnz first_set_bit_found + add ecx,4 + jmp find_first_set_bit_in_dwords + no_set_bit_found_in_dwords: + mov edx,[ebx-4] + find_first_set_bit_in_bytes: + cmp ecx,edx + je no_set_bit_found + movzx eax,byte [ebx+ecx] + bsf eax,eax + jnz first_set_bit_found + inc ecx + jmp find_first_set_bit_in_bytes + no_set_bit_found: + retn + first_set_bit_found: + xor dl,dl + shld edx,ecx,3 + shl ecx,3 + add eax,ecx + adc dl,0 + inc dh + retn + +find_last_set_bit_in_term_value: +; in: +; esi - ExpressionTerm +; out: +; cf set when value is zero or negative +; when cf = 0: +; dl:eax = index of last set bit +; preserves: esi, edi + xchg edi,esi + call get_numeric_term_value + xchg edi,esi + mov ebx,edx + mov edx,[ebx] + add ebx,4 + test byte [ebx+edx-1],80h + jnz last_set_bit_unreachable + mov ecx,edx + and edx,11b + find_last_set_bit_in_dwords: + cmp ecx,edx + je find_last_set_bit_in_bytes + sub ecx,4 + bsr eax,[ebx+ecx] + jnz last_set_bit_found + jmp find_last_set_bit_in_dwords + find_last_set_bit_in_bytes: + jecxz last_set_bit_unreachable + dec ecx + movzx eax,byte [ebx+ecx] + bsr eax,eax + jnz last_set_bit_found + jmp find_last_set_bit_in_bytes + last_set_bit_unreachable: + stc + retn + last_set_bit_found: + xor dl,dl + shld edx,ecx,3 + shl ecx,3 + add eax,ecx + adc dl,0 + ; clc + retn + +truncate_term_value: +; in: +; edi - ExpressionTerm +; preserves: edi + cmp byte [edi+ExpressionTerm.attributes],EXPR_FLOAT + jne get_numeric_term_value + mov [destination_term],edi + call get_term_value + mov ecx,[edx+FloatData.exponent] + cmp ecx,0 + jl no_integer_part + mov esi,edx + mov edi,[value_position] + add edi,[value_workspace.memory_start] + shr ecx,5 + lea ecx,[4+(ecx+1)*4] + mov edx,value_workspace + call reserve_workspace + jnc pointers_ready_for_truncation + mov edi,[destination_term] + call get_term_value + mov esi,edx + mov edi,[value_position] + add edi,[value_workspace.memory_start] + pointers_ready_for_truncation: + add edi,4 + mov ecx,32*MANTISSA_SEGMENTS + sub ecx,[esi+FloatData.exponent] + jbe integer_precision_loss + dec ecx + mov edx,ecx + and ecx,11111b + shr edx,5 + neg edx + add edx,MANTISSA_SEGMENTS + mov ebx,[esi+FloatData.mantissa+(edx-1)*4] + extract_integer_bits: + mov eax,ebx + dec edx + jz extract_highest_integer_bits + mov ebx,[esi+FloatData.mantissa+(edx-1)*4] + shrd eax,ebx,cl + stosd + jmp extract_integer_bits + extract_highest_integer_bits: + shr eax,cl + stosd + integer_part_ready: + and byte [edi],0 + dec ecx + sar ecx,3 + neg ecx + add ecx,edi + mov edi,[destination_term] + mov [edi+ExpressionTerm.attributes],EXPR_NUMBER + EXPRF_VALUE_IN_WORKSPACE + mov edx,[value_position] + mov [edi+ExpressionTerm.value],edx + sub ecx,[value_workspace.memory_start] + mov [value_position],ecx + sub ecx,edx + sub ecx,4 + add edx,[value_workspace.memory_start] + mov [edx],ecx + test [esi+FloatData.attributes],FLOAT_NEGATIVE + jnz negate_term_value + retn + integer_precision_loss: + neg ecx + inc ecx + mov edx,ecx + shr ecx,5 + xor eax,eax + rep stosd + mov ecx,edx + and ecx,11111b + mov edx,MANTISSA_SEGMENTS-1 + mov eax,[esi+FloatData.mantissa+edx*4] + shl eax,cl + stosd + extract_significant_bits: + mov eax,[esi+FloatData.mantissa+(edx-1)*4] + mov ebx,[esi+FloatData.mantissa+edx*4] + shld eax,ebx,cl + stosd + dec edx + jnz extract_significant_bits + mov eax,[esi+FloatData.mantissa+edx*4] + neg ecx + and ecx,11111b + jnz extract_highest_integer_bits + xor eax,eax + jmp integer_part_ready + no_integer_part: + mov [edi+ExpressionTerm.value],zero_value + mov [edi+ExpressionTerm.attributes],EXPR_NUMBER + retn + +reverse_term_value_bytes: +; in: +; edi - ExpressionTerm +; ebx = number of bytes +; preserves: esi, edi + push esi + mov [destination_term],edi + call get_numeric_term_value + mov edi,[value_position] + add edi,[value_workspace.memory_start] + mov ecx,ebx + add ecx,4 + mov edx,value_workspace + call reserve_workspace + mov edi,[destination_term] + call get_term_value + mov [edi+ExpressionTerm.attributes],EXPR_STRING + EXPRF_VALUE_IN_WORKSPACE + mov esi,[value_position] + mov [edi+ExpressionTerm.value],esi + lea eax,[esi+4+ebx] + mov [value_position],eax + add esi,[value_workspace.memory_start] + mov [esi],ebx + xor ecx,ecx + test ebx,ebx + jz reversed_bytes_ready + store_reversed_bytes: + cmp ecx,[edx] + jae extend_reversed_bytes + mov al,[edx+4+ecx] + mov [esi+4+ebx-1],al + inc ecx + dec ebx + jnz store_reversed_bytes + reversed_bytes_ready: + cmp ecx,[edx] + jae reversed_bytes_ok + mov al,[edx+4+ecx] + cbw + mov al,ah + lea edi,[edx+4+ecx] + neg ecx + add ecx,[edx] + repe scasb + je reversed_bytes_ok + mov edx,_value_out_of_range + call register_error + jmp reversed_bytes_ok + extend_reversed_bytes: + mov al,[edx+4+ecx-1] + cbw + mov al,ah + lea edi,[esi+4] + mov ecx,ebx + rep stosb + reversed_bytes_ok: + mov edi,[destination_term] + pop esi + retn diff --git a/x86_64_sse2_x87/fasm/source/floats.inc b/x86_64_sse2_x87/fasm/source/floats.inc new file mode 100644 index 0000000..8366b5c --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/floats.inc @@ -0,0 +1,1182 @@ + +struct FloatData + attributes dd ? ; FLOAT_# + exponent dd ? + mantissa rd MANTISSA_SEGMENTS +ends + +MANTISSA_SEGMENTS = 4 + +FLOAT_NEGATIVE = 1 +FLOAT_INFINITE = 2 +FLOAT_INDETERMINATE = 4 +FLOAT_UNDERFLOW = 8 + +number_to_float: +; in: +; esi - 32-bit length followed by binary data of that length +; edi - FloatData +; preserves: edi +; note: the number is treated as unsigned + xor eax,eax + mov [edi+FloatData.attributes],eax + lodsd + mov ecx,eax + cmp eax,10000000h + ja unsigned_too_large + shl eax,3 + dec eax + mov [edi+FloatData.exponent],eax + mov edx,MANTISSA_SEGMENTS*4 + cmp ecx,edx + ja unsigned_precision_loss + xor eax,eax + mov [mantissa_tail],eax + zero_appended_segments: + sub edx,4 + jc converted_mantissa_ready + cmp ecx,edx + ja appended_segments_zeroed + mov [edi+FloatData.mantissa+edx],eax + jmp zero_appended_segments + appended_segments_zeroed: + sub ecx,edx + jz copy_significant_dwords + read_lowest_bytes: + lodsb + ror eax,8 + loop read_lowest_bytes + mov [edi+FloatData.mantissa+edx],eax + sub edx,4 + jc converted_mantissa_ready + copy_significant_dwords: + lodsd + mov [edi+FloatData.mantissa+edx],eax + sub edx,4 + jnc copy_significant_dwords + converted_mantissa_ready: + mov esi,edi + call normalize_float + call round_float + retn + unsigned_precision_loss: + sub ecx,edx + add esi,ecx + mov eax,[esi-4] + sub ecx,4 + jz unsigned_tail_ready + jc unsigned_short_tail + xor dl,dl + lea ebx,[esi-4] + unsigned_tail_contraction: + dec ebx + or dl,[ebx] + loop unsigned_tail_contraction + setnz dl + and dl,1 + or al,dl + jmp unsigned_tail_ready + unsigned_short_tail: + neg cl + shl cl,3 + shr eax,cl + shl eax,cl + unsigned_tail_ready: + mov [mantissa_tail],eax + mov edx,(MANTISSA_SEGMENTS-1)*4 + jmp copy_significant_dwords + unsigned_too_large: + or [edi+FloatData.attributes],FLOAT_INFINITE + retn + +get_float_exponent: +; in: +; esi - FloatData +; out: +; zf set when value is zero +; eax = exponent of normalized value +; preserves: ebx, ecx, esi, edi +; note: +; when value cannot be normalized, smallest possible exponent is returned +; float attributes are ignored + xor eax,eax + find_exponent: + inc eax + bsr edx,[esi+FloatData.mantissa+(eax-1)*4] + jnz exponent_found + cmp eax,MANTISSA_SEGMENTS + jne find_exponent + xor edx,edx + minimal_exponent: + mov eax,-80000000h + test edx,edx + retn + exponent_found: + assert MANTISSA_SEGMENTS shl 5 <= 1 shl 32 + inc edx + shl eax,5 + sub edx,eax + mov eax,[esi+FloatData.exponent] + add eax,edx + jo minimal_exponent + test esi,esi + retn + +get_float_unnormalization: +; in: +; esi - FloatData +; out: +; cf set when value is zero +; when cf = 0: +; ecx = number of leading zeroes in mantissa +; preserves: ebx, edx, esi, edi + xor ecx,ecx + find_highest_set_bit: + inc ecx + bsr eax,[esi+FloatData.mantissa+(ecx-1)*4] + jnz highest_set_bit_found + cmp ecx,MANTISSA_SEGMENTS + jne find_highest_set_bit + stc + retn + highest_set_bit_found: + assert MANTISSA_SEGMENTS shl 5 <= 1 shl 32 + inc eax + shl ecx,5 + sub ecx,eax + ; clc + retn + +normalize_float: +; in: +; esi - source FloatData +; edi - destination FloatData +; [mantissa_tail] = additional bits of source mantissa, lowest bit indicating that there are set bits far down +; out: +; [mantissa_tail] = additional bits of destination mantissa, lowest bit indicating that there are set bits far down +; preserves: esi, edi +; note: float attributes are ignored + xor edx,edx + find_highest_bit: + bsr ecx,[esi+FloatData.mantissa+edx*4] + jnz highest_bit_found + inc edx + cmp edx,MANTISSA_SEGMENTS + jne find_highest_bit + bsr ecx,[mantissa_tail] + jnz highest_bit_found + call copy_float_data + mov [edi+FloatData.exponent],-80000000h + retn + copy_float_data: + cmp esi,edi + je float_data_ready + assert sizeof.FloatData and 11b = 0 + mov ecx,sizeof.FloatData shr 2 + rep movsd + sub esi,sizeof.FloatData + sub edi,sizeof.FloatData + float_data_ready: + retn + highest_bit_found: + not ecx + add ecx,32 + mov eax,edx + shl eax,5 + add eax,ecx + neg eax + jz copy_float_data + add eax,[esi+FloatData.exponent] + jo copy_float_data + mov [edi+FloatData.exponent],eax + add edi,FloatData.mantissa + shift_mantissa_up: + push edx + xor eax,eax + cmp edx,MANTISSA_SEGMENTS-1 + ja shift_mantissa_tail_up + mov eax,[esi+FloatData.mantissa+edx*4] + je shift_mantissa_tail_up + shift_mantissa_segment_up: + inc edx + mov ebx,[esi+FloatData.mantissa+edx*4] + shld eax,ebx,cl + stosd + mov eax,ebx + cmp edx,MANTISSA_SEGMENTS-1 + jne shift_mantissa_segment_up + shift_mantissa_tail_up: + mov ebx,[mantissa_tail] + and ebx,not 1 + shld eax,ebx,cl + stosd + mov eax,ebx + shl eax,cl + pop ecx + jecxz mantissa_normalized + stosd + dec ecx + xor eax,eax + rep stosd + mantissa_normalized: + and [mantissa_tail],1 + or [mantissa_tail],eax + sub edi,FloatData.mantissa + MANTISSA_SEGMENTS*4 + mov eax,[esi+FloatData.attributes] + mov [edi+FloatData.attributes],eax + retn + +unnormalize_float: +; in: +; esi - source FloatData +; edi - destination FloatData +; ecx = target exponent +; [mantissa_tail] = additional bits of source mantissa, lowest bit indicating that there are set bits far down +; out: +; [mantissa_tail] = additional bits of destination mantissa, lowest bit indicating that there are set bits far down +; preserves: esi, edi +; note: +; when target exponent is smaller than the actual one, the overflowing bits are ignored +; float attributes are ignored + mov eax,[esi+FloatData.exponent] + mov [edi+FloatData.exponent],ecx + sub ecx,eax + jz copy_float_data + js contrary_unnormalization + mov ebx,ecx + shr ebx,5 + and cl,11111b + add edi,FloatData.mantissa + (MANTISSA_SEGMENTS-1)*4 + std + mov edx,MANTISSA_SEGMENTS + sub edx,ebx + push ebx + mov ebx,edx + mov edx,MANTISSA_SEGMENTS + mov eax,[mantissa_tail] + collect_far_mantissa: + cmp edx,ebx + je far_mantissa_collected + or [mantissa_tail],eax + mov eax,[esi+FloatData.mantissa+(edx-1)*4] + dec edx + jz unnormalization_underflow + jmp collect_far_mantissa + far_mantissa_collected: + mov ebx,[esi+FloatData.mantissa+(edx-1)*4] + shrd eax,ebx,cl + xchg eax,[mantissa_tail] + test eax,eax + setnz al + and eax,1 + or [mantissa_tail],eax + dec edx + jz shift_first_mantissa_segment_down + shift_mantissa_segment_down: + mov eax,ebx + mov ebx,[esi+FloatData.mantissa+(edx-1)*4] + shrd eax,ebx,cl + stosd + dec edx + jnz shift_mantissa_segment_down + shift_first_mantissa_segment_down: + mov eax,[esi+FloatData.mantissa] + shr eax,cl + stosd + pop ecx + zero_higher_mantissa_segments: + xor eax,eax + rep stosd + sub edi,FloatData.mantissa-4 + cld + mov eax,[esi+FloatData.attributes] + mov [edi+FloatData.attributes],eax + retn + unnormalization_underflow: + cmp edx,ebx + je only_mantissa_tail + or [mantissa_tail],eax + xor eax,eax + only_mantissa_tail: + shr eax,cl + xchg eax,[mantissa_tail] + test eax,eax + setnz al + and eax,1 + or [mantissa_tail],eax + pop ebx + mov ecx,MANTISSA_SEGMENTS + jmp zero_higher_mantissa_segments + contrary_unnormalization: + add edi,FloatData.mantissa + neg ecx + mov edx,ecx + shr edx,5 + cmp edx,MANTISSA_SEGMENTS + ja unnormalization_overflow + and cl,11111b + jmp shift_mantissa_up + unnormalization_overflow: + mov ecx,MANTISSA_SEGMENTS + xor eax,eax + rep stosd + sub edi,FloatData.mantissa + MANTISSA_SEGMENTS*4 + retn + +round_float: +; in: +; edi - FloatData +; [mantissa_tail] = additional bits of mantissa, lowest bit indicating that there are set bits far down +; preserves: ebx, esi, edi + test [mantissa_tail],1 shl 31 + jz rounding_done + test [mantissa_tail],not (1 shl 31) + jnz round_up + test [edi+FloatData.mantissa+(MANTISSA_SEGMENTS-1)*4],1 + jz rounding_done + round_up: + mov edx,MANTISSA_SEGMENTS + increment_mantissa: + inc [edi+FloatData.mantissa+(edx-1)*4] + jnz rounding_done + dec edx + jnz increment_mantissa + or [edi+FloatData.mantissa],80000000h + add [edi+FloatData.exponent],1 + jo float_overflow + rounding_done: + retn + float_overflow: + or [edi+FloatData.attributes],FLOAT_INFINITE + retn + +add_floats: +; in: +; ebx - FloatData with first addend +; esi - FloatData with second addend +; edi - FloatData where sum should be stored +; preserves: edi + test [ebx+FloatData.attributes],FLOAT_NEGATIVE + setnz [first_sign] + test [esi+FloatData.attributes],FLOAT_NEGATIVE + setnz [second_sign] + float_summator: + call get_float_exponent + mov ecx,eax + xchg esi,ebx + call get_float_exponent + cmp eax,ecx + jge significant_mantissa_selected + xchg esi,ebx + mov al,[first_sign] + xchg al,[second_sign] + mov [first_sign],al + significant_mantissa_selected: + push edi + mov eax,[esi+FloatData.mantissa] + test eax,eax + js align_second_mantissa + push ebx + mov edi,[temporary_floats] + and [mantissa_tail],0 + call normalize_float + mov esi,edi + pop ebx + align_second_mantissa: + push esi + and [mantissa_tail],0 + mov ecx,[esi+FloatData.exponent] + mov esi,ebx + mov edi,[temporary_floats] + add edi,sizeof.FloatData + call unnormalize_float + mov ebx,edi + pop esi + pop edi + mov eax,[esi+FloatData.attributes] + or eax,[ebx+FloatData.attributes] + and eax,FLOAT_INFINITE or FLOAT_INDETERMINATE + mov [edi+FloatData.attributes],eax + mov ecx,[esi+FloatData.exponent] + mov [edi+FloatData.exponent],ecx + mov al,[first_sign] + xor al,[second_sign] + jnz opposite_mantissa + mov ecx,MANTISSA_SEGMENTS + clc + add_mantissa_segments: + mov eax,[esi+FloatData.mantissa+(ecx-1)*4] + adc eax,[ebx+FloatData.mantissa+(ecx-1)*4] + mov [edi+FloatData.mantissa+(ecx-1)*4],eax + loop add_mantissa_segments + jnc sum_value_ready + mov ecx,[edi+FloatData.exponent] + add ecx,1 + jo float_overflow + mov esi,edi + call unnormalize_float + or [edi+FloatData.mantissa],80000000h + jmp sum_value_ready + opposite_mantissa: + xor ecx,ecx + compare_mantissa_segments: + mov eax,[esi+FloatData.mantissa+ecx*4] + cmp eax,[ebx+FloatData.mantissa+ecx*4] + ja keep_mantissa_sources + jb switch_mantissa_sources + inc ecx + cmp ecx,MANTISSA_SEGMENTS + jne compare_mantissa_segments + switch_mantissa_sources: + xchg esi,ebx + mov al,[first_sign] + xchg al,[second_sign] + mov [first_sign],al + clc + jmp mantissa_subtraction_ready + keep_mantissa_sources: + neg [mantissa_tail] + mantissa_subtraction_ready: + mov ecx,MANTISSA_SEGMENTS + subtract_mantissa_segments: + mov eax,[esi+FloatData.mantissa+(ecx-1)*4] + sbb eax,[ebx+FloatData.mantissa+(ecx-1)*4] + mov [edi+FloatData.mantissa+(ecx-1)*4],eax + loop subtract_mantissa_segments + mov esi,edi + call normalize_float + sum_value_ready: + xor eax,eax + cmp [first_sign],0 + je round_float + or [edi+FloatData.attributes],FLOAT_NEGATIVE + jmp round_float + +subtract_floats: +; in: +; ebx - FloatData with minuend +; esi - FloatData with subtrahend +; edi - FloatData where difference should be stored +; preserves: edi + test [ebx+FloatData.attributes],FLOAT_NEGATIVE + setnz [first_sign] + test [esi+FloatData.attributes],FLOAT_NEGATIVE + setz [second_sign] + jmp float_summator + +multiply_float_by_unsigned_int: +; in: +; esi - source FloatData +; ecx = unsigned multiplier +; edi - FloatData where multipled value should be stored +; preserves: edi + mov [multiplier],ecx + cmp esi,edi + je multiply_mantissa + mov eax,[esi+FloatData.attributes] + mov edx,[esi+FloatData.exponent] + mov [edi+FloatData.attributes],eax + mov [edi+FloatData.exponent],edx + multiply_mantissa: + xor edx,edx + mov [mantissa_tail],edx + mov ecx,MANTISSA_SEGMENTS + multiply_mantissa_segments: + mov ebx,edx + mov eax,[esi+FloatData.mantissa+(ecx-1)*4] + mul [multiplier] + add eax,ebx + mov [edi+FloatData.mantissa+(ecx-1)*4],eax + loop multiply_mantissa_segments + mov esi,edi + bsr ecx,edx + jz normalize_float + inc ecx + mov eax,ecx + neg cl + add cl,32 + shl edx,cl + push edx + mov ecx,eax + add ecx,[esi+FloatData.exponent] + jo float_overflow + call unnormalize_float + pop eax + or [edi+FloatData.mantissa],eax + jmp round_float + +multiply_floats: +; in: +; ebx - FloatData with first factor +; esi - FloatData with second factor +; edi - FloatData where product should be stored +; preserves: edi + test [esi+FloatData.attributes],FLOAT_INFINITE or FLOAT_INDETERMINATE + jnz first_factor_not_zero + call get_float_exponent + jz multiply_zero_float + first_factor_not_zero: + xchg esi,ebx + test [esi+FloatData.attributes],FLOAT_INFINITE or FLOAT_INDETERMINATE + jnz second_factor_not_zero + call get_float_exponent + jz multiply_zero_float + second_factor_not_zero: + mov eax,[ebx+FloatData.attributes] + mov edx,[esi+FloatData.attributes] + xor eax,edx + and edx,FLOAT_INFINITE or FLOAT_INDETERMINATE + or eax,edx + mov [edi+FloatData.attributes],eax + mov eax,[ebx+FloatData.exponent] + add eax,[esi+FloatData.exponent] + jo exponent_overflow + inc eax + jo exponent_overflow + mov [edi+FloatData.exponent],eax + push edi + mov edi,accumulator + xor eax,eax + mov ecx,3 + rep stosd + mov [mantissa_tail],eax + mov ecx,MANTISSA_SEGMENTS-1 + mov edi,ecx + multiply_low_segments: + or [mantissa_tail],eax + mov eax,[ebx+FloatData.mantissa+ecx*4] + mul [esi+FloatData.mantissa+edi*4] + add [accumulator],eax + adc [accumulator+4],edx + adc [accumulator+8],0 + dec ecx + inc edi + cmp edi,MANTISSA_SEGMENTS + jb multiply_low_segments + dec edi + xchg ecx,edi + xor eax,eax + xchg eax,[accumulator+8] + xchg eax,[accumulator+4] + xchg eax,[accumulator] + cmp edi,0 + jge multiply_low_segments + xchg eax,[mantissa_tail] + test eax,eax + setnz al + and eax,1 + or [mantissa_tail],eax + dec ecx + inc edi + multiply_high_segments: + mov eax,[ebx+FloatData.mantissa+ecx*4] + mul [esi+FloatData.mantissa+edi*4] + add [accumulator],eax + adc [accumulator+4],edx + adc [accumulator+8],0 + dec ecx + inc edi + cmp ecx,0 + jge multiply_high_segments + inc ecx + xchg ecx,edi + xor eax,eax + xchg eax,[accumulator+8] + xchg eax,[accumulator+4] + xchg eax,[accumulator] + mov edx,[esp] + mov [edx+FloatData.mantissa+ecx*4],eax + sub ecx,2 + ja multiply_high_segments + mov eax,[ebx+FloatData.mantissa] + mul [esi+FloatData.mantissa] + add eax,[accumulator] + adc edx,[accumulator+4] + pop edi + mov [edi+FloatData.mantissa+4],eax + mov [edi+FloatData.mantissa],edx + mov esi,edi + call normalize_float + jmp round_float + multiply_zero_float: + mov eax,[ebx+FloatData.attributes] + test eax,FLOAT_INFINITE or FLOAT_INDETERMINATE + jnz indeterminate_product + mov edx,[esi+FloatData.attributes] + and edx,FLOAT_NEGATIVE + xor eax,edx + mov [edi+FloatData.attributes],eax + xor eax,eax + zero_float_result: + mov [edi+FloatData.exponent],eax + xor eax,eax + add edi,FloatData.mantissa + mov ecx,MANTISSA_SEGMENTS + rep stosd + sub edi,FloatData.mantissa + MANTISSA_SEGMENTS*4 + retn + indeterminate_product: + or [edi+FloatData.attributes],FLOAT_INDETERMINATE + retn + exponent_overflow: + js float_overflow + mov [edi+FloatData.attributes],FLOAT_UNDERFLOW + jmp zero_float_result + +multiply_float_by_power_of_ten: +; in: +; esi - FloatData with number to multiply +; ecx = signed number representing exponent +; edi - FloatData for the result +; preserves: edi + call get_float_exponent + jz copy_float_data + cmp ecx,0 + je copy_float_data + jl negative_power_of_ten + mov [exponent],ecx + mov ebx,edi + mov edi,[temporary_floats] + xor eax,eax + mov [edi+FloatData.attributes],eax + mov al,3 + mov [edi+FloatData.exponent],eax + add edi,FloatData.mantissa + mov eax,0A0000000h + stosd + xor eax,eax + mov ecx,MANTISSA_SEGMENTS-1 + rep stosd + mov edi,ebx + get_power_bit: + shr [exponent],1 + jnc square_multiplier + mov ebx,[temporary_floats] + call multiply_floats + test [edi+FloatData.attributes],FLOAT_UNDERFLOW + jnz power_exhausted + square_multiplier: + cmp [exponent],0 + je power_exhausted + push edi + mov ebx,[temporary_floats] + mov esi,ebx + mov edi,ebx + call multiply_floats + test [edi+FloatData.attributes],FLOAT_UNDERFLOW + jnz power_of_ten_underflow + pop edi + mov esi,edi + jmp get_power_bit + power_exhausted: + retn + negative_power_of_ten: + neg ecx + mov [exponent],ecx + mov ebx,edi + mov edi,[temporary_floats] + xor eax,eax + mov [edi+FloatData.attributes],eax + or eax,-4 + mov [edi+FloatData.exponent],eax + add edi,FloatData.mantissa + mov eax,0CCCCCCCCh + mov ecx,MANTISSA_SEGMENTS + dec ecx + rep stosd + inc eax + stosd + mov edi,ebx + jmp get_power_bit + power_of_ten_underflow: + pop edi + or [edi+FloatData.attributes],FLOAT_UNDERFLOW + retn + +multiplicative_inverse: +; in: +; esi - source FloatData +; edi - destination FloatData +; preserves: edi + and [mantissa_tail],0 + call normalize_float + mov eax,[edi+FloatData.attributes] + test eax,FLOAT_INFINITE + jnz reverse_of_infinity + mov ecx,[edi+FloatData.exponent] + cmp ecx,-80000000h + je float_overflow + and [edi+FloatData.attributes],0 + or [edi+FloatData.exponent],-1 + push eax ecx edi + mov [iterations],3 + bsr (MANTISSA_SEGMENTS+1) + mov ebx,edi + mov edi,[temporary_floats] + add edi,2*sizeof.FloatData + xor eax,eax + mov [edi+FloatData.attributes],eax + inc eax + mov [edi+FloatData.exponent],eax + add edi,FloatData.mantissa + mov ecx,MANTISSA_SEGMENTS + mov eax,0B4B4B4B4h + rep stosd + assert FloatData.mantissa + MANTISSA_SEGMENTS*4 = sizeof.FloatData + xor eax,eax + mov [edi+FloatData.attributes],eax + mov [edi+FloatData.exponent],eax + add edi,FloatData.mantissa + mov ecx,MANTISSA_SEGMENTS + mov eax,0F0F0F0F0h + rep stosd + sub edi,FloatData.mantissa + MANTISSA_SEGMENTS*4 + mov esi,edi + call multiply_floats + lea ebx,[edi-sizeof.FloatData] + mov esi,edi + mov edi,ebx + newton_raphson_iteration: + call subtract_floats + mov ebx,edi + mov esi,edi + add edi,sizeof.FloatData + call multiply_floats + mov ebx,[esp] + mov esi,edi + call multiply_floats + mov esi,edi + sub edi,sizeof.FloatData + inc [edi+FloatData.exponent] + mov ebx,edi + dec [iterations] + jnz newton_raphson_iteration + pop edi + call subtract_floats + pop ecx eax + not ecx + add [edi+FloatData.exponent],ecx + and [edi+FloatData.attributes],FLOAT_INFINITE or FLOAT_INDETERMINATE + or [edi+FloatData.attributes],eax + retn + reverse_of_infinity: + and eax,FLOAT_NEGATIVE or FLOAT_INDETERMINATE + mov [edi+FloatData.attributes],eax + xor eax,eax + jmp zero_float_result + +divide_floats: +; in: +; ebx - FloatData with dividend +; esi - FloatData with divisor +; edi - FloatData where quotient should be stored +; preserves: edi + push ebx + call multiplicative_inverse + pop ebx + mov esi,edi + call multiply_floats + retn + +fit_float: +; in: +; esi - FloatData +; ecx = number of bytes that the value has to fit into +; edi - buffer for the specified amount of bytes +; preserves: esi, edi + cmp ecx,2 + je half_precision + cmp ecx,4 + je single_precision + cmp ecx,8 + je double_precision + cmp ecx,10 + je extended_precision + cmp ecx,16 + je quadruple_precision + mov edx,_invalid_value + call register_error + retn + float_out_of_range: + mov edx,_value_out_of_range + call register_error + retn + zero_float: + dec ecx + xor al,al + rep stosb + test [esi+FloatData.attributes],FLOAT_NEGATIVE + setnz al + ror al,1 + stosb + retn + half_precision: + call get_float_exponent + jz zero_float + mov ebx,[esi+FloatData.exponent] + sub ebx,eax + add eax,0Fh + js half_precision_denormal + je half_precision_denormal + cmp eax,1Fh + jae float_out_of_range + mov ecx,10 + shl eax,cl + mov [edi],ax + inc ebx + call read_mantissa_bits + or [edi],ax + call get_mantissa_rounding_increment + add [edi],ax + mov dx,1Fh shl 10 + mov ax,dx + and ax,[edi] + cmp ax,dx + je float_out_of_range + half_precision_ok: + test [esi+FloatData.attributes],FLOAT_NEGATIVE + setnz al + shl ax,15 + or [edi],ax + retn + half_precision_denormal: + neg eax + mov ecx,10 + sub ecx,eax + jc float_out_of_range + call read_mantissa_bits + mov [edi],ax + call get_mantissa_rounding_increment + add [edi],ax + jz float_out_of_range + jmp half_precision_ok + single_precision: + call get_float_exponent + jz zero_float + mov ebx,[esi+FloatData.exponent] + sub ebx,eax + add eax,7Fh + js single_precision_denormal + je single_precision_denormal + cmp eax,0FFh + jae float_out_of_range + mov ecx,23 + shl eax,cl + mov [edi],eax + inc ebx + call read_mantissa_bits + or [edi],eax + call get_mantissa_rounding_increment + add [edi],eax + mov edx,0FFh shl 23 + mov eax,edx + and eax,[edi] + cmp eax,edx + je float_out_of_range + single_precision_ok: + test [esi+FloatData.attributes],FLOAT_NEGATIVE + setnz al + shl eax,31 + or [edi],eax + retn + single_precision_denormal: + neg eax + mov ecx,23 + sub ecx,eax + jc float_out_of_range + call read_mantissa_bits + mov [edi],eax + call get_mantissa_rounding_increment + add [edi],eax + jz float_out_of_range + jmp single_precision_ok + double_precision: + call get_float_exponent + jz zero_float + mov ebx,[esi+FloatData.exponent] + sub ebx,eax + add eax,3FFh + js double_precision_denormal + je double_precision_denormal + cmp eax,7FFh + jae float_out_of_range + mov ecx,20 + shl eax,cl + mov [edi+4],eax + inc ebx + call read_mantissa_bits + or [edi+4],eax + mov ecx,32 + call read_mantissa_bits + mov [edi],eax + call get_mantissa_rounding_increment + xor edx,edx + add [edi],eax + adc [edi+4],edx + mov edx,7FFh shl 20 + mov eax,edx + and eax,[edi+4] + cmp eax,edx + je float_out_of_range + double_precision_ok: + test [esi+FloatData.attributes],FLOAT_NEGATIVE + setnz al + shl eax,31 + or [edi+4],eax + retn + double_precision_denormal: + neg eax + mov ecx,52 + sub ecx,eax + jc float_out_of_range + xor eax,eax + cmp ecx,32 + jbe double_precision_denormal_high_segment_ready + sub ecx,32 + call read_mantissa_bits + mov ecx,32 + double_precision_denormal_high_segment_ready: + mov [edi+4],eax + call read_mantissa_bits + mov [edi],eax + call get_mantissa_rounding_increment + xor edx,edx + add [edi],eax + jc double_precision_denormal_carry + jnz double_precision_ok + cmp [edi+4],edx + je float_out_of_range + jmp double_precision_ok + double_precision_denormal_carry: + adc [edi+4],edx + jmp double_precision_ok + extended_precision: + call get_float_exponent + jz zero_float + mov ebx,[esi+FloatData.exponent] + sub ebx,eax + add eax,3FFFh + js extended_precision_denormal + jz extended_precision_denormal + cmp eax,7FFFh + jae float_out_of_range + mov [edi+8],ax + mov ecx,32 + call read_mantissa_bits + mov [edi+4],eax + mov ecx,32 + call read_mantissa_bits + mov [edi],eax + call get_mantissa_rounding_increment + xor edx,edx + add [edi],eax + adc [edi+4],edx + jnc extended_precision_ok + or byte [edi+7],80h + inc word [edi+8] + cmp word [edi+8],7FFFh + je float_out_of_range + extended_precision_ok: + test [esi+FloatData.attributes],FLOAT_NEGATIVE + setnz al + shl ax,15 + or [edi+8],ax + retn + extended_precision_denormal: + and word [edi+8],0 + neg eax + mov ecx,63 + sub ecx,eax + jc float_out_of_range + xor eax,eax + cmp ecx,32 + jbe extended_precision_denormal_high_segment_ready + sub ecx,32 + call read_mantissa_bits + mov ecx,32 + extended_precision_denormal_high_segment_ready: + mov [edi+4],eax + call read_mantissa_bits + mov [edi],eax + call get_mantissa_rounding_increment + xor edx,edx + add [edi],eax + jc extended_precision_denormal_carry + jnz extended_precision_ok + cmp [edi+4],edx + je float_out_of_range + jmp extended_precision_ok + extended_precision_denormal_carry: + adc [edi+4],edx + jns extended_precision_ok + inc byte [edi+8] + jmp extended_precision_ok + quadruple_precision: + call get_float_exponent + jz zero_float + mov ebx,[esi+FloatData.exponent] + sub ebx,eax + add eax,3FFFh + js quadruple_precision_denormal + je quadruple_precision_denormal + cmp eax,7FFFh + jae float_out_of_range + mov ecx,16 + shl eax,cl + mov [edi+12],eax + inc ebx + call read_mantissa_bits + or [edi+12],eax + mov ecx,32 + call read_mantissa_bits + mov [edi+8],eax + mov ecx,32 + call read_mantissa_bits + mov [edi+4],eax + mov ecx,32 + call read_mantissa_bits + mov [edi],eax + call get_mantissa_rounding_increment + xor edx,edx + add [edi],eax + adc [edi+4],edx + adc [edi+8],edx + adc [edi+12],edx + mov edx,7FFFh shl 16 + mov eax,edx + and eax,[edi+12] + cmp eax,edx + je float_out_of_range + quadruple_precision_ok: + test [esi+FloatData.attributes],FLOAT_NEGATIVE + setnz al + shl eax,31 + or [edi+12],eax + retn + quadruple_precision_denormal: + neg eax + mov ecx,112 + sub ecx,eax + jc float_out_of_range + xor eax,eax + sub ecx,96 + jbe quadruple_precision_denormal_first_segment_ready + call read_mantissa_bits + xor ecx,ecx + quadruple_precision_denormal_first_segment_ready: + mov [edi+12],eax + add ecx,96 + xor eax,eax + sub ecx,64 + jbe quadruple_precision_denormal_second_segment_ready + call read_mantissa_bits + xor ecx,ecx + quadruple_precision_denormal_second_segment_ready: + mov [edi+8],eax + add ecx,64 + xor eax,eax + sub ecx,32 + jbe quadruple_precision_denormal_third_segment_ready + call read_mantissa_bits + xor ecx,ecx + quadruple_precision_denormal_third_segment_ready: + mov [edi+4],eax + add ecx,32 + call read_mantissa_bits + mov [edi],eax + call get_mantissa_rounding_increment + xor edx,edx + add [edi],eax + jc quadruple_precision_denormal_carry + jnz quadruple_precision_ok + or edx,[edi+4] + or edx,[edi+8] + or edx,[edi+12] + jz float_out_of_range + jmp quadruple_precision_ok + quadruple_precision_denormal_carry: + adc [edi+4],edx + adc [edi+8],edx + adc [edi+12],edx + jmp quadruple_precision_ok + read_mantissa_bits: + add ebx,ecx + mov edx,ebx + dec edx + mov al,dl + shr edx,5 + and al,11111b + inc al + mov ch,cl + cmp edx,MANTISSA_SEGMENTS + ja out_of_mantissa_bits + je read_from_mantissa_border + mov cl,32 + sub cl,al + cmp al,ch + jae read_from_single_mantissa_segment + mov eax,[esi+FloatData.mantissa+edx*4] + mov edx,[esi+FloatData.mantissa+(edx-1)*4] + shrd eax,edx,cl + jmp cut_out_mantissa_bits + read_from_single_mantissa_segment: + mov eax,[esi+FloatData.mantissa+edx*4] + shr eax,cl + cut_out_mantissa_bits: + mov cl,ch + cmp cl,32 + je mantissa_bits_ok + mov edx,1 + shl edx,cl + dec edx + and eax,edx + mantissa_bits_ok: + retn + read_from_mantissa_border: + cmp al,32 + jae out_of_mantissa_bits + mov cl,al + mov eax,[esi+FloatData.mantissa+(edx-1)*4] + shl eax,cl + jmp cut_out_mantissa_bits + out_of_mantissa_bits: + xor eax,eax + retn + get_mantissa_rounding_increment: + mov al,bl + shr ebx,5 + and al,11111b + mov cl,31 + sub cl,al + cmp ebx,MANTISSA_SEGMENTS + jae no_rounding_increment + mov edx,1 + shl edx,cl + mov eax,[esi+FloatData.mantissa+ebx*4] + test eax,edx + jz no_rounding_increment + dec edx + and eax,edx + jnz rounding_increment_needed + mov edx,ebx + check_whole_mantissa: + inc edx + cmp edx,MANTISSA_SEGMENTS + jae rounding_tied + cmp dword [esi+FloatData.mantissa+edx*4],0 + je check_whole_mantissa + rounding_increment_needed: + mov eax,1 + retn + rounding_tied: + inc cl + cmp cl,32 + jb test_mantissa_parity + dec ebx + test_mantissa_parity: + mov eax,[esi+FloatData.mantissa+ebx*4] + shr eax,cl + test eax,1 + jnz rounding_increment_needed + no_rounding_increment: + xor eax,eax + retn diff --git a/x86_64_sse2_x87/fasm/source/libc/fasmg.asm b/x86_64_sse2_x87/fasm/source/libc/fasmg.asm new file mode 100644 index 0000000..29d348d --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/libc/fasmg.asm @@ -0,0 +1,499 @@ + +match ,{ + + include 'macro/struct.inc' + include '../linux/import32.inc' + +} match -,{ +else + + include 'selfhost.inc' + +end match +_ equ } + + format ELF + public main + +include '../version.inc' + +extrn 'malloc' as libc.malloc +extrn 'realloc' as libc.realloc +extrn 'free' as libc.free +extrn 'fopen' as libc.fopen +extrn 'fclose' as libc.fclose +extrn 'fread' as libc.fread +extrn 'fwrite' as libc.fwrite +extrn 'fseek' as libc.fseek +extrn 'ftell' as libc.ftell +extrn 'time' as libc.time +extrn 'write' as libc.write + +extrn getenv +extrn gettimeofday +extrn exit + +struct timeval + time_t dd ? + suseconds_t dd ? +ends + +section '.text' executable align 16 + + main: + mov ecx,[esp+4] + mov [argc],ecx + mov ebx,[esp+8] + mov [argv],ebx + + call system_init + + call get_arguments + mov bl,al + cmp [no_logo],0 + jne logo_ok + mov esi,_logo + xor ecx,ecx + call display_string + logo_ok: + test bl,bl + jnz display_usage_information + + xor al,al + mov ecx,[verbosity_level] + jecxz init + or al,TRACE_ERROR_STACK + dec ecx + jz init + or al,TRACE_DISPLAY + init: + call assembly_init + + ccall gettimeofday,start_time,0 + + assemble: + mov esi,[initial_commands] + mov edx,[source_path] + call assembly_pass + jc assembly_done + + mov eax,[current_pass] + cmp eax,[maximum_number_of_passes] + jb assemble + + call show_display_data + + mov esi,_error_prefix + xor ecx,ecx + call display_error_string + mov esi,_code_cannot_be_generated + xor ecx,ecx + call display_error_string + mov esi,_message_suffix + xor ecx,ecx + call display_error_string + + jmp assembly_failed + + assembly_done: + + call show_display_data + + cmp [first_error],0 + jne assembly_failed + + cmp [no_logo],0 + jne summary_done + mov eax,[current_pass] + xor edx,edx + call itoa + call display_string + mov esi,_passes + cmp [current_pass],1 + jne display_passes_suffix + mov esi,_pass + display_passes_suffix: + xor ecx,ecx + call display_string + ccall gettimeofday,end_time,0 + mov eax,[end_time.time_t] + sub eax,[start_time.time_t] + mov ecx,1000000 + mul ecx + add eax,[end_time.suseconds_t] + adc edx,0 + sub eax,[start_time.suseconds_t] + sbb edx,0 + add eax,50000 + mov ecx,1000000 + div ecx + mov ebx,eax + mov eax,edx + xor edx,edx + mov ecx,100000 + div ecx + mov [tenths_of_second],eax + xchg eax,ebx + or ebx,eax + jz display_output_length + xor edx,edx + call itoa + call display_string + mov esi,_message_suffix + mov ecx,1 + call display_string + mov eax,[tenths_of_second] + xor edx,edx + call itoa + call display_string + mov esi,_seconds + xor ecx,ecx + call display_string + display_output_length: + call get_output_length + push eax edx + call itoa + call display_string + pop edx eax + mov esi,_bytes + cmp eax,1 + jne display_bytes_suffix + test edx,edx + jnz display_bytes_suffix + mov esi,_byte + display_bytes_suffix: + xor ecx,ecx + call display_string + mov esi,_new_line + xor ecx,ecx + call display_string + summary_done: + + mov ebx,[source_path] + mov edi,[output_path] + call write_output_file + jc write_failed + + call assembly_shutdown + call system_shutdown + + ccall exit,0 + + assembly_failed: + + call show_errors + + call assembly_shutdown + call system_shutdown + + ccall exit,2 + + write_failed: + mov ebx,_write_failed + jmp fatal_error + + out_of_memory: + mov ebx,_out_of_memory + jmp fatal_error + + fatal_error: + + mov esi,_error_prefix + xor ecx,ecx + call display_error_string + mov esi,ebx + xor ecx,ecx + call display_error_string + mov esi,_message_suffix + xor ecx,ecx + call display_error_string + + call assembly_shutdown + call system_shutdown + + ccall exit,3 + + display_usage_information: + + mov esi,_usage + xor ecx,ecx + call display_string + + call system_shutdown + + ccall exit,1 + + get_arguments: + xor eax,eax + mov [initial_commands],eax + mov [source_path],eax + mov [output_path],eax + mov [no_logo],al + mov [verbosity_level],eax + mov [maximum_number_of_passes],100 + mov [maximum_number_of_errors],1 + mov [maximum_depth_of_stack],10000 + mov ecx,[argc] + mov ebx,[argv] + add ebx,4 + dec ecx + jz error_in_arguments + get_argument: + mov esi,[ebx] + mov al,[esi] + cmp al,'-' + je get_option + cmp [source_path],0 + jne get_output_file + mov [source_path],esi + jmp next_argument + get_output_file: + cmp [output_path],0 + jne error_in_arguments + mov [output_path],esi + jmp next_argument + get_option: + inc esi + lodsb + cmp al,'e' + je set_errors_limit + cmp al,'E' + je set_errors_limit + cmp al,'i' + je insert_initial_command + cmp al,'I' + je insert_initial_command + cmp al,'p' + je set_passes_limit + cmp al,'P' + je set_passes_limit + cmp al,'r' + je set_recursion_limit + cmp al,'R' + je set_recursion_limit + cmp al,'v' + je set_verbose_mode + cmp al,'V' + je set_verbose_mode + cmp al,'n' + je set_no_logo + cmp al,'N' + jne error_in_arguments + set_no_logo: + or [no_logo],-1 + cmp byte [esi],0 + je next_argument + error_in_arguments: + or al,-1 + ret + set_verbose_mode: + cmp byte [esi],0 + jne get_verbose_setting + dec ecx + jz error_in_arguments + add ebx,4 + mov esi,[ebx] + get_verbose_setting: + call get_option_value + cmp edx,2 + ja error_in_arguments + mov [verbosity_level],edx + jmp next_argument + set_errors_limit: + cmp byte [esi],0 + jne get_errors_setting + dec ecx + jz error_in_arguments + add ebx,4 + mov esi,[ebx] + get_errors_setting: + call get_option_value + test edx,edx + jz error_in_arguments + mov [maximum_number_of_errors],edx + jmp next_argument + set_recursion_limit: + cmp byte [esi],0 + jne get_recursion_setting + dec ecx + jz error_in_arguments + add ebx,4 + mov esi,[ebx] + get_recursion_setting: + call get_option_value + test edx,edx + jz error_in_arguments + mov [maximum_depth_of_stack],edx + jmp next_argument + set_passes_limit: + cmp byte [esi],0 + jne get_passes_setting + dec ecx + jz error_in_arguments + add ebx,4 + mov esi,[ebx] + get_passes_setting: + call get_option_value + test edx,edx + jz error_in_arguments + mov [maximum_number_of_passes],edx + next_argument: + add ebx,4 + dec ecx + jnz get_argument + cmp [source_path],0 + je error_in_arguments + xor al,al + ret + get_option_value: + xor eax,eax + mov edx,eax + find_option_value: + cmp byte [esi],20h + jne get_option_digit + inc esi + jmp find_option_value + get_option_digit: + lodsb + test al,al + jz option_value_ok + sub al,30h + jc invalid_option_value + cmp al,9 + ja invalid_option_value + imul edx,10 + jo invalid_option_value + add edx,eax + jc invalid_option_value + jmp get_option_digit + option_value_ok: + dec esi + clc + ret + invalid_option_value: + stc + ret + insert_initial_command: + cmp byte [esi],0 + jne measure_initial_command + dec ecx + jz error_in_arguments + add ebx,4 + mov esi,[ebx] + measure_initial_command: + push ebx ecx edi + mov edi,esi + or ecx,-1 + xor al,al + repne scasb + not ecx + dec ecx + mov edi,[initial_commands] + lea eax,[ecx+2] + test edi,edi + jz allocate_initial_commands_buffer + mov edx,[initial_commands_length] + add edi,edx + add eax,edx + cmp eax,[initial_commands_maximum_length] + ja grow_initial_commands_buffer + copy_initial_command: + rep movsb + mov ax,0Ah + stosw + dec edi + sub edi,[initial_commands] + mov [initial_commands_length],edi + pop edi ecx ebx + jmp next_argument + allocate_initial_commands_buffer: + push ecx + mov ecx,eax + call malloc + mov [initial_commands],eax + mov [initial_commands_maximum_length],ecx + mov edi,eax + pop ecx + jmp copy_initial_command + grow_initial_commands_buffer: + push ecx + mov ecx,eax + mov eax,[initial_commands] + call realloc + mov [initial_commands],eax + mov [initial_commands_maximum_length],ecx + mov edi,eax + add edi,[initial_commands_length] + pop ecx + jmp copy_initial_command + + include 'system.inc' + + include '../assembler.inc' + include '../symbols.inc' + include '../expressions.inc' + include '../conditions.inc' + include '../floats.inc' + include '../directives.inc' + include '../calm.inc' + include '../errors.inc' + include '../map.inc' + include '../reader.inc' + include '../output.inc' + include '../console.inc' + +section '.data' + + _logo db 'flat assembler version g.',VERSION,10,0 + + _usage db 'Usage: fasmg source [output]',10 + db 'Optional settings:',10 + db ' -p limit Set the maximum allowed number of passes (default 100)',10 + db ' -e limit Set the maximum number of displayed errors (default 1)',10 + db ' -r limit Set the maximum depth of stack (default 10000)',10 + db ' -v flag Enable or disable showing all lines from the stack (default 0)',10 + db ' -i command Insert instruction at the beginning of source',13,10 + db ' -n Do not show logo nor summary',13,10 + db 0 + + _pass db ' pass, ',0 + _passes db ' passes, ',0 + _dot db '.' + _seconds db ' seconds, ',0 + _byte db ' byte.',0 + _bytes db ' bytes.',0 + + _write_failed db 'failed to write the output file',0 + _out_of_memory db 'not enough memory to complete the assembly',0 + _code_cannot_be_generated db 'could not generate code within the allowed number of passes',0 + + _open_mode db 'r',0 + _create_mode db 'w',0 + + include '../tables.inc' + include '../messages.inc' + +section '.bss' writeable align 4 + + include '../variables.inc' + + source_path dd ? + output_path dd ? + maximum_number_of_passes dd ? + + initial_commands dd ? + initial_commands_length dd ? + initial_commands_maximum_length dd ? + + argc dd ? + argv dd ? + timestamp dq ? + + start_time timeval + end_time timeval + tenths_of_second dd ? + + verbosity_level dd ? + no_logo db ? + + path_buffer rb 1000h diff --git a/x86_64_sse2_x87/fasm/source/libc/selfhost.inc b/x86_64_sse2_x87/fasm/source/libc/selfhost.inc new file mode 100644 index 0000000..f64064e --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/libc/selfhost.inc @@ -0,0 +1,60 @@ + +include '../../examples/x86/include/80386.inc' + +macro format?.ELF? variant + match , variant + format binary as 'o' + include '../../examples/x86/include/format/elf32.inc' + use32 + else match =executable? settings, variant: + match brand =at? base:, settings + ELF.Settings.ABI = brand + ELF.Settings.BaseAddress = base + else match =at? base:, settings + ELF.Settings.BaseAddress = base + else match brand:, settings + ELF.Settings.ABI = brand + end match + include '../../examples/x86/include/format/elfexe.inc' + use32 + else + err 'invalid argument' + end match +end macro + + +macro struct? name + macro ends?! + end namespace + esc end struc + virtual at 0 + name name + sizeof.name = $ + end virtual + purge ends? + end macro + esc struc name + label . : sizeof.name + namespace . +end macro + +macro ccall? proc*,args& + local size + mov ebp,esp + if size + sub esp,size + end if + and esp,0FFFFFFF0h + match any, args + iterate arg, args + mov dword [esp+(%-1)*4],arg + if % = 1 + size := %%*4 + end if + end iterate + else + size := 0 + end match + call proc + mov esp,ebp +end macro diff --git a/x86_64_sse2_x87/fasm/source/libc/system.inc b/x86_64_sse2_x87/fasm/source/libc/system.inc new file mode 100644 index 0000000..00c833e --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/libc/system.inc @@ -0,0 +1,224 @@ + +LINE_FEED = 0Ah + +system_init: + ccall libc.time,timestamp + retn + +system_shutdown: + retn + +malloc: +malloc_fixed: +malloc_growable: +; in: ecx = requested size +; out: eax - allocated block, ecx = allocated size, on error jumps to out_of_memory (does not return) +; preserves: ebx, esi, edi +; note: +; use of malloc_fixed hints that block will be kept as is until the end of assembly +; use of malloc_growable hints that block is likely to be resized + push ebx ecx esi edi + ccall libc.malloc,ecx + pop edi esi ecx ebx + test eax,eax + jz out_of_memory + retn +realloc: +; in: eax - memory block, ecx = requested size +; out: eax - resized block, ecx = allocated size, on error jumps to out_of_memory (does not return) +; preserves: ebx, esi, edi + push ebx ecx esi edi + ccall libc.realloc,eax,ecx + pop edi esi ecx ebx + test eax,eax + jz out_of_memory + retn +mfree: +; in: eax - memory block +; out: cf set on error +; preserves: ebx, esi, edi +; note: eax may have value 0 or -1, it should be treated as invalid input then + test eax,eax + jz interface_error + cmp eax,-1 + je interface_error + push ebx esi edi + ccall libc.free,eax + pop edi esi ebx + clc + retn + interface_error: + stc + retn + +open: +; in: edx - path to file +; out: ebx = file handle, cf set on error +; preserves: esi, edi + push esi edi + call adapt_path + ccall libc.fopen,ebx,_open_mode + pop edi esi + test eax,eax + jz interface_error + mov ebx,eax + clc + retn + adapt_path: + xor ecx,ecx + mov ebx,path_buffer + copy_path: + mov al,[edx+ecx] + cmp al,'\' + jne path_char_ok + mov al,'/' + path_char_ok: + cmp ecx,1000h + jae out_of_memory + mov [ebx+ecx],al + inc ecx + test al,al + jnz copy_path + retn +create: +; in: edx - path to file +; out: ebx = file handle, cf set on error +; preserves: esi, edi + push esi edi + call adapt_path + ccall libc.fopen,ebx,_create_mode + pop edi esi + test eax,eax + jz interface_error + mov ebx,eax + clc + retn +write: +; in: ebx = file handle, edx - data, ecx = number of bytes +; out: cf set on error +; preserves: ebx, esi, edi + push ebx ecx esi edi + ccall libc.fwrite,edx,1,ecx,ebx + pop edi esi ecx ebx + cmp eax,ecx + jne interface_error + clc + ret +read: +; in: ebx = file handle, edx - buffer, ecx = number of bytes +; out: cf set on error +; preserves: ebx, esi, edi + push ebx ecx esi edi + ccall libc.fread,edx,1,ecx,ebx + pop edi esi ecx ebx + cmp eax,ecx + jne interface_error + clc + ret +close: +; in: ebx = file handle +; preserves: ebx, esi, edi + ccall libc.fclose,ebx + ret +lseek: +; in: ebx = file handle, cl = method, edx:eax = offset +; out: edx:eax = new offset from the beginning of file, cf set on error +; preserves: ebx, esi, edi + test edx,edx + jnz interface_error + push esi edi ebx + movzx ecx,cl + ccall libc.fseek,ebx,eax,ecx + test eax,eax + jnz lseek_error + mov ebx,[esp] + ccall libc.ftell,ebx + cmp eax,-1 + je lseek_error + xor edx,edx + pop ebx edi esi + clc + ret + lseek_error: + pop ebx edi esi + stc + ret + +get_timestamp: +; out: edx:eax = timestamp +; preserves: ebx, ecx, esi, edi + mov eax,dword [timestamp] + mov edx,dword [timestamp+4] + retn + +display_string: +; in: +; esi - string +; ecx = string length, zero for ASCIIZ string +; preserves: ebx, esi + push ebx esi + test ecx,ecx + jnz write_string_to_stdout + xor al,al + mov edi,esi + or ecx,-1 + repne scasb + neg ecx + sub ecx,2 + write_string_to_stdout: + ccall libc.write,1,esi,ecx + pop esi ebx + retn + +display_error_string: +; in: +; esi - string +; ecx = string length, zero for ASCIIZ string +; preserves: ebx, esi + push ebx esi + test ecx,ecx + jnz write_string_to_stderr + xor al,al + mov edi,esi + or ecx,-1 + repne scasb + neg ecx + sub ecx,2 + write_string_to_stderr: + ccall libc.write,2,esi,ecx + pop esi ebx + retn + +get_environment_variable: +; in: +; esi - name +; edi - buffer for value +; ecx = size of buffer +; out: +; eax = length of value +; preserves: ebx, esi, edi + push ebx ecx esi edi + ccall getenv,esi + pop edi esi ecx ebx + test eax,eax + jz no_environment_variable + push esi + mov esi,eax + xor eax,eax + copy_environment_variable: + mov dl,[esi+eax] + cmp eax,ecx + jae next_environment_variable_character + mov [edi+eax],dl + next_environment_variable_character: + inc eax + test dl,dl + jnz copy_environment_variable + pop esi + environment_variable_ok: + ret + no_environment_variable: + mov eax,1 + jecxz environment_variable_ok + and byte [edi],0 + ret diff --git a/x86_64_sse2_x87/fasm/source/linux/fasmg.asm b/x86_64_sse2_x87/fasm/source/linux/fasmg.asm new file mode 100644 index 0000000..778cb55 --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/linux/fasmg.asm @@ -0,0 +1,513 @@ + +match ,{ + + include 'macro/struct.inc' + include 'import32.inc' + +} match -,{ +else + + include 'selfhost.inc' + +end match +_ equ } + +format ELF executable 3 +entry start + +include '../version.inc' + +interpreter '/lib/ld-linux.so.2' +needed 'libc.so.6' +import libc.malloc,'malloc',\ + libc.calloc,'calloc',\ + libc.realloc,'realloc',\ + libc.free,'free' + +struct timeval + time_t dd ? + suseconds_t dd ? +ends + +segment readable executable + + start: + + mov ecx,[esp] + mov [argc],ecx + lea ebx,[esp+4] + mov [argv],ebx + lea esi,[esp+4+ecx*4+4] + mov [env],esi + + call system_init + + call get_arguments + mov bl,al + cmp [no_logo],0 + jne logo_ok + mov esi,_logo + xor ecx,ecx + call display_string + logo_ok: + test bl,bl + jnz display_usage_information + + xor al,al + mov ecx,[verbosity_level] + jecxz init + or al,TRACE_ERROR_STACK + dec ecx + jz init + or al,TRACE_DISPLAY + init: + call assembly_init + + mov eax,78 ; sys_gettimeofday + mov ebx,start_time + xor ecx,ecx + int 0x80 + + assemble: + mov esi,[initial_commands] + mov edx,[source_path] + call assembly_pass + jc assembly_done + + mov eax,[current_pass] + cmp eax,[maximum_number_of_passes] + jb assemble + + call show_display_data + + mov esi,_error_prefix + xor ecx,ecx + call display_error_string + mov esi,_code_cannot_be_generated + xor ecx,ecx + call display_error_string + mov esi,_message_suffix + xor ecx,ecx + call display_error_string + + jmp assembly_failed + + assembly_done: + + call show_display_data + + cmp [first_error],0 + jne assembly_failed + + cmp [no_logo],0 + jne summary_done + mov eax,[current_pass] + xor edx,edx + call itoa + call display_string + mov esi,_passes + cmp [current_pass],1 + jne display_passes_suffix + mov esi,_pass + display_passes_suffix: + xor ecx,ecx + call display_string + mov eax,78 ; sys_gettimeofday + mov ebx,end_time + xor ecx,ecx + int 0x80 + mov eax,[end_time.time_t] + sub eax,[start_time.time_t] + mov ecx,1000000 + mul ecx + add eax,[end_time.suseconds_t] + adc edx,0 + sub eax,[start_time.suseconds_t] + sbb edx,0 + add eax,50000 + mov ecx,1000000 + div ecx + mov ebx,eax + mov eax,edx + xor edx,edx + mov ecx,100000 + div ecx + mov [tenths_of_second],eax + xchg eax,ebx + or ebx,eax + jz display_output_length + xor edx,edx + call itoa + call display_string + mov esi,_message_suffix + mov ecx,1 + call display_string + mov eax,[tenths_of_second] + xor edx,edx + call itoa + call display_string + mov esi,_seconds + xor ecx,ecx + call display_string + display_output_length: + call get_output_length + push eax edx + call itoa + call display_string + pop edx eax + mov esi,_bytes + cmp eax,1 + jne display_bytes_suffix + test edx,edx + jnz display_bytes_suffix + mov esi,_byte + display_bytes_suffix: + xor ecx,ecx + call display_string + mov esi,_new_line + xor ecx,ecx + call display_string + summary_done: + + mov ebx,[source_path] + mov edi,[output_path] + call write_output_file + jc write_failed + + call assembly_shutdown + call system_shutdown + + xor ebx,ebx + mov eax,1 ; sys_exit + int 0x80 + + assembly_failed: + + call show_errors + + call assembly_shutdown + call system_shutdown + + mov ebx,2 + mov eax,1 ; sys_exit + int 0x80 + + write_failed: + mov ebx,_write_failed + jmp fatal_error + + out_of_memory: + mov ebx,_out_of_memory + jmp fatal_error + + fatal_error: + + mov esi,_error_prefix + xor ecx,ecx + call display_error_string + mov esi,ebx + xor ecx,ecx + call display_error_string + mov esi,_message_suffix + xor ecx,ecx + call display_error_string + + call assembly_shutdown + call system_shutdown + + mov ebx,3 + mov eax,1 ; sys_exit + int 0x80 + + display_usage_information: + + mov esi,_usage + xor ecx,ecx + call display_string + + call system_shutdown + + mov ebx,1 + mov eax,1 ; sys_exit + int 0x80 + + get_arguments: + xor eax,eax + mov [initial_commands],eax + mov [source_path],eax + mov [output_path],eax + mov [no_logo],al + mov [verbosity_level],eax + mov [maximum_number_of_passes],100 + mov [maximum_number_of_errors],1 + mov [maximum_depth_of_stack],10000 + mov ecx,[argc] + mov ebx,[argv] + add ebx,4 + dec ecx + jz error_in_arguments + get_argument: + mov esi,[ebx] + mov al,[esi] + cmp al,'-' + je get_option + cmp [source_path],0 + jne get_output_file + mov [source_path],esi + jmp next_argument + get_output_file: + cmp [output_path],0 + jne error_in_arguments + mov [output_path],esi + jmp next_argument + get_option: + inc esi + lodsb + cmp al,'e' + je set_errors_limit + cmp al,'E' + je set_errors_limit + cmp al,'i' + je insert_initial_command + cmp al,'I' + je insert_initial_command + cmp al,'p' + je set_passes_limit + cmp al,'P' + je set_passes_limit + cmp al,'r' + je set_recursion_limit + cmp al,'R' + je set_recursion_limit + cmp al,'v' + je set_verbose_mode + cmp al,'V' + je set_verbose_mode + cmp al,'n' + je set_no_logo + cmp al,'N' + jne error_in_arguments + set_no_logo: + or [no_logo],-1 + cmp byte [esi],0 + je next_argument + error_in_arguments: + or al,-1 + ret + set_verbose_mode: + cmp byte [esi],0 + jne get_verbose_setting + dec ecx + jz error_in_arguments + add ebx,4 + mov esi,[ebx] + get_verbose_setting: + call get_option_value + cmp edx,2 + ja error_in_arguments + mov [verbosity_level],edx + jmp next_argument + set_errors_limit: + cmp byte [esi],0 + jne get_errors_setting + dec ecx + jz error_in_arguments + add ebx,4 + mov esi,[ebx] + get_errors_setting: + call get_option_value + test edx,edx + jz error_in_arguments + mov [maximum_number_of_errors],edx + jmp next_argument + set_recursion_limit: + cmp byte [esi],0 + jne get_recursion_setting + dec ecx + jz error_in_arguments + add ebx,4 + mov esi,[ebx] + get_recursion_setting: + call get_option_value + test edx,edx + jz error_in_arguments + mov [maximum_depth_of_stack],edx + jmp next_argument + set_passes_limit: + cmp byte [esi],0 + jne get_passes_setting + dec ecx + jz error_in_arguments + add ebx,4 + mov esi,[ebx] + get_passes_setting: + call get_option_value + test edx,edx + jz error_in_arguments + mov [maximum_number_of_passes],edx + next_argument: + add ebx,4 + dec ecx + jnz get_argument + cmp [source_path],0 + je error_in_arguments + xor al,al + ret + get_option_value: + xor eax,eax + mov edx,eax + find_option_value: + cmp byte [esi],20h + jne get_option_digit + inc esi + jmp find_option_value + get_option_digit: + lodsb + test al,al + jz option_value_ok + sub al,30h + jc invalid_option_value + cmp al,9 + ja invalid_option_value + imul edx,10 + jo invalid_option_value + add edx,eax + jc invalid_option_value + jmp get_option_digit + option_value_ok: + dec esi + clc + ret + invalid_option_value: + stc + ret + insert_initial_command: + cmp byte [esi],0 + jne measure_initial_command + dec ecx + jz error_in_arguments + add ebx,4 + mov esi,[ebx] + measure_initial_command: + push ebx ecx edi + mov edi,esi + or ecx,-1 + xor al,al + repne scasb + not ecx + dec ecx + mov edi,[initial_commands] + lea eax,[ecx+2] + test edi,edi + jz allocate_initial_commands_buffer + mov edx,[initial_commands_length] + add edi,edx + add eax,edx + cmp eax,[initial_commands_maximum_length] + ja grow_initial_commands_buffer + copy_initial_command: + rep movsb + mov ax,0Ah + stosw + dec edi + sub edi,[initial_commands] + mov [initial_commands_length],edi + pop edi ecx ebx + jmp next_argument + allocate_initial_commands_buffer: + push ecx + mov ecx,eax + call malloc + mov [initial_commands],eax + mov [initial_commands_maximum_length],ecx + mov edi,eax + pop ecx + jmp copy_initial_command + grow_initial_commands_buffer: + push ecx + mov ecx,eax + mov eax,[initial_commands] + call realloc + mov [initial_commands],eax + mov [initial_commands_maximum_length],ecx + mov edi,eax + add edi,[initial_commands_length] + pop ecx + jmp copy_initial_command + + include 'system.inc' + + include '../assembler.inc' + include '../symbols.inc' + include '../expressions.inc' + include '../conditions.inc' + include '../floats.inc' + include '../directives.inc' + include '../calm.inc' + include '../errors.inc' + include '../map.inc' + include '../reader.inc' + include '../output.inc' + include '../console.inc' + +segment readable + + _logo db 'flat assembler version g.',VERSION,10,0 + + _usage db 'Usage: fasmg source [output]',10 + db 'Optional settings:',10 + db ' -p limit Set the maximum allowed number of passes (default 100)',10 + db ' -e limit Set the maximum number of displayed errors (default 1)',10 + db ' -r limit Set the maximum depth of stack (default 10000)',10 + db ' -v flag Enable or disable showing all lines from the stack (default 0)',10 + db ' -i command Insert instruction at the beginning of source',13,10 + db ' -n Do not show logo nor summary',13,10 + db 0 + + _pass db ' pass, ',0 + _passes db ' passes, ',0 + _dot db '.' + _seconds db ' seconds, ',0 + _byte db ' byte.',0 + _bytes db ' bytes.',0 + + _write_failed db 'failed to write the output file',0 + _out_of_memory db 'not enough memory to complete the assembly',0 + _code_cannot_be_generated db 'could not generate code within the allowed number of passes',0 + + _open_mode db 'r',0 + _create_mode db 'w',0 + + include '../tables.inc' + include '../messages.inc' + +segment readable writeable + + align 16 + + include '../variables.inc' + + source_path dd ? + output_path dd ? + maximum_number_of_passes dd ? + + initial_commands dd ? + initial_commands_length dd ? + initial_commands_maximum_length dd ? + + argc dd ? + argv dd ? + env dd ? + timestamp dq ? + loff dq ? + + start_time timeval + end_time timeval + tenths_of_second dd ? + + verbosity_level dd ? + no_logo db ? + + path_buffer rb 1000h + +segment readable writeable gnustack diff --git a/x86_64_sse2_x87/fasm/source/linux/import32.inc b/x86_64_sse2_x87/fasm/source/linux/import32.inc new file mode 100644 index 0000000..8cea710 --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/linux/import32.inc @@ -0,0 +1,162 @@ + +macro ccall proc*,[arg] +{ + common + local size,count + mov ebp,esp + if size + sub esp,size + end if + and esp,0FFFFFFF0h + count = 0 + if ~ arg eq + forward + mov dword [esp+count*4],arg + count = count + 1 + common + end if + size = count*4 + call proc + mov esp,ebp +} + +macro cinvoke proc*,arg& { ccall [proc],arg } + +DT_NULL = 0 +DT_NEEDED = 1 +DT_HASH = 4 +DT_STRTAB = 5 +DT_SYMTAB = 6 +DT_RELA = 7 +DT_RELASZ = 8 +DT_RELAENT = 9 +DT_STRSZ = 10 +DT_SYMENT = 11 +DT_REL = 17 +DT_RELSZ = 18 +DT_RELENT = 19 + +STB_LOCAL = 0 +STB_GLOBAL = 1 +STB_WEAK = 2 + +STT_NOTYPE = 0 +STT_OBJECT = 1 +STT_FUNC = 2 +STT_SECTION = 3 +STT_FILE = 4 + +R_386_NONE = 0 +R_386_32 = 1 +R_386_PC32 = 2 +R_386_GOT32 = 3 +R_386_PLT32 = 4 +R_386_COPY = 5 +R_386_GLOB_DAT = 6 +R_386_JMP_SLOT = 7 +R_386_RELATIVE = 8 +R_386_GOTOFF = 9 +R_386_GOTPC = 10 + +macro Elf32_Sym name,value,size,bind,type,other,shndx +{ + dd name+0 + dd value+0 + dd size+0 + db (bind+0) shl 4 + (type+0) + db other+0 + dw shndx+0 +} + +virtual at 0 + Elf32_Sym + sizeof.Elf32_Sym = $ +end virtual + +macro Elf32_Rel offset,symbol,type +{ + dd offset+0 + dd (symbol+0) shl 8 + (type+0) +} + +virtual at 0 + Elf32_Rel + sizeof.Elf32_Rel = $ +end virtual + +macro Elf32_Rela offset,symbol,type,addend +{ + dd offset+0 + dd (symbol+0) shl 8 + (type+0) + dd addend+0 +} + +virtual at 0 + Elf32_Rela + sizeof.Elf32_Rela = $ +end virtual + +macro interpreter [library] +{ + segment interpreter readable + db library,0 +} + +macro needed [library] +{ + local str + match needed,needed@dynamic \{ define needed@dynamic needed,str:library \} + match ,needed@dynamic \{ define needed@dynamic str:library \} +} +define needed@dynamic + +macro import [name,string] +{ + common + local strtab,strsz,symtab,rel,relsz,hash + segment dynamic readable + match needed,needed@dynamic + \{ irp item,needed \\{ match str:library,item \\\{ dd DT_NEEDED,str-strtab \\\} \\} \} + dd DT_STRTAB,strtab + dd DT_STRSZ,strsz + dd DT_SYMTAB,symtab + dd DT_SYMENT,sizeof.Elf32_Sym + dd DT_REL,rel + dd DT_RELSZ,relsz + dd DT_RELENT,sizeof.Elf32_Rel + dd DT_HASH,hash + dd DT_NULL,0 + segment readable writeable + symtab: Elf32_Sym + forward + local fstr + Elf32_Sym fstr-strtab,0,0,STB_GLOBAL,STT_FUNC,0,0 + common + rel: + local counter + counter = 1 + forward + Elf32_Rel name,counter,R_386_32 + counter = counter+1 + common + relsz = $-rel + hash: + dd 1,counter + dd 0 + repeat counter + if %=counter + dd 0 + else + dd % + end if + end repeat + strtab db 0 + forward + fstr db string,0 + common + match needed,needed@dynamic + \{ irp item,needed \\{ match str:library,item \\\{ str db library,0 \\\} \\} \} + strsz = $-strtab + forward + name dd 0 +} diff --git a/x86_64_sse2_x87/fasm/source/linux/selfhost.inc b/x86_64_sse2_x87/fasm/source/linux/selfhost.inc new file mode 100644 index 0000000..5f3e6de --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/linux/selfhost.inc @@ -0,0 +1,189 @@ + +include '../../examples/x86/include/80386.inc' + +macro format?.ELF? variant + match , variant + format binary as 'o' + include '../../examples/x86/include/format/elf32.inc' + use32 + else match =executable? settings, variant: + match brand =at? base:, settings + ELF.Settings.ABI = brand + ELF.Settings.BaseAddress = base + else match =at? base:, settings + ELF.Settings.BaseAddress = base + else match brand:, settings + ELF.Settings.ABI = brand + end match + include '../../examples/x86/include/format/elfexe.inc' + use32 + else + err 'invalid argument' + end match +end macro + +macro struct? name + macro ends?! + end namespace + esc end struc + virtual at 0 + name name + sizeof.name = $ + end virtual + purge ends? + end macro + esc struc name + label . : sizeof.name + namespace . +end macro + +macro cinvoke? proc*,args& + local size + mov ebp,esp + if size + sub esp,size + end if + and esp,0FFFFFFF0h + match any, args + iterate arg, args + mov dword [esp+(%-1)*4],arg + if % = 1 + size := %%*4 + end if + end iterate + else + size := 0 + end match + call [proc] + mov esp,ebp +end macro + +DT_NULL = 0 +DT_NEEDED = 1 +DT_HASH = 4 +DT_STRTAB = 5 +DT_SYMTAB = 6 +DT_RELA = 7 +DT_RELASZ = 8 +DT_RELAENT = 9 +DT_STRSZ = 10 +DT_SYMENT = 11 +DT_REL = 17 +DT_RELSZ = 18 +DT_RELENT = 19 + +STB_LOCAL = 0 +STB_GLOBAL = 1 +STB_WEAK = 2 + +STT_NOTYPE = 0 +STT_OBJECT = 1 +STT_FUNC = 2 +STT_SECTION = 3 +STT_FILE = 4 + +R_386_NONE = 0 +R_386_32 = 1 +R_386_PC32 = 2 +R_386_GOT32 = 3 +R_386_PLT32 = 4 +R_386_COPY = 5 +R_386_GLOB_DAT = 6 +R_386_JMP_SLOT = 7 +R_386_RELATIVE = 8 +R_386_GOTOFF = 9 +R_386_GOTPC = 10 + +macro Elf32_Sym name:0,value:0,size:0,bind:0,type:0,other:0,shndx:0 + dd name + dd value + dd size + db bind shl 4 + type + db other + dw shndx +end macro + +virtual at 0 + Elf32_Sym + sizeof.Elf32_Sym = $ +end virtual + +macro Elf32_Rel offset:0,symbol:0,type:0 + dd offset + dd symbol shl 8 + type +end macro + +virtual at 0 + Elf32_Rel + sizeof.Elf32_Rel = $ +end virtual + +macro Elf32_Rela offset:0,symbol:0,type:0,addend:0 + dd offset + dd symbol shl 8 + type + dd addend +end macro + +virtual at 0 + Elf32_Rela + sizeof.Elf32_Rela = $ +end virtual + +macro interpreter library + segment interpreter readable + db library,0 +end macro + +macro needed libraries& + irp library, libraries + define needed@dynamic library + end irp +end macro + +macro import definitions& + local strtab,strsz,symtab,rel,relsz,hash + segment dynamic readable + irpv library, needed@dynamic + dd DT_NEEDED,strtab.needed#%-strtab + end irpv + dd DT_STRTAB,strtab + dd DT_STRSZ,strsz + dd DT_SYMTAB,symtab + dd DT_SYMENT,sizeof.Elf32_Sym + dd DT_REL,rel + dd DT_RELSZ,relsz + dd DT_RELENT,sizeof.Elf32_Rel + dd DT_HASH,hash + dd DT_NULL,0 + segment readable writeable + symtab: Elf32_Sym + local count + count = 0 + irp , definitions + Elf32_Sym strtab.label-strtab,0,0,STB_GLOBAL,STT_FUNC,0,0 + count = count+1 + end irp + rel: + irp , definitions + Elf32_Rel label,%,R_386_32 + end irp + relsz = $-rel + hash: + dd 1,count+1 + dd 0 + repeat count + dd % + end repeat + dd 0 + strtab db 0 + irp , definitions + strtab.label db string,0 + end irp + irpv library, needed@dynamic + strtab.needed#% db library,0 + end irpv + strsz = $-strtab + irp , definitions + label dd 0 + end irp +end macro diff --git a/x86_64_sse2_x87/fasm/source/linux/system.inc b/x86_64_sse2_x87/fasm/source/linux/system.inc new file mode 100644 index 0000000..9db3a84 --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/linux/system.inc @@ -0,0 +1,298 @@ + +LINE_FEED = 0Ah + +O_ACCMODE = 0003o +O_RDONLY = 0000o +O_WRONLY = 0001o +O_RDWR = 0002o +O_CREAT = 0100o +O_EXCL = 0200o +O_NOCTTY = 0400o +O_TRUNC = 1000o +O_APPEND = 2000o +O_NONBLOCK = 4000o + +S_ISUID = 4000o +S_ISGID = 2000o +S_ISVTX = 1000o +S_IRUSR = 0400o +S_IWUSR = 0200o +S_IXUSR = 0100o +S_IRGRP = 0040o +S_IWGRP = 0020o +S_IXGRP = 0010o +S_IROTH = 0004o +S_IWOTH = 0002o +S_IXOTH = 0001o + +system_init: + mov eax,13 ; sys_time + mov ebx,timestamp + int 0x80 + retn + +system_shutdown: + retn + +malloc: +malloc_fixed: +malloc_growable: +; in: ecx = requested size +; out: eax - allocated block, ecx = allocated size, on error jumps to out_of_memory (does not return) +; preserves: ebx, esi, edi +; note: +; use of malloc_fixed hints that block will be kept as is until the end of assembly +; use of malloc_growable hints that block is likely to be resized + push ebx ecx esi edi + cinvoke libc.malloc,ecx + pop edi esi ecx ebx + test eax,eax + jz out_of_memory + retn +realloc: +; in: eax - memory block, ecx = requested size +; out: eax - resized block, ecx = allocated size, on error jumps to out_of_memory (does not return) +; preserves: ebx, esi, edi + push ebx ecx esi edi + cinvoke libc.realloc,eax,ecx + pop edi esi ecx ebx + test eax,eax + jz out_of_memory + retn +mfree: +; in: eax - memory block +; out: cf set on error +; preserves: ebx, esi, edi +; note: eax may have value 0 or -1, it should be treated as invalid input then + test eax,eax + jz interface_error + cmp eax,-1 + je interface_error + push ebx esi edi + cinvoke libc.free,eax + pop edi esi ebx + clc + retn + interface_error: + stc + retn + +open: +; in: edx - path to file +; out: ebx = file handle, cf set on error +; preserves: esi, edi + push esi edi ebp + call adapt_path + mov eax,5 ; sys_open + mov ecx,O_RDONLY + xor edx,edx + int 0x80 + pop ebp edi esi + test eax,eax + js interface_error + mov ebx,eax + clc + retn + adapt_path: + xor ecx,ecx + mov ebx,path_buffer + copy_path: + mov al,[edx+ecx] + cmp al,'\' + jne path_char_ok + mov al,'/' + path_char_ok: + cmp ecx,1000h + jae out_of_memory + mov [ebx+ecx],al + inc ecx + test al,al + jnz copy_path + retn +create: +; in: edx - path to file +; out: ebx = file handle, cf set on error +; preserves: esi, edi + push esi edi ebp + call adapt_path + mov eax,5 ; sys_open + mov ecx,O_CREAT+O_TRUNC+O_WRONLY + mov edx,S_IRUSR+S_IWUSR+S_IRGRP+S_IROTH + int 0x80 + pop ebp edi esi + test eax,eax + js interface_error + mov ebx,eax + clc + retn +write: +; in: ebx = file handle, edx - data, ecx = number of bytes +; out: cf set on error +; preserves: ebx, esi, edi + push ebx ecx esi edi ebp + mov eax,4 ; sys_write + xchg ecx,edx + int 0x80 + pop ebp edi esi ecx ebx + test eax,eax + js interface_error + cmp eax,ecx + jne interface_error + clc + ret +read: +; in: ebx = file handle, edx - buffer, ecx = number of bytes +; out: cf set on error +; preserves: ebx, esi, edi + push ebx ecx esi edi ebp + mov eax,3 ; sys_read + xchg ecx,edx + int 0x80 + pop ebp edi esi ecx ebx + test eax,eax + js interface_error + cmp eax,ecx + jne interface_error + clc + ret +close: +; in: ebx = file handle +; preserves: ebx, esi, edi + push ebx esi edi ebp + mov eax,6 ; sys_close + int 0x80 + pop ebp edi esi ebx + ret +lseek: +; in: ebx = file handle, cl = method, edx:eax = offset +; out: edx:eax = new offset from the beginning of file, cf set on error +; preserves: ebx, esi, edi + test edx,edx + jnz interface_error + push esi edi ebx ebp + movzx edi,cl + mov ecx,edx + mov edx,eax + mov eax,140 ; sys_llseek + mov esi,loff + int 0x80 + pop ebp ebx edi esi + test eax,eax + js interface_error + mov eax,dword [loff] + mov edx,dword [loff+4] + clc + ret + +get_timestamp: +; out: edx:eax = timestamp +; preserves: ebx, ecx, esi, edi + mov eax,dword [timestamp] + mov edx,dword [timestamp+4] + retn + +display_string: +; in: +; esi - string +; ecx = string length, zero for ASCIIZ string +; preserves: ebx, esi + push ebx esi ebp + test ecx,ecx + jnz write_string_to_stdout + xor al,al + mov edi,esi + or ecx,-1 + repne scasb + neg ecx + sub ecx,2 + write_string_to_stdout: + mov eax,4 ; sys_write + mov ebx,1 + mov edx,ecx + mov ecx,esi + int 0x80 + pop ebp esi ebx + retn + +display_error_string: +; in: +; esi - string +; ecx = string length, zero for ASCIIZ string +; preserves: ebx, esi + push ebx esi ebp + test ecx,ecx + jnz write_string_to_stderr + xor al,al + mov edi,esi + or ecx,-1 + repne scasb + neg ecx + sub ecx,2 + write_string_to_stderr: + mov eax,4 ; sys_write + mov ebx,2 + mov edx,ecx + mov ecx,esi + int 0x80 + pop ebp esi ebx + retn + +get_environment_variable: +; in: +; esi - name +; edi - buffer for value +; ecx = size of buffer +; out: +; eax = length of value +; preserves: ebx, esi, edi + push ebx ecx + mov edx,[env] + scan_environment: + mov ebx,[edx] + test ebx,ebx + jz no_environment_variable + xor ecx,ecx + compare_character: + mov al,[ebx+ecx] + mov ah,[esi+ecx] + inc ecx + cmp al,'=' + je end_of_variable_name + test ah,ah + jz next_variable + sub ah,al + je compare_character + cmp ah,20h + jne next_variable + cmp al,'A' + jb next_variable + cmp al,'Z' + jna compare_character + next_variable: + add edx,4 + jmp scan_environment + end_of_variable_name: + test ah,ah + jnz next_variable + add ebx,ecx + pop ecx + xor eax,eax + copy_environment_variable: + mov dl,[ebx+eax] + cmp eax,ecx + jae next_environment_variable_character + mov [edi+eax],dl + next_environment_variable_character: + inc eax + test dl,dl + jnz copy_environment_variable + environment_variable_ok: + pop ebx + ret + no_environment_variable: + pop ecx + mov eax,1 + jecxz environment_variable_ok + and byte [edi],0 + pop ebx + ret diff --git a/x86_64_sse2_x87/fasm/source/linux/x64/32on64.inc b/x86_64_sse2_x87/fasm/source/linux/x64/32on64.inc new file mode 100644 index 0000000..8df8ff0 --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/linux/x64/32on64.inc @@ -0,0 +1,124 @@ + +macro use32on64 +{ + + define esp rsp + + define promote.eax rax + define promote.ebx rbx + define promote.ecx rcx + define promote.edx rdx + define promote.esi rsi + define promote.edi rdi + define promote.ebp rbp + define promote.esp rsp + + macro push args + \{ + local list,arg,status + define list + define arg + irps sym, args \\{ + define status + match =dword, sym \\\{ + define status : + \\\} + match [any, status arg sym \\\{ + define arg [any + match [mem], arg \\\\{ + match previous, list \\\\\{ define list previous,[mem] \\\\\} + match , list \\\\\{ define list [mem] \\\\\} + define arg + \\\\} + define status : + \\\} + match [, status arg sym \\\{ + define arg [ + define status : + \\\} + match , status \\\{ + match previous, list \\\\{ define list previous,sym \\\\} + match , list \\\\{ define list sym \\\\} + \\\} + \\} + match ops,list \\{ + irp op, ops \\\{ + if op eqtype eax + push promote.\\\#op + else + mov r8d,op + push r8 + end if + \\\} + \\} + \} + + macro pop args + \{ + local list,arg,status + define list + define arg + irps sym, args \\{ + define status + match =dword, sym \\\{ + define status : + \\\} + match [any, status arg sym \\\{ + define arg [any + match [mem], arg \\\\{ + match previous, list \\\\\{ define list previous,[mem] \\\\\} + match , list \\\\\{ define list [mem] \\\\\} + define arg + \\\\} + define status : + \\\} + match [, status arg sym \\\{ + define arg [ + define status : + \\\} + match , status \\\{ + match previous, list \\\\{ define list previous,sym \\\\} + match , list \\\\{ define list sym \\\\} + \\\} + \\} + match ops,list \\{ + irp op, ops \\\{ + if op eqtype eax + pop promote.\\\#op + else + pop r8 + mov op,r8d + end if + \\\} + \\} + \} + + irp instr, jmp,call + \{ + macro instr op + \\{ + if op eqtype [0] + mov r8d,op + instr r8 + else if op eqtype 0 + instr op + else + instr promote.\\#op + end if + \\} + \} + + macro jecxz target + \{ + if target-($+1) < 80h & target-($+1) >= -80h + jecxz target + else + local j,k + jecxz j + jmp k + j: jmp target + k: + end if + \} + +} diff --git a/x86_64_sse2_x87/fasm/source/linux/x64/fasmg.asm b/x86_64_sse2_x87/fasm/source/linux/x64/fasmg.asm new file mode 100644 index 0000000..3e52474 --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/linux/x64/fasmg.asm @@ -0,0 +1,535 @@ + +match ,{ + + include 'macro/struct.inc' + include '32on64.inc' + +} match -,{ +else + + include 'selfhost.inc' + +end match +_ equ } + +format ELF64 executable 3 +entry start + +include '../../version.inc' + +struct timeval + time_t dq ? + suseconds_t dq ? +ends + +segment readable executable + + start: + + mov rcx,[rsp] + mov [argc],rcx + lea rbx,[rsp+8] + mov [argv],rbx + lea rsi,[rsp+8+rcx*8+8] + mov [env],rsi + + call system_init + + call get_arguments + mov bl,al + cmp [no_logo],0 + jne logo_ok + mov esi,_logo + xor ecx,ecx + call display_string + logo_ok: + test bl,bl + jnz display_usage_information + + xor al,al + mov ecx,[verbosity_level] + jecxz init + or al,TRACE_ERROR_STACK + dec ecx + jz init + or al,TRACE_DISPLAY + init: + call assembly_init + + mov eax,96 ; sys_gettimeofday + mov edi,start_time + xor esi,esi + syscall + + assemble: + mov esi,[initial_commands] + mov edx,[source_path] + call assembly_pass + jc assembly_done + + mov eax,[current_pass] + cmp eax,[maximum_number_of_passes] + jb assemble + + call show_display_data + + mov esi,_error_prefix + xor ecx,ecx + call display_error_string + mov esi,_code_cannot_be_generated + xor ecx,ecx + call display_error_string + mov esi,_message_suffix + xor ecx,ecx + call display_error_string + + jmp assembly_failed + + assembly_done: + + call show_display_data + + cmp [first_error],0 + jne assembly_failed + + cmp [no_logo],0 + jne summary_done + mov eax,[current_pass] + xor edx,edx + call itoa + call display_string + mov esi,_passes + cmp [current_pass],1 + jne display_passes_suffix + mov esi,_pass + display_passes_suffix: + xor ecx,ecx + call display_string + mov eax,96 ; sys_gettimeofday + mov edi,end_time + xor esi,esi + syscall + mov rax,[end_time.time_t] + sub rax,[start_time.time_t] + mov rcx,1000000 + mul rcx + add rax,[end_time.suseconds_t] + adc rdx,0 + sub rax,[start_time.suseconds_t] + sbb rdx,0 + add rax,50000 + mov rcx,1000000 + div rcx + mov rbx,rax + mov rax,rdx + xor rdx,rdx + mov rcx,100000 + div rcx + mov [tenths_of_second],eax + xchg rax,rbx + or rbx,rax + jz display_output_length + mov rdx,rax + shr rdx,32 + call itoa + call display_string + mov esi,_message_suffix + mov ecx,1 + call display_string + mov eax,[tenths_of_second] + xor edx,edx + call itoa + call display_string + mov esi,_seconds + xor ecx,ecx + call display_string + display_output_length: + call get_output_length + push rax rdx + call itoa + call display_string + pop rdx rax + mov esi,_bytes + cmp eax,1 + jne display_bytes_suffix + test edx,edx + jnz display_bytes_suffix + mov esi,_byte + display_bytes_suffix: + xor ecx,ecx + call display_string + mov esi,_new_line + xor ecx,ecx + call display_string + summary_done: + + mov ebx,[source_path] + mov edi,[output_path] + call write_output_file + jc write_failed + + call assembly_shutdown + call system_shutdown + + xor edi,edi ; exit code 0 + mov eax,60 ; sys_exit + syscall + + assembly_failed: + + call show_errors + + call assembly_shutdown + call system_shutdown + + mov edi,2 + mov eax,60 ; sys_exit + syscall + + write_failed: + mov ebx,_write_failed + jmp fatal_error + + out_of_memory: + mov ebx,_out_of_memory + jmp fatal_error + + fatal_error: + + mov esi,_error_prefix + xor ecx,ecx + call display_error_string + mov esi,ebx + xor ecx,ecx + call display_error_string + mov esi,_message_suffix + xor ecx,ecx + call display_error_string + + call assembly_shutdown + call system_shutdown + + mov edi,3 + mov eax,60 ; sys_exit + syscall + + internal_error: + int3 + + display_usage_information: + + mov esi,_usage + xor ecx,ecx + call display_string + + call system_shutdown + + mov edi,1 + mov eax,60 ; sys_exit + syscall + + get_arguments: + xor eax,eax + mov [initial_commands],eax + mov [source_path],eax + mov [output_path],eax + mov [no_logo],al + mov [verbosity_level],eax + mov [maximum_number_of_passes],100 + mov [maximum_number_of_errors],1 + mov [maximum_depth_of_stack],10000 + mov rcx,[argc] + mov rbx,[argv] + add rbx,8 + dec ecx + jz error_in_arguments + get_argument: + mov rsi,[rbx] + mov al,[rsi] + cmp al,'-' + je get_option + cmp [source_path],0 + jne get_output_file + call strdup + mov [source_path],eax + jmp next_argument + get_output_file: + cmp [output_path],0 + jne error_in_arguments + call strdup + mov [output_path],eax + jmp next_argument + get_option: + inc rsi + lodsb + cmp al,'e' + je set_errors_limit + cmp al,'E' + je set_errors_limit + cmp al,'i' + je insert_initial_command + cmp al,'I' + je insert_initial_command + cmp al,'p' + je set_passes_limit + cmp al,'P' + je set_passes_limit + cmp al,'r' + je set_recursion_limit + cmp al,'R' + je set_recursion_limit + cmp al,'v' + je set_verbose_mode + cmp al,'V' + je set_verbose_mode + cmp al,'n' + je set_no_logo + cmp al,'N' + jne error_in_arguments + set_no_logo: + or [no_logo],-1 + cmp byte [rsi],0 + je next_argument + error_in_arguments: + or al,-1 + ret + set_verbose_mode: + cmp byte [rsi],0 + jne get_verbose_setting + dec ecx + jz error_in_arguments + add rbx,8 + mov rsi,[rbx] + get_verbose_setting: + call get_option_value + cmp edx,2 + ja error_in_arguments + mov [verbosity_level],edx + jmp next_argument + set_errors_limit: + cmp byte [rsi],0 + jne get_errors_setting + dec ecx + jz error_in_arguments + add rbx,8 + mov rsi,[rbx] + get_errors_setting: + call get_option_value + test edx,edx + jz error_in_arguments + mov [maximum_number_of_errors],edx + jmp next_argument + set_recursion_limit: + cmp byte [rsi],0 + jne get_recursion_setting + dec ecx + jz error_in_arguments + add rbx,8 + mov rsi,[rbx] + get_recursion_setting: + call get_option_value + test edx,edx + jz error_in_arguments + mov [maximum_depth_of_stack],edx + jmp next_argument + set_passes_limit: + cmp byte [rsi],0 + jne get_passes_setting + dec ecx + jz error_in_arguments + add rbx,8 + mov rsi,[rbx] + get_passes_setting: + call get_option_value + test edx,edx + jz error_in_arguments + mov [maximum_number_of_passes],edx + next_argument: + add rbx,8 + dec ecx + jnz get_argument + cmp [source_path],0 + je error_in_arguments + xor al,al + ret + get_option_value: + xor eax,eax + mov edx,eax + find_option_value: + cmp byte [rsi],20h + jne get_option_digit + inc rsi + jmp find_option_value + get_option_digit: + lodsb + test al,al + jz option_value_ok + sub al,30h + jc invalid_option_value + cmp al,9 + ja invalid_option_value + imul edx,10 + jo invalid_option_value + add edx,eax + jc invalid_option_value + jmp get_option_digit + option_value_ok: + dec rsi + clc + ret + invalid_option_value: + stc + ret + insert_initial_command: + cmp byte [rsi],0 + jne measure_initial_command + dec ecx + jz error_in_arguments + add rbx,8 + mov rsi,[rbx] + measure_initial_command: + push rbx rcx rdi + mov rdi,rsi + or ecx,-1 + xor al,al + repne scasb + not ecx + dec ecx + mov edi,[initial_commands] + lea eax,[ecx+2] + test edi,edi + jz allocate_initial_commands_buffer + mov edx,[initial_commands_length] + add edi,edx + add eax,edx + cmp eax,[initial_commands_maximum_length] + ja grow_initial_commands_buffer + copy_initial_command: + rep movsb + mov ax,0Ah + stosw + dec edi + sub edi,[initial_commands] + mov [initial_commands_length],edi + pop rdi rcx rbx + jmp next_argument + allocate_initial_commands_buffer: + push rsi rcx + mov ecx,eax + call malloc + mov [initial_commands],eax + mov [initial_commands_maximum_length],ecx + mov edi,eax + pop rcx rsi + jmp copy_initial_command + grow_initial_commands_buffer: + push rsi rcx + mov ecx,eax + mov eax,[initial_commands] + call realloc + mov [initial_commands],eax + mov [initial_commands_maximum_length],ecx + mov edi,eax + add edi,[initial_commands_length] + pop rcx rsi + jmp copy_initial_command + + strdup: + ; in: rsi - ASCIIZ string + ; out: eax - copy of the string in 32-bit addressable memory + ; preserves: rbx, rcx, rsi + push rbx rcx rsi + mov rdi,rsi + or ecx,-1 + xor al,al + repne scasb + not ecx + push rsi rcx + call malloc + pop rcx rsi + mov edi,eax + rep movsb + pop rsi rcx rbx + ret + + include 'system.inc' + include 'malloc.inc' + + use32on64 + + include '../../assembler.inc' + include '../../symbols.inc' + include '../../expressions.inc' + include '../../conditions.inc' + include '../../floats.inc' + include '../../directives.inc' + include '../../calm.inc' + include '../../errors.inc' + include '../../map.inc' + include '../../reader.inc' + include '../../output.inc' + include '../../console.inc' + +segment readable + + _logo db 'flat assembler version g.',VERSION,10,0 + + _usage db 'Usage: fasmg source [output]',10 + db 'Optional settings:',10 + db ' -p limit Set the maximum allowed number of passes (default 100)',10 + db ' -e limit Set the maximum number of displayed errors (default 1)',10 + db ' -r limit Set the maximum depth of stack (default 10000)',10 + db ' -v flag Enable or disable showing all lines from the stack (default 0)',10 + db ' -i command Insert instruction at the beginning of source',13,10 + db ' -n Do not show logo nor summary',13,10 + db 0 + + _pass db ' pass, ',0 + _passes db ' passes, ',0 + _dot db '.' + _seconds db ' seconds, ',0 + _byte db ' byte.',0 + _bytes db ' bytes.',0 + + _write_failed db 'failed to write the output file',0 + _out_of_memory db 'not enough memory to complete the assembly',0 + _code_cannot_be_generated db 'could not generate code within the allowed number of passes',0 + + include '../../tables.inc' + include '../../messages.inc' + +segment readable writeable + + align 16 + + malloc_hint dd 480000h + malloc_ffirst dd 0 + malloc_flast dd 0 + malloc_fbrk dd 0 + malloc_lbrk dd 0 + + include '../../variables.inc' + + argc dq ? + argv dq ? + env dq ? + timestamp dq ? + + source_path dd ? + output_path dd ? + maximum_number_of_passes dd ? + + initial_commands dd ? + initial_commands_length dd ? + initial_commands_maximum_length dd ? + + start_time timeval + end_time timeval + tenths_of_second dd ? + + verbosity_level dd ? + no_logo db ? + + path_buffer rb 1000h + +segment readable writeable gnustack diff --git a/x86_64_sse2_x87/fasm/source/linux/x64/malloc.inc b/x86_64_sse2_x87/fasm/source/linux/x64/malloc.inc new file mode 100644 index 0000000..5261840 --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/linux/x64/malloc.inc @@ -0,0 +1,417 @@ + +; this is a simplified memory allocator +; based on code contributed by Jeff Marrison + +MALLOC_MINIMUM_SIZE = 8 +assert MALLOC_MINIMUM_SIZE = 1 shl (bsr MALLOC_MINIMUM_SIZE) ; must be a power of 2 +MALLOC_MINIMUM_MASK = MALLOC_MINIMUM_SIZE - 1 + +MALLOC_SYSTEM_SIZE = 262144 +assert MALLOC_SYSTEM_SIZE = 1 shl (bsr MALLOC_SYSTEM_SIZE) ; must be a power of 2 +MALLOC_SYSTEM_MASK = MALLOC_SYSTEM_SIZE - 1 + +assert MALLOC_SYSTEM_SIZE mod MALLOC_MINIMUM_SIZE = 0 + +malloc: +malloc_fixed: +malloc_growable: +; in: ecx = requested size +; out: eax - allocated block, ecx = allocated size, on error jumps to out_of_memory (does not return) +; preserves: ebx, esi, edi +; note: +; use of malloc_fixed hints that block will be kept as is until the end of assembly +; use of malloc_growable hints that block is likely to be resized + mov eax,8 + cmp ecx,eax ; minimum 8 bytes for actual storage space + cmovb ecx,eax ; (so that when it is freed we can maintain our freelist) + mov r12d,edi + mov r13d,esi + mov r14d,ecx + mov edx,[malloc_ffirst] + test edx,edx + jz malloc_brk + add ecx,MALLOC_MINIMUM_MASK+4 + and ecx,not MALLOC_MINIMUM_MASK + mov eax,-1 + xor edi,edi + malloc_traverse: + mov esi,[edx-4] ; this block's size + and esi,not 1 ; remove the freelist bit + mov r8d,[edx+4] + cmp ecx,esi + je malloc_use_entire_block + ja malloc_traverse_next + cmp esi,eax + cmovb eax,esi + cmovb edi,edx + malloc_traverse_next: + mov edx,r8d + test r8d,r8d + jnz malloc_traverse + test edi,edi + jz malloc_brk + malloc_use_block: + ; in: + ; edi - free block to use for allocation + ; eax = size of the free block, including the preceding header + ; ecx = requested size, including the preceding header (must be aligned to MALLOC_MINIMUM_SIZE and in range from 12 to eax) + ; out: + ; eax - allocated block (the same address as the free block that was given) + ; ecx = allocated size (preceding header not included) + ; edi = what was in r12d + ; esi = what was in r13d + ; preserves: rbx + ; note: for internal use only, it does not validate the parameters + mov edx,edi + mov esi,eax + sub esi,ecx + cmp esi,MALLOC_MINIMUM_SIZE+4 + jb malloc_use_entire_block + mov edi,[malloc_ffirst] + lea r8d,[edx+ecx] ; new block's pointer (freelist is already offset by 4) + or esi,1 + mov [edx+ecx-4],esi ; size of new block with freelist bit set + mov dword [edx+ecx],0 ; its fprev = 0 + mov [edx+ecx+4],edi ; its fnext = ffirst, which may be edx + mov [edx-4],ecx ; set new size of original block + mov [edi],r8d ; old ffirst's fprev = new block + mov [malloc_ffirst],r8d ; ffirst = new block + malloc_use_entire_block: + and dword [edx-4],not 1 ; clear the freelist bit + call unlink_from_freelist + mov eax,edx + mov ecx,[edx-4] + sub ecx,4 + mov edi,r12d + mov esi,r13d + ret + malloc_brk: + ; in: + ; r14d = requested size + ; out: + ; eax - allocated block, equal to previous value of [malloc_lbrk] as long as it was non-zero + ; ecx = allocated size + ; edi = what was in r12d + ; esi = what was in r13d + ; on error jumps to out_of_memory (does not return) + ; preserves: rbx + ; note: for internal use only + lea ecx,[4+r14d+MALLOC_SYSTEM_MASK] + mov edi,[malloc_lbrk] + test edi,edi + jz malloc_mmap + and ecx,not MALLOC_SYSTEM_MASK + add edi,ecx + jc out_of_memory + mov eax,12 ; sys_brk + syscall + mov edx,eax + sub edx,[malloc_lbrk] + jz out_of_memory + xchg [malloc_lbrk],eax + malloc_brk_use: + lea ecx,[4+r14d+MALLOC_MINIMUM_MASK] + and ecx,not MALLOC_MINIMUM_MASK + mov [eax],ecx ; the size of block to return + sub edx,ecx + jz malloc_brk_return + cmp edx,MALLOC_MINIMUM_SIZE+4 + jb malloc_brk_extra ; extra space not enough for another small block + mov edi,[malloc_flast] + lea r8d,[eax+ecx+4] + or edx,1 + mov [eax+ecx],edx ; store the size of the free block with freelist bit set + mov [r8d],edi ; fprev = flast + mov dword [r8d+4],0 ; fnext = 0 + mov [malloc_flast],r8d + cmp dword [malloc_ffirst],0 + je malloc_brk_set_first + mov [edi+4],r8d ; old flast's fnext = this one + jmp malloc_brk_return + malloc_brk_set_first: + mov [malloc_ffirst],r8d + jmp malloc_brk_return + malloc_brk_extra: + add [eax],edx + malloc_brk_return: + mov ecx,[eax] + sub ecx,4 + add eax,4 + mov edi,r12d + mov esi,r13d + ret + malloc_mmap: + add ecx,4 + and ecx,not MALLOC_SYSTEM_MASK + xor r9d,r9d + or r8,-1 + mov r10d,62h ; MAP_PRIVATE + MAP_ANONYMOUS + MAP_32BIT + mov edx,3 ; PROT_READ + PROT_WRITE + mov esi,ecx + xor edi,edi + mov eax,9 ; sys_mmap + syscall + cmp eax,-1 + je malloc_mmap_with_hint + mov ecx,eax + cmp rcx,rax + jne malloc_mmap_unusable + add ecx,esi + jnc malloc_mmap_use + malloc_mmap_unusable: + mov rdi,rax + mov eax,11 ; sys_munmap + syscall + malloc_mmap_with_hint: + mov r10d,22h ; MAP_PRIVATE + MAP_ANONYMOUS + mov edx,3 ; PROT_READ + PROT_WRITE + mov edi,[malloc_hint] + mov eax,9 ; sys_mmap + syscall + cmp eax,-1 + je out_of_memory + mov ecx,eax + cmp rcx,rax + jne out_of_memory + add ecx,esi + jc out_of_memory + malloc_mmap_use: + lea edx,[esi-4] + and dword [ecx-4],0 ; dummy non-freelist block + mov [malloc_hint],ecx + jmp malloc_brk_use + unlink_from_freelist: + ; in: edx - block in freelist to unlink + ; preserves: rbx, rdx, rsi, rdi, r12, r13, r14 + mov eax,[edx] ; fprev + mov ecx,[edx+4] ; fnext + cmp edx,[malloc_ffirst] + je unlink_first + cmp edx,[malloc_flast] + je unlink_last_not_first + mov [eax+4],ecx ; fprev's fnext = fnext + mov [ecx],eax ; fnext's fprev = fprev + ret + unlink_last_not_first: + mov [malloc_flast],eax ; flast = fprev + mov dword [eax+4],0 ; flast's fnext = 0 + ret + unlink_first: + cmp edx,[malloc_flast] + je unlink_first_and_last + mov [malloc_ffirst],ecx ; ffirst = fnext + mov dword [ecx],0 ; ffirst's fprev = 0 + ret + unlink_first_and_last: + mov dword [malloc_ffirst],0 + mov dword [malloc_flast],0 + ret + +mfree: +; in: eax - memory block +; out: cf set on error +; preserves: ebx, esi, edi, r12, r13, r14 +; note: eax may have value 0 or -1, it should be treated as invalid input then + test eax,eax + jz mfree_error + cmp eax,-1 + je mfree_error + mov ecx,[eax-4] ; size of this block + mfree_coalesce: + lea edx,[eax+ecx-4] ; pointer to next block size + cmp edx,[malloc_lbrk] ; is the next block the end of our program break? + je add_to_freelist + test dword [edx],1 ; is the next block in the freelist? + jz add_to_freelist + mov r8d,[edx] + and r8d,not 1 ; clear the freelist bit + add ecx,r8d + mov [eax-4],ecx ; store the new size of the block + add edx,4 + push rax rcx + call unlink_from_freelist + pop rcx rax + jmp mfree_coalesce + add_to_freelist: + mov r8d,[malloc_ffirst] + mov dword [eax],0 ; our block's fprev = 0 + mov [eax+4],r8d ; our block's fnext = ffirst + test r8d,r8d + jz add_to_empty_freelist + mov [malloc_ffirst],eax ; new ffirst = our block + mov [r8d],eax ; old ffirst's fprev = our block + or dword [eax-4],1 ; set the freelist bit + clc + ret + add_to_empty_freelist: + mov [malloc_ffirst],eax ; ffirst = our block + mov [malloc_flast],eax ; flast = our block + or dword [eax-4],1 ; set the freelist bit + clc + ret + mfree_error: + stc + ret + +realloc: +; in: eax - memory block, ecx = requested size +; out: eax - resized block, ecx = allocated size, on error jumps to out_of_memory (does not return) +; preserves: ebx, esi, edi +; note: this implementation does not shrink allocations + mov edx,[eax-4] + sub edx,4 + cmp ecx,edx + jbe realloc_already_satisfied + lea r8d,[eax+edx] + cmp r8d,[malloc_lbrk] + je realloc_brk + lea r8d,[edx+12] + cmp ecx,r8d + cmovb ecx,r8d + lea r9d,[4+ecx+MALLOC_MINIMUM_MASK] + and r9d,not MALLOC_MINIMUM_MASK + mov r8d,[eax+edx] + btr r8d,0 ; freelist bit set? + jnc realloc_and_copy + add edx,4 + add r8d,edx + cmp r8d,r9d + jb realloc_and_copy ; for simplicity, no coalescing here + push rax + mov r12d,edi + mov r13d,esi + lea edi,[eax+edx] + mov eax,[edi-4] + and eax,not 1 + mov ecx,r9d + sub ecx,edx + call malloc_use_block + pop rax + add ecx,4 + add ecx,[eax-4] + mov [eax-4],ecx + sub ecx,4 + ret + realloc_brk: + mov r14d,ecx + sub r14d,edx + mov r12d,edi + mov r13d,esi + push rax + call malloc_brk + pop rax + mov edx,[eax-4] + mov ecx,edx + add ecx,[eax+edx-4] + mov [eax-4],ecx + sub ecx,4 + ret + realloc_and_copy: + push rax rdi rsi + call malloc + pop rsi rdi r14 + mov r12d,edi + mov r13d,esi + mov edi,eax + mov esi,r14d + mov edx,[r14d-4] + sub edx,4 + add edi,edx + add esi,edx + neg edx + realloc_copy32: + cmp edx,-32 + jg realloc_copy16 + mov r8,[esi+edx] + mov r9,[esi+edx+8] + mov [edi+edx],r8 + mov [edi+edx+8],r9 + mov r8,[esi+edx+16] + mov r9,[esi+edx+24] + mov [edi+edx+16],r8 + mov [edi+edx+24],r9 + add edx,0x20 + jmp realloc_copy32 + realloc_copy16: + cmp edx,-16 + jg realloc_copy8 + mov r8,[esi+edx] + mov r9,[esi+edx+8] + mov [edi+edx],r8 + mov [edi+edx+8],r9 + add edx,0x10 + jz realloc_copy_done + realloc_copy8: + cmp edx,-8 + jg realloc_copy4 + mov r8,[esi+edx] + mov [edi+edx],r8 + add edx,8 + jz realloc_copy_done + realloc_copy4: + cmp edx,-4 + jg realloc_copy2 + mov r8d,[esi+edx] + mov [edi+edx],r8d + add edx,4 + jz realloc_copy_done + realloc_copy2: + cmp edx,-2 + jg realloc_copy1 + movzx r8d,word [esi+edx] + mov [edi+edx],r8w + add edx,2 + jz realloc_copy_done + realloc_copy1: + cmp edx,-1 + jg realloc_copy_done + movzx r8d,byte [esi+edx] + mov [edi+edx],r8b + realloc_copy_done: + push rax rcx + mov eax,r14d + call mfree + pop rcx rax + mov edi,r12d + mov esi,r13d + ret + realloc_already_satisfied: + mov ecx,edx + ret + +minit: + mov eax,12 ; sys_brk + xor edi,edi + syscall + mov ecx,eax + cmp rcx,rax + jne minit_done + mov [malloc_fbrk],eax + mov [malloc_lbrk],eax + minit_done: + retn + +if used mcheck + + mcheck: + push rax rsi + mov esi,[malloc_fbrk] + test esi,esi + jz mcheck_ok + mcheck_walk: + cmp esi,[malloc_lbrk] + je mcheck_ok + ja mcheck_break + mov eax,[esi] + and eax,not 1 + test eax,eax + jz mcheck_break + add esi,eax + jmp mcheck_walk + mcheck_break: + jmp internal_error + mcheck_ok: + pop rsi rax + ret + +end if \ No newline at end of file diff --git a/x86_64_sse2_x87/fasm/source/linux/x64/selfhost.inc b/x86_64_sse2_x87/fasm/source/linux/x64/selfhost.inc new file mode 100644 index 0000000..de61195 --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/linux/x64/selfhost.inc @@ -0,0 +1,120 @@ + +include '../../../examples/x86/include/x64.inc' + +macro format?.ELF64? variant + match , variant + format binary as 'o' + include '../../../examples/x86/include/format/elf64.inc' + use64 + else match =executable? settings, variant: + ELF.Settings.Class = ELFCLASS64 + ELF.Settings.Machine = EM_X86_64 + ELF.Settings.BaseAddress = 400000h + match brand =at? base:, settings + ELF.Settings.ABI = brand + ELF.Settings.BaseAddress = base + else match =at? base:, settings + ELF.Settings.BaseAddress = base + else match brand:, settings + ELF.Settings.ABI = brand + end match + include '../../../examples/x86/include/format/elfexe.inc' + use64 + else + err 'invalid argument' + end match +end macro + +macro struct? name + macro ends?! + end namespace + esc end struc + virtual at 0 + name name + sizeof.name = $ + end virtual + purge ends? + end macro + esc struc name + label . : sizeof.name + namespace . +end macro + +macro use32on64? + + define esp rsp + + define promote + + iterate , eax,rax, ebx,rbx, ecx,rcx, edx,rdx, esi,rsi, edi,rdi, esp,rsp, ebp,rbp + promote.reg32? equ reg64 + end iterate + + iterate instr, jmp,call + calminstruction instr? arg + local tmp + match [tmp], arg + jyes zero_extend + transform arg, promote + arrange tmp, =instr arg + assemble tmp + exit + zero_extend: + arrange tmp, =mov =r8d,[tmp] + assemble tmp + arrange tmp, =instr =r8 + assemble tmp + end calminstruction + end iterate + + calminstruction push? arg + local car, cdr + match car cdr?, arg + loop: + transform car, promote + jno non_reg + arrange car, =push car + assemble car + match car cdr?, cdr + jyes loop + exit + non_reg: + arrange tmp, =mov =r8d,arg + assemble tmp + arrange tmp, =push =r8 + assemble tmp + exit + end calminstruction + + calminstruction pop? arg + local car, cdr + match car cdr?, arg + loop: + transform car, promote + jno non_reg + arrange car, =pop car + assemble car + match car cdr?, cdr + jyes loop + exit + non_reg: + arrange tmp, =pop =r8 + assemble tmp + arrange tmp, =mov arg,=r8d + assemble tmp + exit + end calminstruction + + macro jecxz? target + if target-($+1) < 80h & target-($+1) >= -80h + jecxz target + else + local j,k + jecxz j + jmp k + j: jmp target + k: + end if + end macro + +end macro diff --git a/x86_64_sse2_x87/fasm/source/linux/x64/system.inc b/x86_64_sse2_x87/fasm/source/linux/x64/system.inc new file mode 100644 index 0000000..90b3cd8 --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/linux/x64/system.inc @@ -0,0 +1,264 @@ + +LINE_FEED = 0Ah + +O_ACCMODE = 0003o +O_RDONLY = 0000o +O_WRONLY = 0001o +O_RDWR = 0002o +O_CREAT = 0100o +O_EXCL = 0200o +O_NOCTTY = 0400o +O_TRUNC = 1000o +O_APPEND = 2000o +O_NONBLOCK = 4000o + +S_ISUID = 4000o +S_ISGID = 2000o +S_ISVTX = 1000o +S_IRUSR = 0400o +S_IWUSR = 0200o +S_IXUSR = 0100o +S_IRGRP = 0040o +S_IWGRP = 0020o +S_IXGRP = 0010o +S_IROTH = 0004o +S_IWOTH = 0002o +S_IXOTH = 0001o + +system_init: + mov eax,201 ; sys_time + mov edi,timestamp + syscall + call minit + retn + +system_shutdown: + call mcheck + retn + +open: +; in: edx - path to file +; out: ebx = file handle, cf set on error +; preserves: esi, edi + push rsi rdi + call adapt_path + mov eax,2 ; sys_open + mov esi,O_RDONLY + xor edx,edx + syscall + pop rdi rsi + test eax,eax + js interface_error + mov ebx,eax + clc + ret + interface_error: + stc + ret + adapt_path: + xor ecx,ecx + mov edi,path_buffer + copy_path: + mov al,[edx+ecx] + cmp al,'\' + jne path_char_ok + mov al,'/' + path_char_ok: + cmp ecx,1000h + jae out_of_memory + mov [edi+ecx],al + inc ecx + test al,al + jnz copy_path + retn +create: +; in: edx - path to file +; out: ebx = file handle, cf set on error +; preserves: esi, edi + push rsi rdi + call adapt_path + mov esi,O_CREAT+O_TRUNC+O_WRONLY + mov edx,S_IRUSR+S_IWUSR+S_IRGRP+S_IROTH + mov eax,2 ; sys_open + syscall + pop rdi rsi + test eax,eax + js interface_error + mov ebx,eax + clc + retn +write: +; in: ebx = file handle, edx - data, ecx = number of bytes +; out: cf set on error +; preserves: ebx, esi, edi + push rsi rdi + mov eax,1 ; sys_write + mov edi,ebx + mov esi,edx + mov edx,ecx + syscall + pop rdi rsi + test eax,eax + js interface_error + cmp eax,edx + jne interface_error + clc + ret +read: +; in: ebx = file handle, edx - buffer, ecx = number of bytes +; out: cf set on error +; preserves: ebx, esi, edi + push rsi rdi + mov eax,0 ; sys_read + mov edi,ebx + mov esi,edx + mov edx,ecx + syscall + pop rdi rsi + test eax,eax + js interface_error + cmp eax,edx + jne interface_error + clc + ret +close: +; in: ebx = file handle +; preserves: ebx, esi, edi + push rdi + mov edi,ebx + mov eax,3 ; sys_close + syscall + pop rdi + ret +lseek: +; in: ebx = file handle, cl = method, edx:eax = offset +; out: edx:eax = new offset from the beginning of file, cf set on error +; preserves: ebx, esi, edi + push rsi rdi + mov edi,ebx + mov esi,edx + mov eax,eax + shl rsi,32 + or rsi,rax + xor edx,edx + mov dl,cl + mov eax,8 ; sys_lseek + syscall + pop rdi rsi + cmp rax,-1 + je interface_error + mov rdx,rax + shr rdx,32 + clc + ret + +get_timestamp: +; out: edx:eax = timestamp +; preserves: ebx, ecx, esi, edi + mov eax,dword [timestamp] + mov edx,dword [timestamp+4] + retn + +display_string: +; in: +; esi - string +; ecx = string length, zero for ASCIIZ string +; preserves: ebx, esi + push rbx rsi rbp + test ecx,ecx + jnz write_string_to_stdout + xor al,al + mov edi,esi + or ecx,-1 + repne scasb + neg ecx + sub ecx,2 + write_string_to_stdout: + mov eax,1 ; sys_write + mov edi,1 + mov edx,ecx + syscall + pop rbp rsi rbx + retn + +display_error_string: +; in: +; esi - string +; ecx = string length, zero for ASCIIZ string +; preserves: ebx, esi + push rbx rsi rbp + test ecx,ecx + jnz write_string_to_stderr + xor al,al + mov edi,esi + or ecx,-1 + repne scasb + neg ecx + sub ecx,2 + write_string_to_stderr: + mov eax,1 ; sys_write + mov edi,2 + mov edx,ecx + syscall + pop rbp rsi rbx + retn + +get_environment_variable: +; in: +; esi - name +; edi - buffer for value +; ecx = size of buffer +; out: +; eax = length of value +; preserves: ebx, esi, edi + push rbx rcx + mov rdx,[env] + scan_environment: + mov rbx,[rdx] + test rbx,rbx + jz no_environment_variable + xor ecx,ecx + compare_character: + mov al,[rbx+rcx] + mov ah,[esi+ecx] + inc ecx + cmp al,'=' + je end_of_variable_name + test ah,ah + jz next_variable + sub ah,al + je compare_character + cmp ah,20h + jne next_variable + cmp al,'A' + jb next_variable + cmp al,'Z' + jna compare_character + next_variable: + add rdx,8 + jmp scan_environment + end_of_variable_name: + test ah,ah + jnz next_variable + add rbx,rcx + pop rcx + xor eax,eax + copy_environment_variable: + mov dl,[rbx+rax] + cmp eax,ecx + jae next_environment_variable_character + mov [edi+eax],dl + next_environment_variable_character: + inc eax + test dl,dl + jnz copy_environment_variable + environment_variable_ok: + pop rbx + ret + no_environment_variable: + pop rcx + mov eax,1 + jecxz environment_variable_ok + and byte [edi],0 + pop rbx + ret diff --git a/x86_64_sse2_x87/fasm/source/macos/fasmg b/x86_64_sse2_x87/fasm/source/macos/fasmg new file mode 100644 index 0000000000000000000000000000000000000000..f9595e535276c8a0d98a28607ff58e9e95b9aa7b GIT binary patch literal 69966 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 literal 0 HcwPel00001 diff --git a/x86_64_sse2_x87/fasm/source/macos/fasmg.asm b/x86_64_sse2_x87/fasm/source/macos/fasmg.asm new file mode 100644 index 0000000..cf3a540 --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/macos/fasmg.asm @@ -0,0 +1,504 @@ + +match ,{ + + err ; fasm 1 assembly not supported + +} match -,{ +else + + include 'selfhost.inc' + +end match +_ equ } + +format MachO executable +entry start + +include '../version.inc' + +interpreter '/usr/lib/dyld' +uses '/usr/lib/libSystem.B.dylib' + +import libc.malloc,'_malloc',\ + libc.realloc,'_realloc',\ + libc.free,'_free',\ + libc.fopen,'_fopen',\ + libc.fclose,'_fclose',\ + libc.fread,'_fread',\ + libc.fwrite,'_fwrite',\ + libc.fseek,'_fseek',\ + libc.ftell,'_ftell',\ + libc.time,'_time',\ + libc.write,'_write',\ + getenv,'_getenv',\ + gettimeofday,'_gettimeofday',\ + exit,'_exit' + +struct timeval + time_t dd ? + suseconds_t dd ? +ends + +segment '__TEXT' readable executable + +section '__text' align 16 + + start: + mov ecx,[esp] + mov [argc],ecx + lea ebx,[esp+4] + mov [argv],ebx + + call system_init + + call get_arguments + mov bl,al + cmp [no_logo],0 + jne logo_ok + mov esi,_logo + xor ecx,ecx + call display_string + logo_ok: + test bl,bl + jnz display_usage_information + + xor al,al + mov ecx,[verbosity_level] + jecxz init + or al,TRACE_ERROR_STACK + dec ecx + jz init + or al,TRACE_DISPLAY + init: + call assembly_init + + ccall gettimeofday,start_time,0 + + assemble: + mov esi,[initial_commands] + mov edx,[source_path] + call assembly_pass + jc assembly_done + + mov eax,[current_pass] + cmp eax,[maximum_number_of_passes] + jb assemble + + call show_display_data + + mov esi,_error_prefix + xor ecx,ecx + call display_error_string + mov esi,_code_cannot_be_generated + xor ecx,ecx + call display_error_string + mov esi,_message_suffix + xor ecx,ecx + call display_error_string + + jmp assembly_failed + + assembly_done: + + call show_display_data + + cmp [first_error],0 + jne assembly_failed + + cmp [no_logo],0 + jne summary_done + mov eax,[current_pass] + xor edx,edx + call itoa + call display_string + mov esi,_passes + cmp [current_pass],1 + jne display_passes_suffix + mov esi,_pass + display_passes_suffix: + xor ecx,ecx + call display_string + ccall gettimeofday,end_time,0 + mov eax,[end_time.time_t] + sub eax,[start_time.time_t] + mov ecx,1000000 + mul ecx + add eax,[end_time.suseconds_t] + adc edx,0 + sub eax,[start_time.suseconds_t] + sbb edx,0 + add eax,50000 + mov ecx,1000000 + div ecx + mov ebx,eax + mov eax,edx + xor edx,edx + mov ecx,100000 + div ecx + mov [tenths_of_second],eax + xchg eax,ebx + or ebx,eax + jz display_output_length + xor edx,edx + call itoa + call display_string + mov esi,_message_suffix + mov ecx,1 + call display_string + mov eax,[tenths_of_second] + xor edx,edx + call itoa + call display_string + mov esi,_seconds + xor ecx,ecx + call display_string + display_output_length: + call get_output_length + push eax edx + call itoa + call display_string + pop edx eax + mov esi,_bytes + cmp eax,1 + jne display_bytes_suffix + test edx,edx + jnz display_bytes_suffix + mov esi,_byte + display_bytes_suffix: + xor ecx,ecx + call display_string + mov esi,_new_line + xor ecx,ecx + call display_string + summary_done: + + mov ebx,[source_path] + mov edi,[output_path] + call write_output_file + jc write_failed + + call assembly_shutdown + call system_shutdown + + ccall exit,0 + + assembly_failed: + + call show_errors + + call assembly_shutdown + call system_shutdown + + ccall exit,2 + + write_failed: + mov ebx,_write_failed + jmp fatal_error + + out_of_memory: + mov ebx,_out_of_memory + jmp fatal_error + + fatal_error: + + mov esi,_error_prefix + xor ecx,ecx + call display_error_string + mov esi,ebx + xor ecx,ecx + call display_error_string + mov esi,_message_suffix + xor ecx,ecx + call display_error_string + + call assembly_shutdown + call system_shutdown + + ccall exit,3 + + display_usage_information: + + mov esi,_usage + xor ecx,ecx + call display_string + + call system_shutdown + + ccall exit,1 + + get_arguments: + xor eax,eax + mov [initial_commands],eax + mov [source_path],eax + mov [output_path],eax + mov [no_logo],al + mov [verbosity_level],eax + mov [maximum_number_of_passes],100 + mov [maximum_number_of_errors],1 + mov [maximum_depth_of_stack],10000 + mov ecx,[argc] + mov ebx,[argv] + add ebx,4 + dec ecx + jz error_in_arguments + get_argument: + mov esi,[ebx] + mov al,[esi] + cmp al,'-' + je get_option + cmp [source_path],0 + jne get_output_file + mov [source_path],esi + jmp next_argument + get_output_file: + cmp [output_path],0 + jne error_in_arguments + mov [output_path],esi + jmp next_argument + get_option: + inc esi + lodsb + cmp al,'e' + je set_errors_limit + cmp al,'E' + je set_errors_limit + cmp al,'i' + je insert_initial_command + cmp al,'I' + je insert_initial_command + cmp al,'p' + je set_passes_limit + cmp al,'P' + je set_passes_limit + cmp al,'r' + je set_recursion_limit + cmp al,'R' + je set_recursion_limit + cmp al,'v' + je set_verbose_mode + cmp al,'V' + je set_verbose_mode + cmp al,'n' + je set_no_logo + cmp al,'N' + jne error_in_arguments + set_no_logo: + or [no_logo],-1 + cmp byte [esi],0 + je next_argument + error_in_arguments: + or al,-1 + ret + set_verbose_mode: + cmp byte [esi],0 + jne get_verbose_setting + dec ecx + jz error_in_arguments + add ebx,4 + mov esi,[ebx] + get_verbose_setting: + call get_option_value + cmp edx,2 + ja error_in_arguments + mov [verbosity_level],edx + jmp next_argument + set_errors_limit: + cmp byte [esi],0 + jne get_errors_setting + dec ecx + jz error_in_arguments + add ebx,4 + mov esi,[ebx] + get_errors_setting: + call get_option_value + test edx,edx + jz error_in_arguments + mov [maximum_number_of_errors],edx + jmp next_argument + set_recursion_limit: + cmp byte [esi],0 + jne get_recursion_setting + dec ecx + jz error_in_arguments + add ebx,4 + mov esi,[ebx] + get_recursion_setting: + call get_option_value + test edx,edx + jz error_in_arguments + mov [maximum_depth_of_stack],edx + jmp next_argument + set_passes_limit: + cmp byte [esi],0 + jne get_passes_setting + dec ecx + jz error_in_arguments + add ebx,4 + mov esi,[ebx] + get_passes_setting: + call get_option_value + test edx,edx + jz error_in_arguments + mov [maximum_number_of_passes],edx + next_argument: + add ebx,4 + dec ecx + jnz get_argument + cmp [source_path],0 + je error_in_arguments + xor al,al + ret + get_option_value: + xor eax,eax + mov edx,eax + find_option_value: + cmp byte [esi],20h + jne get_option_digit + inc esi + jmp find_option_value + get_option_digit: + lodsb + test al,al + jz option_value_ok + sub al,30h + jc invalid_option_value + cmp al,9 + ja invalid_option_value + imul edx,10 + jo invalid_option_value + add edx,eax + jc invalid_option_value + jmp get_option_digit + option_value_ok: + dec esi + clc + ret + invalid_option_value: + stc + ret + insert_initial_command: + cmp byte [esi],0 + jne measure_initial_command + dec ecx + jz error_in_arguments + add ebx,4 + mov esi,[ebx] + measure_initial_command: + push ebx ecx edi + mov edi,esi + or ecx,-1 + xor al,al + repne scasb + not ecx + dec ecx + mov edi,[initial_commands] + lea eax,[ecx+2] + test edi,edi + jz allocate_initial_commands_buffer + mov edx,[initial_commands_length] + add edi,edx + add eax,edx + cmp eax,[initial_commands_maximum_length] + ja grow_initial_commands_buffer + copy_initial_command: + rep movsb + mov ax,0Ah + stosw + dec edi + sub edi,[initial_commands] + mov [initial_commands_length],edi + pop edi ecx ebx + jmp next_argument + allocate_initial_commands_buffer: + push ecx + mov ecx,eax + call malloc + mov [initial_commands],eax + mov [initial_commands_maximum_length],ecx + mov edi,eax + pop ecx + jmp copy_initial_command + grow_initial_commands_buffer: + push ecx + mov ecx,eax + mov eax,[initial_commands] + call realloc + mov [initial_commands],eax + mov [initial_commands_maximum_length],ecx + mov edi,eax + add edi,[initial_commands_length] + pop ecx + jmp copy_initial_command + + include 'system.inc' + + include '../assembler.inc' + include '../symbols.inc' + include '../expressions.inc' + include '../conditions.inc' + include '../floats.inc' + include '../directives.inc' + include '../calm.inc' + include '../errors.inc' + include '../map.inc' + include '../reader.inc' + include '../output.inc' + include '../console.inc' + +section '__cstring' align 4 + + _logo db 'flat assembler version g.',VERSION,10,0 + + _usage db 'Usage: fasmg source [output]',10 + db 'Optional settings:',10 + db ' -p limit Set the maximum allowed number of passes (default 100)',10 + db ' -e limit Set the maximum number of displayed errors (default 1)',10 + db ' -r limit Set the maximum depth of stack (default 10000)',10 + db ' -v flag Enable or disable showing all lines from the stack (default 0)',10 + db ' -i command Insert instruction at the beginning of source',13,10 + db ' -n Do not show logo nor summary',13,10 + db 0 + + _pass db ' pass, ',0 + _passes db ' passes, ',0 + _dot db '.' + _seconds db ' seconds, ',0 + _byte db ' byte.',0 + _bytes db ' bytes.',0 + + _write_failed db 'failed to write the output file',0 + _out_of_memory db 'not enough memory to complete the assembly',0 + _code_cannot_be_generated db 'could not generate code within the allowed number of passes',0 + + _open_mode db 'r',0 + _create_mode db 'w',0 + + include '../tables.inc' + include '../messages.inc' + +segment '__DATA' readable writable + +section '__bss' align 4 + + include '../variables.inc' + + source_path dd ? + output_path dd ? + maximum_number_of_passes dd ? + + initial_commands dd ? + initial_commands_length dd ? + initial_commands_maximum_length dd ? + + argc dd ? + argv dd ? + timestamp dq ? + + start_time timeval + end_time timeval + tenths_of_second dd ? + + verbosity_level dd ? + no_logo db ? + + path_buffer rb 1000h diff --git a/x86_64_sse2_x87/fasm/source/macos/fasmg.o.asm b/x86_64_sse2_x87/fasm/source/macos/fasmg.o.asm new file mode 100644 index 0000000..6c6117f --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/macos/fasmg.o.asm @@ -0,0 +1,498 @@ + +match ,{ + + err ; fasm 1 assembly not supported + +} match -,{ +else + + include 'selfhost.inc' + +end match +_ equ } + +format MachO +public main as '_main' + +include '../version.inc' + +extrn '_malloc' as libc.malloc +extrn '_realloc' as libc.realloc +extrn '_free' as libc.free +extrn '_fopen' as libc.fopen +extrn '_fclose' as libc.fclose +extrn '_fread' as libc.fread +extrn '_fwrite' as libc.fwrite +extrn '_fseek' as libc.fseek +extrn '_ftell' as libc.ftell +extrn '_time' as libc.time +extrn '_write' as libc.write + +extrn '_getenv' as getenv +extrn '_gettimeofday' as gettimeofday +extrn '_exit' as exit + +struct timeval + time_t dd ? + suseconds_t dd ? +ends + +section '__TEXT':'__text' align 16 + + main: + mov ecx,[esp+4] + mov [argc],ecx + mov ebx,[esp+8] + mov [argv],ebx + + call system_init + + call get_arguments + mov bl,al + cmp [no_logo],0 + jne logo_ok + mov esi,_logo + xor ecx,ecx + call display_string + logo_ok: + test bl,bl + jnz display_usage_information + + xor al,al + mov ecx,[verbosity_level] + jecxz init + or al,TRACE_ERROR_STACK + dec ecx + jz init + or al,TRACE_DISPLAY + init: + call assembly_init + + ccall gettimeofday,start_time,0 + + assemble: + mov esi,[initial_commands] + mov edx,[source_path] + call assembly_pass + jc assembly_done + + mov eax,[current_pass] + cmp eax,[maximum_number_of_passes] + jb assemble + + call show_display_data + + mov esi,_error_prefix + xor ecx,ecx + call display_error_string + mov esi,_code_cannot_be_generated + xor ecx,ecx + call display_error_string + mov esi,_message_suffix + xor ecx,ecx + call display_error_string + + jmp assembly_failed + + assembly_done: + + call show_display_data + + cmp [first_error],0 + jne assembly_failed + + cmp [no_logo],0 + jne summary_done + mov eax,[current_pass] + xor edx,edx + call itoa + call display_string + mov esi,_passes + cmp [current_pass],1 + jne display_passes_suffix + mov esi,_pass + display_passes_suffix: + xor ecx,ecx + call display_string + ccall gettimeofday,end_time,0 + mov eax,[end_time.time_t] + sub eax,[start_time.time_t] + mov ecx,1000000 + mul ecx + add eax,[end_time.suseconds_t] + adc edx,0 + sub eax,[start_time.suseconds_t] + sbb edx,0 + add eax,50000 + mov ecx,1000000 + div ecx + mov ebx,eax + mov eax,edx + xor edx,edx + mov ecx,100000 + div ecx + mov [tenths_of_second],eax + xchg eax,ebx + or ebx,eax + jz display_output_length + xor edx,edx + call itoa + call display_string + mov esi,_message_suffix + mov ecx,1 + call display_string + mov eax,[tenths_of_second] + xor edx,edx + call itoa + call display_string + mov esi,_seconds + xor ecx,ecx + call display_string + display_output_length: + call get_output_length + push eax edx + call itoa + call display_string + pop edx eax + mov esi,_bytes + cmp eax,1 + jne display_bytes_suffix + test edx,edx + jnz display_bytes_suffix + mov esi,_byte + display_bytes_suffix: + xor ecx,ecx + call display_string + mov esi,_new_line + xor ecx,ecx + call display_string + summary_done: + + mov ebx,[source_path] + mov edi,[output_path] + call write_output_file + jc write_failed + + call assembly_shutdown + call system_shutdown + + ccall exit,0 + + assembly_failed: + + call show_errors + + call assembly_shutdown + call system_shutdown + + ccall exit,2 + + write_failed: + mov ebx,_write_failed + jmp fatal_error + + out_of_memory: + mov ebx,_out_of_memory + jmp fatal_error + + fatal_error: + + mov esi,_error_prefix + xor ecx,ecx + call display_error_string + mov esi,ebx + xor ecx,ecx + call display_error_string + mov esi,_message_suffix + xor ecx,ecx + call display_error_string + + call assembly_shutdown + call system_shutdown + + ccall exit,3 + + display_usage_information: + + mov esi,_usage + xor ecx,ecx + call display_string + + call system_shutdown + + ccall exit,1 + + get_arguments: + xor eax,eax + mov [initial_commands],eax + mov [source_path],eax + mov [output_path],eax + mov [no_logo],al + mov [verbosity_level],eax + mov [maximum_number_of_passes],100 + mov [maximum_number_of_errors],1 + mov [maximum_depth_of_stack],10000 + mov ecx,[argc] + mov ebx,[argv] + add ebx,4 + dec ecx + jz error_in_arguments + get_argument: + mov esi,[ebx] + mov al,[esi] + cmp al,'-' + je get_option + cmp [source_path],0 + jne get_output_file + mov [source_path],esi + jmp next_argument + get_output_file: + cmp [output_path],0 + jne error_in_arguments + mov [output_path],esi + jmp next_argument + get_option: + inc esi + lodsb + cmp al,'e' + je set_errors_limit + cmp al,'E' + je set_errors_limit + cmp al,'i' + je insert_initial_command + cmp al,'I' + je insert_initial_command + cmp al,'p' + je set_passes_limit + cmp al,'P' + je set_passes_limit + cmp al,'r' + je set_recursion_limit + cmp al,'R' + je set_recursion_limit + cmp al,'v' + je set_verbose_mode + cmp al,'V' + je set_verbose_mode + cmp al,'n' + je set_no_logo + cmp al,'N' + jne error_in_arguments + set_no_logo: + or [no_logo],-1 + cmp byte [esi],0 + je next_argument + error_in_arguments: + or al,-1 + ret + set_verbose_mode: + cmp byte [esi],0 + jne get_verbose_setting + dec ecx + jz error_in_arguments + add ebx,4 + mov esi,[ebx] + get_verbose_setting: + call get_option_value + cmp edx,2 + ja error_in_arguments + mov [verbosity_level],edx + jmp next_argument + set_errors_limit: + cmp byte [esi],0 + jne get_errors_setting + dec ecx + jz error_in_arguments + add ebx,4 + mov esi,[ebx] + get_errors_setting: + call get_option_value + test edx,edx + jz error_in_arguments + mov [maximum_number_of_errors],edx + jmp next_argument + set_recursion_limit: + cmp byte [esi],0 + jne get_recursion_setting + dec ecx + jz error_in_arguments + add ebx,4 + mov esi,[ebx] + get_recursion_setting: + call get_option_value + test edx,edx + jz error_in_arguments + mov [maximum_depth_of_stack],edx + jmp next_argument + set_passes_limit: + cmp byte [esi],0 + jne get_passes_setting + dec ecx + jz error_in_arguments + add ebx,4 + mov esi,[ebx] + get_passes_setting: + call get_option_value + test edx,edx + jz error_in_arguments + mov [maximum_number_of_passes],edx + next_argument: + add ebx,4 + dec ecx + jnz get_argument + cmp [source_path],0 + je error_in_arguments + xor al,al + ret + get_option_value: + xor eax,eax + mov edx,eax + find_option_value: + cmp byte [esi],20h + jne get_option_digit + inc esi + jmp find_option_value + get_option_digit: + lodsb + test al,al + jz option_value_ok + sub al,30h + jc invalid_option_value + cmp al,9 + ja invalid_option_value + imul edx,10 + jo invalid_option_value + add edx,eax + jc invalid_option_value + jmp get_option_digit + option_value_ok: + dec esi + clc + ret + invalid_option_value: + stc + ret + insert_initial_command: + cmp byte [esi],0 + jne measure_initial_command + dec ecx + jz error_in_arguments + add ebx,4 + mov esi,[ebx] + measure_initial_command: + push ebx ecx edi + mov edi,esi + or ecx,-1 + xor al,al + repne scasb + not ecx + dec ecx + mov edi,[initial_commands] + lea eax,[ecx+2] + test edi,edi + jz allocate_initial_commands_buffer + mov edx,[initial_commands_length] + add edi,edx + add eax,edx + cmp eax,[initial_commands_maximum_length] + ja grow_initial_commands_buffer + copy_initial_command: + rep movsb + mov ax,0Ah + stosw + dec edi + sub edi,[initial_commands] + mov [initial_commands_length],edi + pop edi ecx ebx + jmp next_argument + allocate_initial_commands_buffer: + push ecx + mov ecx,eax + call malloc + mov [initial_commands],eax + mov [initial_commands_maximum_length],ecx + mov edi,eax + pop ecx + jmp copy_initial_command + grow_initial_commands_buffer: + push ecx + mov ecx,eax + mov eax,[initial_commands] + call realloc + mov [initial_commands],eax + mov [initial_commands_maximum_length],ecx + mov edi,eax + add edi,[initial_commands_length] + pop ecx + jmp copy_initial_command + + include 'system.inc' + + include '../assembler.inc' + include '../symbols.inc' + include '../expressions.inc' + include '../conditions.inc' + include '../floats.inc' + include '../directives.inc' + include '../calm.inc' + include '../errors.inc' + include '../map.inc' + include '../reader.inc' + include '../output.inc' + include '../console.inc' + +section '__TEXT':'__cstring' align 4 + + _logo db 'flat assembler version g.',VERSION,10,0 + + _usage db 'Usage: fasmg source [output]',10 + db 'Optional settings:',10 + db ' -p limit Set the maximum allowed number of passes (default 100)',10 + db ' -e limit Set the maximum number of displayed errors (default 1)',10 + db ' -r limit Set the maximum depth of stack (default 10000)',10 + db ' -v flag Enable or disable showing all lines from the stack (default 0)',10 + db ' -i command Insert instruction at the beginning of source',13,10 + db ' -n Do not show logo nor summary',13,10 + db 0 + + _pass db ' pass, ',0 + _passes db ' passes, ',0 + _dot db '.' + _seconds db ' seconds, ',0 + _byte db ' byte.',0 + _bytes db ' bytes.',0 + + _write_failed db 'failed to write the output file',0 + _out_of_memory db 'not enough memory to complete the assembly',0 + _code_cannot_be_generated db 'could not generate code within the allowed number of passes',0 + + _open_mode db 'r',0 + _create_mode db 'w',0 + + include '../tables.inc' + include '../messages.inc' + +section '__DATA':'__data' align 4 + + include '../variables.inc' + + source_path dd ? + output_path dd ? + maximum_number_of_passes dd ? + + initial_commands dd ? + initial_commands_length dd ? + initial_commands_maximum_length dd ? + + argc dd ? + argv dd ? + timestamp dq ? + + start_time timeval + end_time timeval + tenths_of_second dd ? + + verbosity_level dd ? + no_logo db ? + + path_buffer rb 1000h diff --git a/x86_64_sse2_x87/fasm/source/macos/selfhost.inc b/x86_64_sse2_x87/fasm/source/macos/selfhost.inc new file mode 100644 index 0000000..4ab1c87 --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/macos/selfhost.inc @@ -0,0 +1,50 @@ + +include '../../examples/x86/include/80386.inc' + +macro format?.MachO? variant + match , variant + MachO.Settings.FileType equ MH_OBJECT + include '../../examples/x86/include/format/macho.inc' + use32 + else match =executable?, variant + MachO.Settings.BaseAddress = 0x1000 + include '../../examples/x86/include/format/macho.inc' + use32 + else + err 'invalid argument' + end match +end macro + +macro struct? name + macro ends?! + end namespace + esc end struc + virtual at 0 + name name + sizeof.name = $ + end virtual + purge ends? + end macro + esc struc name + label . : sizeof.name + namespace . +end macro + +macro ccall? proc*,args& + local size + mov ebp,esp + sub esp,size + and esp,0FFFFFFF0h + match any, args + iterate arg, args + mov dword [esp+(%-1)*4],arg + if % = 1 + size := %%*4 + end if + end iterate + else + size := 0 + end match + call proc + mov esp,ebp +end macro diff --git a/x86_64_sse2_x87/fasm/source/macos/system.inc b/x86_64_sse2_x87/fasm/source/macos/system.inc new file mode 100644 index 0000000..00c833e --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/macos/system.inc @@ -0,0 +1,224 @@ + +LINE_FEED = 0Ah + +system_init: + ccall libc.time,timestamp + retn + +system_shutdown: + retn + +malloc: +malloc_fixed: +malloc_growable: +; in: ecx = requested size +; out: eax - allocated block, ecx = allocated size, on error jumps to out_of_memory (does not return) +; preserves: ebx, esi, edi +; note: +; use of malloc_fixed hints that block will be kept as is until the end of assembly +; use of malloc_growable hints that block is likely to be resized + push ebx ecx esi edi + ccall libc.malloc,ecx + pop edi esi ecx ebx + test eax,eax + jz out_of_memory + retn +realloc: +; in: eax - memory block, ecx = requested size +; out: eax - resized block, ecx = allocated size, on error jumps to out_of_memory (does not return) +; preserves: ebx, esi, edi + push ebx ecx esi edi + ccall libc.realloc,eax,ecx + pop edi esi ecx ebx + test eax,eax + jz out_of_memory + retn +mfree: +; in: eax - memory block +; out: cf set on error +; preserves: ebx, esi, edi +; note: eax may have value 0 or -1, it should be treated as invalid input then + test eax,eax + jz interface_error + cmp eax,-1 + je interface_error + push ebx esi edi + ccall libc.free,eax + pop edi esi ebx + clc + retn + interface_error: + stc + retn + +open: +; in: edx - path to file +; out: ebx = file handle, cf set on error +; preserves: esi, edi + push esi edi + call adapt_path + ccall libc.fopen,ebx,_open_mode + pop edi esi + test eax,eax + jz interface_error + mov ebx,eax + clc + retn + adapt_path: + xor ecx,ecx + mov ebx,path_buffer + copy_path: + mov al,[edx+ecx] + cmp al,'\' + jne path_char_ok + mov al,'/' + path_char_ok: + cmp ecx,1000h + jae out_of_memory + mov [ebx+ecx],al + inc ecx + test al,al + jnz copy_path + retn +create: +; in: edx - path to file +; out: ebx = file handle, cf set on error +; preserves: esi, edi + push esi edi + call adapt_path + ccall libc.fopen,ebx,_create_mode + pop edi esi + test eax,eax + jz interface_error + mov ebx,eax + clc + retn +write: +; in: ebx = file handle, edx - data, ecx = number of bytes +; out: cf set on error +; preserves: ebx, esi, edi + push ebx ecx esi edi + ccall libc.fwrite,edx,1,ecx,ebx + pop edi esi ecx ebx + cmp eax,ecx + jne interface_error + clc + ret +read: +; in: ebx = file handle, edx - buffer, ecx = number of bytes +; out: cf set on error +; preserves: ebx, esi, edi + push ebx ecx esi edi + ccall libc.fread,edx,1,ecx,ebx + pop edi esi ecx ebx + cmp eax,ecx + jne interface_error + clc + ret +close: +; in: ebx = file handle +; preserves: ebx, esi, edi + ccall libc.fclose,ebx + ret +lseek: +; in: ebx = file handle, cl = method, edx:eax = offset +; out: edx:eax = new offset from the beginning of file, cf set on error +; preserves: ebx, esi, edi + test edx,edx + jnz interface_error + push esi edi ebx + movzx ecx,cl + ccall libc.fseek,ebx,eax,ecx + test eax,eax + jnz lseek_error + mov ebx,[esp] + ccall libc.ftell,ebx + cmp eax,-1 + je lseek_error + xor edx,edx + pop ebx edi esi + clc + ret + lseek_error: + pop ebx edi esi + stc + ret + +get_timestamp: +; out: edx:eax = timestamp +; preserves: ebx, ecx, esi, edi + mov eax,dword [timestamp] + mov edx,dword [timestamp+4] + retn + +display_string: +; in: +; esi - string +; ecx = string length, zero for ASCIIZ string +; preserves: ebx, esi + push ebx esi + test ecx,ecx + jnz write_string_to_stdout + xor al,al + mov edi,esi + or ecx,-1 + repne scasb + neg ecx + sub ecx,2 + write_string_to_stdout: + ccall libc.write,1,esi,ecx + pop esi ebx + retn + +display_error_string: +; in: +; esi - string +; ecx = string length, zero for ASCIIZ string +; preserves: ebx, esi + push ebx esi + test ecx,ecx + jnz write_string_to_stderr + xor al,al + mov edi,esi + or ecx,-1 + repne scasb + neg ecx + sub ecx,2 + write_string_to_stderr: + ccall libc.write,2,esi,ecx + pop esi ebx + retn + +get_environment_variable: +; in: +; esi - name +; edi - buffer for value +; ecx = size of buffer +; out: +; eax = length of value +; preserves: ebx, esi, edi + push ebx ecx esi edi + ccall getenv,esi + pop edi esi ecx ebx + test eax,eax + jz no_environment_variable + push esi + mov esi,eax + xor eax,eax + copy_environment_variable: + mov dl,[esi+eax] + cmp eax,ecx + jae next_environment_variable_character + mov [edi+eax],dl + next_environment_variable_character: + inc eax + test dl,dl + jnz copy_environment_variable + pop esi + environment_variable_ok: + ret + no_environment_variable: + mov eax,1 + jecxz environment_variable_ok + and byte [edi],0 + ret diff --git a/x86_64_sse2_x87/fasm/source/macos/x64/fasmg b/x86_64_sse2_x87/fasm/source/macos/x64/fasmg new file mode 100644 index 0000000000000000000000000000000000000000..4a40d570c9220865ab9fde26cc49b61de2ddddfa GIT binary patch literal 74118 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 literal 0 HcwPel00001 diff --git a/x86_64_sse2_x87/fasm/source/macos/x64/fasmg.asm b/x86_64_sse2_x87/fasm/source/macos/x64/fasmg.asm new file mode 100644 index 0000000..e4c145f --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/macos/x64/fasmg.asm @@ -0,0 +1,535 @@ + +; Adapted and tested by Jacob Young (jacobly.alt@gmail.com) + +match ,{ + + err ; fasm 1 assembly not supported + +} match -,{ +else + + include 'selfhost.inc' + +end match +_ equ } + +format MachO64 executable +entry start + +include '../../version.inc' + +interpreter '/usr/lib/dyld' +uses '/usr/lib/libSystem.B.dylib' + +import libc.malloc,'_malloc',\ + libc.realloc,'_realloc',\ + libc.free,'_free',\ + libc.fopen,'_fopen',\ + libc.fclose,'_fclose',\ + libc.fread,'_fread',\ + libc.fwrite,'_fwrite',\ + libc.fseek,'_fseek',\ + libc.ftell,'_ftell',\ + libc.time,'_time',\ + libc.write,'_write',\ + getenv,'_getenv',\ + gettimeofday,'_gettimeofday',\ + exit,'_exit' + +struct timeval + time_t dq ? + suseconds_t dd ?,? +ends + +segment '__TEXT' readable executable + +section '__text' align 16 + + start: + mov rcx,[rsp] + mov [argc],rcx + lea rbx,[rsp+8] + mov [argv],rbx + + call system_init + + call get_arguments + mov bl,al + cmp [no_logo],0 + jne logo_ok + mov esi,_logo + xor ecx,ecx + call display_string + logo_ok: + test bl,bl + jnz display_usage_information + + xor al,al + mov ecx,[verbosity_level] + jecxz init + or al,TRACE_ERROR_STACK + dec ecx + jz init + or al,TRACE_DISPLAY + init: + call assembly_init + + ccall gettimeofday,start_time,0 + + assemble: + mov esi,[initial_commands] + mov edx,[source_path] + call assembly_pass + jc assembly_done + + mov eax,[current_pass] + cmp eax,[maximum_number_of_passes] + jb assemble + + call show_display_data + + mov esi,_error_prefix + xor ecx,ecx + call display_error_string + mov esi,_code_cannot_be_generated + xor ecx,ecx + call display_error_string + mov esi,_message_suffix + xor ecx,ecx + call display_error_string + + jmp assembly_failed + + assembly_done: + + call show_display_data + + cmp [first_error],0 + jne assembly_failed + + cmp [no_logo],0 + jne summary_done + mov eax,[current_pass] + xor edx,edx + call itoa + call display_string + mov esi,_passes + cmp [current_pass],1 + jne display_passes_suffix + mov esi,_pass + display_passes_suffix: + xor ecx,ecx + call display_string + ccall gettimeofday,end_time,0 + mov rax,[end_time.time_t] + sub rax,[start_time.time_t] + mov rcx,1000000 + mul rcx + mov ecx,[end_time.suseconds_t] + add ecx,50000 + add rax,rcx + adc rdx,0 + mov ecx,[start_time.suseconds_t] + sub rax,rcx + sbb rdx,0 + mov rcx,1000000 + div rcx + mov rbx,rax + mov rax,rdx + xor edx,edx + mov rcx,100000 + div rcx + mov [tenths_of_second],eax + xchg rax,rbx + or rbx,rax + jz display_output_length + mov rdx,rax + shr rdx,32 + call itoa + call display_string + mov esi,_message_suffix + mov ecx,1 + call display_string + mov eax,[tenths_of_second] + xor edx,edx + call itoa + call display_string + mov esi,_seconds + xor ecx,ecx + call display_string + display_output_length: + call get_output_length + push rax rdx + call itoa + call display_string + pop rdx rax + mov esi,_bytes + cmp eax,1 + jne display_bytes_suffix + test edx,edx + jnz display_bytes_suffix + mov esi,_byte + display_bytes_suffix: + xor ecx,ecx + call display_string + mov esi,_new_line + xor ecx,ecx + call display_string + summary_done: + + mov ebx,[source_path] + mov edi,[output_path] + call write_output_file + jc write_failed + + call assembly_shutdown + call system_shutdown + + ccall exit,0 + + assembly_failed: + + call show_errors + + call assembly_shutdown + call system_shutdown + + ccall exit,2 + + write_failed: + mov ebx,_write_failed + jmp fatal_error + + out_of_memory: + mov ebx,_out_of_memory + jmp fatal_error + + fatal_error: + + mov esi,_error_prefix + xor ecx,ecx + call display_error_string + mov esi,ebx + xor ecx,ecx + call display_error_string + mov esi,_message_suffix + xor ecx,ecx + call display_error_string + + call assembly_shutdown + call system_shutdown + + ccall exit,3 + + display_usage_information: + + mov esi,_usage + xor ecx,ecx + call display_string + + call system_shutdown + + ccall exit,1 + + get_arguments: + xor eax,eax + mov [initial_commands],eax + mov [source_path],eax + mov [output_path],eax + mov [no_logo],al + mov [verbosity_level],eax + mov [maximum_number_of_passes],100 + mov [maximum_number_of_errors],1 + mov [maximum_depth_of_stack],10000 + mov rcx,[argc] + mov rbx,[argv] + add rbx,8 + dec ecx + jz error_in_arguments + get_argument: + mov rsi,[rbx] + mov al,[rsi] + cmp al,'-' + je get_option + cmp [source_path],0 + jne get_output_file + call strdup + mov [source_path],eax + jmp next_argument + get_output_file: + cmp [output_path],0 + jne error_in_arguments + call strdup + mov [output_path],eax + jmp next_argument + get_option: + inc rsi + lodsb + cmp al,'e' + je set_errors_limit + cmp al,'E' + je set_errors_limit + cmp al,'i' + je insert_initial_command + cmp al,'I' + je insert_initial_command + cmp al,'p' + je set_passes_limit + cmp al,'P' + je set_passes_limit + cmp al,'r' + je set_recursion_limit + cmp al,'R' + je set_recursion_limit + cmp al,'v' + je set_verbose_mode + cmp al,'V' + je set_verbose_mode + cmp al,'n' + je set_no_logo + cmp al,'N' + jne error_in_arguments + set_no_logo: + or [no_logo],-1 + cmp byte [rsi],0 + je next_argument + error_in_arguments: + or al,-1 + ret + set_verbose_mode: + cmp byte [rsi],0 + jne get_verbose_setting + dec ecx + jz error_in_arguments + add rbx,8 + mov rsi,[rbx] + get_verbose_setting: + call get_option_value + cmp edx,2 + ja error_in_arguments + mov [verbosity_level],edx + jmp next_argument + set_errors_limit: + cmp byte [rsi],0 + jne get_errors_setting + dec ecx + jz error_in_arguments + add rbx,8 + mov rsi,[rbx] + get_errors_setting: + call get_option_value + test edx,edx + jz error_in_arguments + mov [maximum_number_of_errors],edx + jmp next_argument + set_recursion_limit: + cmp byte [rsi],0 + jne get_recursion_setting + dec ecx + jz error_in_arguments + add rbx,8 + mov rsi,[rbx] + get_recursion_setting: + call get_option_value + test edx,edx + jz error_in_arguments + mov [maximum_depth_of_stack],edx + jmp next_argument + set_passes_limit: + cmp byte [rsi],0 + jne get_passes_setting + dec ecx + jz error_in_arguments + add rbx,8 + mov rsi,[rbx] + get_passes_setting: + call get_option_value + test edx,edx + jz error_in_arguments + mov [maximum_number_of_passes],edx + next_argument: + add rbx,8 + dec ecx + jnz get_argument + cmp [source_path],0 + je error_in_arguments + xor al,al + ret + get_option_value: + xor eax,eax + mov edx,eax + find_option_value: + cmp byte [rsi],20h + jne get_option_digit + inc rsi + jmp find_option_value + get_option_digit: + lodsb + test al,al + jz option_value_ok + sub al,30h + jc invalid_option_value + cmp al,9 + ja invalid_option_value + imul edx,10 + jo invalid_option_value + add edx,eax + jc invalid_option_value + jmp get_option_digit + option_value_ok: + dec rsi + clc + ret + invalid_option_value: + stc + ret + insert_initial_command: + cmp byte [rsi],0 + jne measure_initial_command + dec ecx + jz error_in_arguments + add rbx,8 + mov rsi,[rbx] + measure_initial_command: + push rbx rcx rdi + mov rdi,rsi + or ecx,-1 + xor al,al + repne scasb + not ecx + dec ecx + mov edi,[initial_commands] + lea eax,[ecx+2] + test edi,edi + jz allocate_initial_commands_buffer + mov edx,[initial_commands_length] + add edi,edx + add eax,edx + cmp eax,[initial_commands_maximum_length] + ja grow_initial_commands_buffer + copy_initial_command: + rep movsb + mov ax,0Ah + stosw + dec edi + sub edi,[initial_commands] + mov [initial_commands_length],edi + pop rdi rcx rbx + jmp next_argument + allocate_initial_commands_buffer: + push rsi rcx + mov ecx,eax + call malloc + mov [initial_commands],eax + mov [initial_commands_maximum_length],ecx + mov edi,eax + pop rcx rsi + jmp copy_initial_command + grow_initial_commands_buffer: + push rsi rcx + mov ecx,eax + mov eax,[initial_commands] + call realloc + mov [initial_commands],eax + mov [initial_commands_maximum_length],ecx + mov edi,eax + add edi,[initial_commands_length] + pop rcx rsi + jmp copy_initial_command + + strdup: + ; in: rsi - ASCIIZ string + ; out: eax - copy of the string in 32-bit addressable memory + ; preserves: rbx, rcx, rsi + push rbx rcx rsi + mov rdi,rsi + or ecx,-1 + xor al,al + repne scasb + not ecx + push rsi rcx + call malloc + pop rcx rsi + mov edi,eax + rep movsb + pop rsi rcx rbx + ret + + include 'system.inc' + + use32on64 + + include '../../assembler.inc' + include '../../symbols.inc' + include '../../expressions.inc' + include '../../conditions.inc' + include '../../floats.inc' + include '../../directives.inc' + include '../../calm.inc' + include '../../errors.inc' + include '../../map.inc' + include '../../reader.inc' + include '../../output.inc' + include '../../console.inc' + +section '__cstring' align 4 + + _logo db 'flat assembler version g.',VERSION,10,0 + + _usage db 'Usage: fasmg source [output]',10 + db 'Optional settings:',10 + db ' -p limit Set the maximum allowed number of passes (default 100)',10 + db ' -e limit Set the maximum number of displayed errors (default 1)',10 + db ' -r limit Set the maximum depth of stack (default 10000)',10 + db ' -v flag Enable or disable showing all lines from the stack (default 0)',10 + db ' -i command Insert instruction at the beginning of source',13,10 + db ' -n Do not show logo nor summary',13,10 + db 0 + + _pass db ' pass, ',0 + _passes db ' passes, ',0 + _dot db '.' + _seconds db ' seconds, ',0 + _byte db ' byte.',0 + _bytes db ' bytes.',0 + + _write_failed db 'failed to write the output file',0 + _out_of_memory db 'not enough memory to complete the assembly',0 + _code_cannot_be_generated db 'could not generate code within the allowed number of passes',0 + + _open_mode db 'r',0 + _create_mode db 'w',0 + + include '../../tables.inc' + include '../../messages.inc' + +segment '__DATA' readable writable + +section '__bss' align 4 + + include '../../variables.inc' + + source_path dd ? + output_path dd ? + maximum_number_of_passes dd ? + + initial_commands dd ? + initial_commands_length dd ? + initial_commands_maximum_length dd ? + + files dd ? + files_count dd ? + files_maximum_count dd ? + + argc dq ? + argv dq ? + timestamp dq ? + + start_time timeval + end_time timeval + tenths_of_second dd ? + + verbosity_level dd ? + no_logo db ? + + path_buffer rb 1000h diff --git a/x86_64_sse2_x87/fasm/source/macos/x64/selfhost.inc b/x86_64_sse2_x87/fasm/source/macos/x64/selfhost.inc new file mode 100644 index 0000000..12a3549 --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/macos/x64/selfhost.inc @@ -0,0 +1,41 @@ + +; Adapted and tested by Jacob Young (jacobly.alt@gmail.com) + +include '../../linux/x64/selfhost.inc' + +macro format?.MachO64? variant + match , variant + MachO.Settings.ProcessorType = CPU_TYPE_X86_64 + MachO.Settings.FileType equ MH_OBJECT + include '../../../examples/x86/include/format/macho.inc' + use64 + else match =executable?, variant + MachO.Settings.ProcessorType = CPU_TYPE_X86_64 + MachO.Settings.BaseAddress = 0x1000 + include '../../../examples/x86/include/format/macho.inc' + use64 + else + err 'invalid argument' + end match +end macro + +iterate reg, rdi, rsi, rdx, rcx, r8, r9 + arguments.%? equ reg +end iterate + +macro ccall? proc*,args& + local size + mov rbp,rsp + and rsp,0FFFFFFFFFFFFFFF0h + match any, args + iterate arg, args + if sizeof (arg) + lea arguments.%,[arg] + else + mov arguments.%,arg + end if + end iterate + end match + call proc + mov rsp,rbp +end macro diff --git a/x86_64_sse2_x87/fasm/source/macos/x64/system.inc b/x86_64_sse2_x87/fasm/source/macos/x64/system.inc new file mode 100644 index 0000000..efa2bb2 --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/macos/x64/system.inc @@ -0,0 +1,264 @@ + +; Adapted and tested by Jacob Young (jacobly.alt@gmail.com) + +LINE_FEED = 0Ah + +system_init: + ccall libc.time,timestamp + retn + +system_shutdown: + retn + +malloc: +malloc_fixed: +malloc_growable: +; in: ecx = requested size +; out: eax - allocated block, ecx = allocated size, on error jumps to out_of_memory (does not return) +; preserves: ebx, esi, edi +; note: +; use of malloc_fixed hints that block will be kept as is until the end of assembly +; use of malloc_growable hints that block is likely to be resized + push rbx rcx rsi rdi + ccall libc.malloc,rcx + pop rdi rsi rcx rbx + test eax,eax + jz out_of_memory + lea rdx,[rax+rcx-1] + shr rdx,32 + jnz out_of_memory + retn +realloc: +; in: eax - memory block, ecx = requested size +; out: eax - resized block, ecx = allocated size, on error jumps to out_of_memory (does not return) +; preserves: ebx, esi, edi + push rbx rcx rsi rdi + ccall libc.realloc,rax,rcx + pop rdi rsi rcx rbx + test eax,eax + jz out_of_memory + lea rdx,[rax+rcx-1] + shr rdx,32 + jnz out_of_memory + retn +mfree: +; in: eax - memory block +; out: cf set on error +; preserves: ebx, esi, edi +; note: eax may have value 0 or -1, it should be treated as invalid input then + test eax,eax + jz interface_error + cmp eax,-1 + je interface_error + push rbx rsi rdi + ccall libc.free,rax + pop rdi rsi rbx + clc + retn + interface_error: + stc + retn + +open: +; in: edx - path to file +; out: ebx = file handle, cf set on error +; preserves: esi, edi + push rsi rdi + call adapt_path + ccall libc.fopen,rbx,_open_mode + put_file_entry: + pop rdi rsi + test rax,rax + jz interface_error + push rax + mov eax,[files] + mov ecx,[files_count] + mov ebx,ecx + inc ecx + mov [files_count],ecx + cmp ecx,[files_maximum_count] + ja grow_files_buffer + store_file_entry: + pop rdx + mov [eax+ebx*8],rdx + clc + retn + grow_files_buffer: + shl ecx,4 + test eax,eax + jz allocate_files_buffer + call realloc + jmp allocated_files_buffer + allocate_files_buffer: + call malloc + allocated_files_buffer: + mov [files],eax + shr ecx,3 + mov [files_maximum_count],ecx + jmp store_file_entry + adapt_path: + xor ecx,ecx + mov ebx,path_buffer + copy_path: + mov al,[edx+ecx] + cmp al,'\' + jne path_char_ok + mov al,'/' + path_char_ok: + cmp ecx,1000h + jae out_of_memory + mov [ebx+ecx],al + inc ecx + test al,al + jnz copy_path + retn +create: +; in: edx - path to file +; out: ebx = file handle, cf set on error +; preserves: esi, edi + push rsi rdi + call adapt_path + ccall libc.fopen,rbx,_create_mode + jmp put_file_entry +write: +; in: ebx = file handle, edx - data, ecx = number of bytes +; out: cf set on error +; preserves: ebx, esi, edi + push rbx rcx rsi rdi + mov eax,[files] + mov rax,[eax+ebx*8] + ccall libc.fwrite,rdx,1,rcx,rax + pop rdi rsi rcx rbx + cmp eax,ecx + jne interface_error + clc + ret +read: +; in: ebx = file handle, edx - buffer, ecx = number of bytes +; out: cf set on error +; preserves: ebx, esi, edi + push rbx rcx rsi rdi + mov eax,[files] + mov rax,[eax+ebx*8] + ccall libc.fread,rdx,1,rcx,rax + pop rdi rsi rcx rbx + cmp eax,ecx + jne interface_error + clc + ret +close: +; in: ebx = file handle +; preserves: ebx, esi, edi + push rsi rdi + mov edi,[files] + mov rdi,[edi+ebx*8] + ccall libc.fclose,rdi + pop rdi rsi + ret +lseek: +; in: ebx = file handle, cl = method, edx:eax = offset +; out: edx:eax = new offset from the beginning of file, cf set on error +; preserves: ebx, esi, edi + push rsi rdi rbx + shl rdx,32 + or rax,rdx + movzx ecx,cl + mov edi,[files] + mov rdi,[edi+ebx*8] + push rdi + ccall libc.fseek,rdi,rax,rcx + test eax,eax + jnz lseek_error + pop rdi + ccall libc.ftell,rdi + cmp rax,-1 + je lseek_error + mov rdx,rax + shr rdx,32 + mov eax,eax + pop rbx rdi rsi + clc + ret + lseek_error: + pop rbx rdi rsi + stc + ret + +get_timestamp: +; out: edx:eax = timestamp +; preserves: ebx, ecx, esi, edi + mov eax,dword [timestamp] + mov edx,dword [timestamp+4] + retn + +display_string: +; in: +; esi - string +; ecx = string length, zero for ASCIIZ string +; preserves: ebx, esi + push rbx rsi + test ecx,ecx + jnz write_string_to_stdout + xor al,al + mov edi,esi + or ecx,-1 + repne scasb + neg ecx + sub ecx,2 + write_string_to_stdout: + ccall libc.write,1,rsi,rcx + pop rsi rbx + retn + +display_error_string: +; in: +; esi - string +; ecx = string length, zero for ASCIIZ string +; preserves: ebx, esi + push rbx rsi + test ecx,ecx + jnz write_string_to_stderr + xor al,al + mov edi,esi + or ecx,-1 + repne scasb + neg ecx + sub ecx,2 + write_string_to_stderr: + ccall libc.write,2,rsi,rcx + pop rsi rbx + retn + +get_environment_variable: +; in: +; esi - name +; edi - buffer for value +; ecx = size of buffer +; out: +; eax = length of value +; preserves: ebx, esi, edi + push rbx rcx rsi rdi + ccall getenv,rsi + pop rdi rsi rcx rbx + test rax,rax + jz no_environment_variable + push rsi + mov rsi,rax + xor eax,eax + copy_environment_variable: + mov dl,[rsi+rax] + cmp eax,ecx + jae next_environment_variable_character + mov [edi+eax],dl + next_environment_variable_character: + inc eax + test dl,dl + jnz copy_environment_variable + pop rsi + environment_variable_ok: + ret + no_environment_variable: + mov eax,1 + jecxz environment_variable_ok + and byte [edi],0 + ret diff --git a/x86_64_sse2_x87/fasm/source/map.inc b/x86_64_sse2_x87/fasm/source/map.inc new file mode 100644 index 0000000..18ee2ad --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/map.inc @@ -0,0 +1,237 @@ + +; this is a simple map used primarily for the source cache + +struct Map + hash_mask dd ? + linked_blocks dd ? + free_space dd ? + free_space_length dd ? +ends + +struct MapEntry + name dd ? + name_length dd ? + value dd ? + next_entry dd ? +ends + +create_string_map: +; in: cl = number of hash bits +; out: ebx - new map +; preserves: esi + mov ebx,1 + shl ebx,cl + shl ebx,2 + lea ecx,[sizeof.Map+ebx*4] + call malloc_fixed + xchg ebx,eax + mov ecx,eax + dec eax + mov [ebx+Map.hash_mask],eax + lea edi,[ebx+sizeof.Map] + xor eax,eax + rep stosd + mov ecx,1000h + call malloc_fixed + mov [ebx+Map.linked_blocks],eax + xor edx,edx + mov [eax],edx + add eax,10h + mov [ebx+Map.free_space],eax + mov [ebx+Map.free_space_length],1000h-10h + retn + +destroy_string_map: +; in: ebx - map +; preserves: esi, edi + mov eax,ebx + mov ebx,[ebx+Map.linked_blocks] + call mfree + free_map_blocks: + test ebx,ebx + jz string_map_destroyed + mov eax,ebx + mov ebx,[ebx] + call mfree + jmp free_map_blocks + string_map_destroyed: + retn + +get_from_map: +; in: +; ebx - map +; esi - string +; ecx = string length, zero for ASCIIZ string +; out: +; eax = value +; cf set when no entry found +; preserves: ebx, [esi], edi +; note: when entry is found, esi is replaced with pointer to the same string in persistent storage + call get_bucket + test eax,eax + jz not_found_in_map + call find_map_entry + jc not_found_in_map + mov eax,[eax+MapEntry.value] + retn + get_bucket: + call hash_string + and edx,[ebx+Map.hash_mask] + mov eax,[ebx+sizeof.Map+edx*4] + retn + find_map_entry: + cmp [eax+MapEntry.name_length],ecx + jne next_map_entry + push edi + mov edi,[eax+MapEntry.name] + test edi,edi + jz not_this_map_entry + push ecx esi + repe cmpsb + pop esi ecx + jne not_this_map_entry + mov esi,edi + sub esi,ecx + pop edi + clc + retn + not_this_map_entry: + pop edi + next_map_entry: + mov eax,[eax+MapEntry.next_entry] + test eax,eax + jnz find_map_entry + not_found_in_map: + stc + retn + +put_into_map: +; in: +; ebx - map +; esi - string +; ecx = string length, zero for ASCIIZ string +; eax = value +; preserves: ebx, [esi], edi +; note: +; esi is replaced with pointer to the same string in persistent storage, +; an ASCIIZ string is a key with length including the terminating zero +; and when it is put into persistent storage, final zero is copied as well + push eax + call get_bucket + test eax,eax + jz new_bucket + call find_map_entry + jnc put_value_into_map_entry + mov eax,[ebx+sizeof.Map+edx*4] + find_free_map_entry: + cmp [eax+MapEntry.name],0 + je fill_map_entry + mov edx,eax + mov eax,[eax+MapEntry.next_entry] + test eax,eax + jnz find_free_map_entry + call allocate_map_entry + mov [edx+MapEntry.next_entry],eax + jmp new_map_entry + new_bucket: + call allocate_map_entry + mov [ebx+sizeof.Map+edx*4],eax + new_map_entry: + mov [eax+MapEntry.next_entry],0 + fill_map_entry: + mov [eax+MapEntry.name_length],ecx + push eax + call store_string + pop eax + mov [eax+MapEntry.name],esi + put_value_into_map_entry: + pop [eax+MapEntry.value] + retn + allocate_map_entry: + mov eax,[ebx+Map.free_space] + add [ebx+Map.free_space],sizeof.MapEntry + sub [ebx+Map.free_space_length],sizeof.MapEntry + jc map_out_of_free_space + retn + map_out_of_free_space: + push ecx edx + mov ecx,1000h + call malloc_fixed + mov edx,eax + xchg [ebx+Map.linked_blocks],edx + mov [eax],edx + add eax,10h + mov [ebx+Map.free_space],eax + mov [ebx+Map.free_space_length],1000h-10h + pop edx ecx + jmp allocate_map_entry + +remove_from_map: +; in: +; ebx - map +; esi - string +; ecx = string length, zero for ASCIIZ string +; preserves: ebx, [esi], edi + call get_bucket + test eax,eax + jz not_found_in_map + call find_map_entry + jc not_found_in_map + mov dword [eax+MapEntry.name],0 + retn + +iterate_through_map: +; in: +; ebx - map +; edi - callback function +; callback: +; eax = value +; esi - string +; ecx = string length +; edx - MapEntry + mov ecx,[ebx+Map.hash_mask] + inc ecx + add ebx,sizeof.Map + iterate_through_hash_table: + mov edx,[ebx] + iterate_through_bucket: + test edx,edx + jz end_of_bucket + push ebx ecx edx edi + mov eax,[edx+MapEntry.value] + mov esi,[edx+MapEntry.name] + mov ecx,[edx+MapEntry.name_length] + call edi + pop edi edx ecx ebx + mov edx,[edx+MapEntry.next_entry] + jmp iterate_through_bucket + end_of_bucket: + add ebx,4 + loop iterate_through_hash_table + retn + +hash_string: +; in: esi - string, ecx = string length, zero for ASCIIZ string +; out: ecx = string length, edx = 32-bit hash +; preserves: ebx, esi, edi + mov edx,FNV_OFFSET + jecxz hash_asciiz + mov eax,ecx + hash_known_length: + xor dl,[esi] + inc esi + imul edx,FNV_PRIME + loop hash_known_length + mov ecx,eax + sub esi,ecx + retn + hash_asciiz: + inc ecx + lodsb + xor dl,al + imul edx,FNV_PRIME + test al,al + jnz hash_asciiz + hash_ready: + sub esi,ecx + retn diff --git a/x86_64_sse2_x87/fasm/source/messages.inc b/x86_64_sse2_x87/fasm/source/messages.inc new file mode 100644 index 0000000..d68efa7 --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/messages.inc @@ -0,0 +1,52 @@ + +_macro_source db 'macro ',0 +_preprocessed_source db 'assemble ',0 +_unnamed_source db '?',0 +_memory_source db 'eval',0 +_space db ' ',0 +_line_number_prefix db ' [',0 +_line_number_suffix db ']',0 +_line_content_prefix db ':' +_line_segment_prefix db LINE_FEED,9,0 +_calm_source db ' (CALM)',0 +_single_quote db 27h,0 +_preprocessed_text_prefix db 'Processed: ',0 +_error_prefix db 'Error: ',0 +_custom_error_prefix db 'Custom error: ',0 +_message_suffix db '.' +_new_line db LINE_FEED,0 + +_source_file_not_found db "source file '%s' not found",0 +_error_reading_file db "error reading file '%s'",0 +_missing_end_quote db "missing end quote",0 +_illegal_instruction db "illegal instruction",0 +_invalid_argument db "invalid argument",0 +_missing_argument db "missing argument",0 +_extra_characters_on_line db "extra characters on line",0 +_invalid_identifier db "invalid identifier",0 +_invalid_number db "invalid number",0 +_undefined_symbol db "symbol '%i' is undefined or out of scope",0 +_invalid_symbol_value db "the value of symbol '%i' is not valid for this use",0 +_conflicting_definition db "definition of '%i' in conflict with already defined symbol",0 +_cannot_apply_to_constant db "cannot apply this operation to constant",0 +_symbolic_self_reference db "detected a circular reference of symbolic values",0 +_unexpected_instruction db "unexpected instruction",0 +_missing_end_directive db "missing end directive",0 +_missing_closing_chevron db "missing closing chevron",0 +_invalid_value db "value of type not allowed in this context",0 +_invalid_expression db "invalid expression",0 +_missing_closing_parenthesis db "missing closing parenthesis",0 +_excess_closing_parenthesis db "excess closing parenthesis",0 +_value_out_of_range db "value out of allowed range",0 +_indeterminate_result db "expression has indeterminate, infinite, or infinitesimal result",0 +_misused_variable_term db "variable term used where not expected",0 +_nonlinear_polynomial db "cannot multiply variable terms",0 +_subdivided_variable_term db "cannot subdivide variable term",0 +_values_not_comparable db "values not comparable",0 +_assertion_failed db "assertion failed",0 +_area_overflow db "exceeded the maximum allowed length of output area",0 +_address_out_of_range db "address out of range",0 +_invalid_area db "invalid or inaccessible addressing area",0 +_stack_limit_exceeded db "exceeded the maximum allowed depth of stack",0 +_repeated_declaration db "choice has already been declared",0 +_undefined_jump_target db "the target of the jump is not defined in this instruction",0 diff --git a/x86_64_sse2_x87/fasm/source/output.inc b/x86_64_sse2_x87/fasm/source/output.inc new file mode 100644 index 0000000..d96c356 --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/output.inc @@ -0,0 +1,746 @@ + +struct OutputArea + cached_offset dq ? + definition dd ? ; pointer to ValueDefinition +ends + +struct AreaHeader + flags dd ? ; AREA_# + base_address_length dd ? + uninitialized_length dd ? +ends + +AREA_VIRTUAL = 1 +AREA_VARIABLE = 2 +AREA_SHIFT_TRACKING_DISABLED = 4 + +create_output_area: +; in: +; esi - base address in format of VALTYPE_NUMERIC value +; ecx = length of base address value +; out: +; ebx - AreaHeader +; edx - ValueDefinition + mov eax,[current_output_area_entry] + test eax,eax + jz create_first_output_area + push ecx + lea ecx,[eax+sizeof.OutputArea*2] + cmp ecx,[output_areas_list_end] + jbe get_next_output_entry + mov eax,[output_areas_list] + sub ecx,eax + sub [current_output_area_entry],eax + sub [initial_output_area_entry],eax + sub [output_areas_list_end],eax + call grow_stack + mov [output_areas_list],eax + add [current_output_area_entry],eax + add [initial_output_area_entry],eax + add eax,ecx + mov edi,eax + xchg [output_areas_list_end],eax + sub ecx,eax + sub edi,ecx + shr ecx,2 + xor eax,eax + rep stosd + mov eax,[current_output_area_entry] + get_next_output_entry: + add eax,sizeof.OutputArea + cmp [initial_output_area_entry],0 + je another_initial_output_area + mov edi,[eax-sizeof.OutputArea+OutputArea.definition] + mov ecx,[edi+ValueDefinition.value_length] + mov edi,[edi+ValueDefinition.value] + mov ebx,[edi+AreaHeader.uninitialized_length] + sub ecx,sizeof.AreaHeader + sub ecx,[edi+AreaHeader.base_address_length] + jz prior_uninitialized_length_ok + and dword [prior_uninitialized_length],0 + and dword [prior_uninitialized_length+4],0 + prior_uninitialized_length_ok: + add dword [prior_uninitialized_length],ebx + adc dword [prior_uninitialized_length+4],0 + xor edx,edx + add ecx,ebx + adc edx,0 + add ecx,dword [eax-sizeof.OutputArea+OutputArea.cached_offset] + adc edx,dword [eax-sizeof.OutputArea+OutputArea.cached_offset+4] + mov dword [eax+OutputArea.cached_offset],ecx + mov dword [eax+OutputArea.cached_offset+4],edx + pop ecx + jmp new_output_entry_ready + another_initial_output_area: + pop ecx + jmp create_initial_output_area + create_first_output_area: + mov eax,[output_areas_list] + create_initial_output_area: + mov [initial_output_area_entry],eax + and dword [eax+OutputArea.cached_offset],0 + and dword [eax+OutputArea.cached_offset+4],0 + and dword [prior_uninitialized_length],0 + and dword [prior_uninitialized_length+4],0 + new_output_entry_ready: + mov [current_output_area_entry],eax + lea ebx,[eax+OutputArea.definition] + call create_area + retn + +create_area: +; in: +; ebx - where pointer to ValueDefinition should be stored, may already hold a previously used one (should contain null otherwise) +; esi - base address in format of VALTYPE_NUMERIC value +; ecx = length of base address value +; out: +; ebx - AreaHeader +; edx - ValueDefinition + mov [address_length],ecx + mov edx,[ebx] + test edx,edx + jz current_area_definition_unusable + cmp [edx+ValueDefinition.reference_count],1 + je area_definition_ready + dec [edx+ValueDefinition.reference_count] + current_area_definition_unusable: + mov ecx,retired_definition + retrieve_retired_detached_value: + mov edx,[ecx] + test edx,edx + jz create_area_definition + cmp [edx+ValueDefinition.reference_count],0 + jne retired_detached_value_immutable + xor eax,eax + xchg eax,[edx+ValueDefinition.previous] + mov [ecx],eax + jmp adopt_area_definition + retired_detached_value_immutable: + lea ecx,[edx+ValueDefinition.previous] + jmp retrieve_retired_detached_value + create_area_definition: + mov ecx,sizeof.ValueDefinition + call create_tree_element + mov ecx,eax + xchg ecx,[value_definition_chain] + mov [eax+ValueDefinition.interlink],ecx + mov edx,eax + adopt_area_definition: + mov [ebx],edx + or [edx+ValueDefinition.flags],VAL_DETACHED + inc [edx+ValueDefinition.reference_count] + area_definition_ready: + mov ecx,[address_length] + add ecx,sizeof.AreaHeader + mov eax,[edx+ValueDefinition.block_length] + test eax,eax + jz allocate_area_block + cmp ecx,eax + jbe initialize_area_block + push ecx edx + xor eax,eax + xchg eax,[edx+ValueDefinition.value] + call mfree + pop edx ecx + allocate_area_block: + push edx + call malloc_growable + pop edx + mov [edx+ValueDefinition.value],eax + mov [edx+ValueDefinition.block_length],ecx + initialize_area_block: + mov ebx,[edx+ValueDefinition.value] + lea edi,[ebx+sizeof.AreaHeader] + mov ecx,[address_length] + mov [ebx+AreaHeader.base_address_length],ecx + rep movsb + mov [ebx+AreaHeader.uninitialized_length],ecx + mov [ebx+AreaHeader.flags],ecx + sub edi,ebx + mov [edx+ValueDefinition.value_length],edi + mov [edx+ValueDefinition.type],VALTYPE_AREA + mov ecx,[current_pass] + mov [edx+ValueDefinition.pass],ecx + retn + +initialize_output: +; in: ecx = number of bytes that should be added to output +; out: edi - output buffer to be filled with data +; preserves: esi + mov edx,[current_area] + mov ebx,[edx+ValueDefinition.value] + add ecx,[ebx+AreaHeader.uninitialized_length] + jc output_out_of_memory + mov eax,[edx+ValueDefinition.value_length] + lea edi,[ebx+eax] + add ecx,eax + jc output_out_of_memory + mov [edx+ValueDefinition.value_length],ecx + cmp ecx,[edx+ValueDefinition.block_length] + jbe area_reserve_sufficient + mov eax,[edx+ValueDefinition.value] + sub edi,eax + push edx + push ecx + bsr edx,ecx + xchg ecx,edx + dec cl + shr edx,cl + inc edx + shl edx,cl + pop ecx + cmp edx,ecx + jbe output_out_of_memory + xchg ecx,edx + call realloc + pop edx + mov ebx,eax + add edi,eax + mov [edx+ValueDefinition.value],ebx + mov [edx+ValueDefinition.block_length],ecx + area_reserve_sufficient: + mov ecx,[ebx+AreaHeader.uninitialized_length] + jecxz output_buffer_ready + xor eax,eax + mov dl,cl + shr ecx,2 + rep stosd + mov cl,dl + and cl,11b + rep stosb + mov [ebx+AreaHeader.uninitialized_length],eax + output_buffer_ready: + retn + output_out_of_memory: + jmp out_of_memory + +uninitialized_output: +; in: ecx = number of uninitialized bytes to be added to output +; preserves: ebx, ecx, esi, edi + mov edx,[current_area] + mov eax,[edx+ValueDefinition.value] + add [eax+AreaHeader.uninitialized_length],ecx + jc area_overflow + mov edx,[edx+ValueDefinition.value_length] + sub edx,sizeof.AreaHeader + sub edx,[eax+AreaHeader.base_address_length] + add edx,[eax+AreaHeader.uninitialized_length] + jc area_overflow + retn + area_overflow: + mov edx,_area_overflow + call register_error + mov edx,[current_area] + or ecx,-1 + mov eax,[edx+ValueDefinition.value] + sub ecx,[edx+ValueDefinition.value_length] + add ecx,sizeof.AreaHeader + add ecx,[eax+AreaHeader.base_address_length] + mov [eax+AreaHeader.uninitialized_length],ecx + retn + +trim_output: +; preserves: ecx, esi + xor eax,eax + mov dword [prior_uninitialized_length],eax + mov dword [prior_uninitialized_length+4],eax + mov edi,[current_output_area_entry] + trim_current_output_area: + mov edx,[edi+OutputArea.definition] + mov eax,[edx+ValueDefinition.value] + and [eax+AreaHeader.uninitialized_length],0 + mov ebx,[edx+ValueDefinition.value_length] + sub ebx,sizeof.AreaHeader + sub ebx,[eax+AreaHeader.base_address_length] + jnz output_trimmed + cmp edi,[initial_output_area_entry] + je output_trimmed + sub edi,sizeof.OutputArea + jmp trim_current_output_area + output_trimmed: + mov [current_output_area_entry],edi + retn + +find_output_area: +; in: +; [file_offset] = offset within the output +; out: +; cf set when not found an area that would contain a byte at specified offset +; when cf = 0: +; ebx - OutputArea +; [file_offset] = offset relative to the beginning of the found area (upper 32 bits are zero) + mov esi,[initial_output_area_entry] + mov edi,[current_output_area_entry] + add edi,sizeof.OutputArea + search_areas: + mov ebx,edi + sub ebx,esi + jz output_area_not_found + test ebx,1 shl bsf sizeof.OutputArea + jz bisect_areas_list + sub ebx,sizeof.OutputArea + bisect_areas_list: + shr ebx,1 + add ebx,esi + mov eax,dword [file_offset] + mov edx,dword [file_offset+4] + sub eax,dword [ebx+OutputArea.cached_offset] + sbb edx,dword [ebx+OutputArea.cached_offset+4] + jc search_earlier_areas + jnz search_later_areas + mov edx,[ebx+OutputArea.definition] + mov ecx,[edx+ValueDefinition.value_length] + mov edx,[edx+ValueDefinition.value] + sub ecx,sizeof.AreaHeader + sub ecx,[edx+AreaHeader.base_address_length] + add ecx,[edx+AreaHeader.uninitialized_length] + cmp eax,ecx + jae search_later_areas + output_area_found: + mov dword [file_offset],eax + and dword [file_offset+4],0 + ; clc + retn + output_area_not_found: + stc + retn + search_later_areas: + lea esi,[ebx+sizeof.OutputArea] + jmp search_areas + search_earlier_areas: + mov edi,ebx + jmp search_areas + +read_from_output: +; in: +; edi - buffer for read data +; [value_length] = length of data to read +; [file_offset] = offset within the output +; out: +; [value_length] = number of bytes that were not in the existing output and could not be read + push edi + call find_output_area + pop edi + jc output_reading_done + read_output_areas: + cmp [value_length],0 + je output_reading_done + mov edx,[ebx+OutputArea.definition] + mov ecx,[edx+ValueDefinition.value_length] + mov eax,ecx + mov edx,[edx+ValueDefinition.value] + sub ecx,sizeof.AreaHeader + sub ecx,[edx+AreaHeader.base_address_length] + sub dword [file_offset],ecx + jnc initialized_load_done + lea esi,[edx+eax] + mov ecx,dword [file_offset] + add esi,ecx + neg ecx + cmp ecx,[value_length] + jbe initialized_load_length_ok + mov ecx,[value_length] + initialized_load_length_ok: + sub [value_length],ecx + rep movsb + mov dword [file_offset],ecx + initialized_load_done: + mov ecx,[edx+AreaHeader.uninitialized_length] + sub dword [file_offset],ecx + jnc uninitialized_load_done + mov ecx,dword [file_offset] + neg ecx + cmp ecx,[value_length] + jbe uninitialized_load_length_ok + mov ecx,[value_length] + uninitialized_load_length_ok: + sub [value_length],ecx + xor al,al + rep stosb + mov dword [file_offset],ecx + uninitialized_load_done: + cmp ebx,[current_output_area_entry] + je output_reading_done + add ebx,sizeof.OutputArea + jmp read_output_areas + output_reading_done: + retn + +rewrite_output: +; in: +; esi - data to write +; [value_length] = length of data to write +; [file_offset] = offset within the output +; out: +; [value_length] = number of bytes that were not in the existing output and could not be rewritten + push esi + call find_output_area + pop esi + jc output_rewriting_done + rewrite_output_areas: + cmp [value_length],0 + je output_rewriting_done + mov edx,[ebx+OutputArea.definition] + mov ecx,[edx+ValueDefinition.value_length] + mov edx,[edx+ValueDefinition.value] + sub ecx,sizeof.AreaHeader + sub ecx,[edx+AreaHeader.base_address_length] + mov edi,[edx+AreaHeader.uninitialized_length] + add ecx,edi + sub dword [file_offset],ecx + jnc rewrite_next_area + mov eax,[value_length] + xor ecx,ecx + add eax,edi + adc ecx,ecx + add eax,dword [file_offset] + jnc rewrite_initialized_data + cmp eax,edi + jbe rewrite_uninitialized_data + mov eax,edi + rewrite_uninitialized_data: + test eax,eax + jz rewrite_initialized_data + push ebx + sub [edx+AreaHeader.uninitialized_length],eax + mov edx,[ebx+OutputArea.definition] + mov ebx,eax + call expand_value + call update_output_offsets + pop ebx + rewrite_initialized_data: + mov edx,[ebx+OutputArea.definition] + mov ecx,[edx+ValueDefinition.value_length] + mov edi,[edx+ValueDefinition.value] + or [edi+AreaHeader.flags],AREA_VARIABLE + add edi,[edi+AreaHeader.uninitialized_length] + add edi,ecx + mov ecx,dword [file_offset] + add edi,ecx + neg ecx + cmp ecx,[value_length] + jbe rewrite_length_ok + mov ecx,[value_length] + rewrite_length_ok: + sub [value_length],ecx + rep movsb + mov dword [file_offset],ecx + rewrite_next_area: + cmp ebx,[current_output_area_entry] + je output_rewriting_done + add ebx,sizeof.OutputArea + jmp rewrite_output_areas + output_rewriting_done: + retn + +update_output_offsets: +; in: +; edx - ValueDefinition of output area that had some of uninitialized data made initialized +; preserves: esi + mov eax,[current_output_area_entry] + cmp edx,[eax+OutputArea.definition] + je output_offsets_ok + and dword [prior_uninitialized_length],0 + and dword [prior_uninitialized_length+4],0 + recount_prior_uninitialized_length: + cmp eax,[initial_output_area_entry] + je output_offsets_ok + sub eax,sizeof.OutputArea + mov edi,[eax+OutputArea.definition] + mov ebx,[edi+ValueDefinition.value] + mov ecx,[ebx+AreaHeader.uninitialized_length] + add dword [prior_uninitialized_length],ecx + adc dword [prior_uninitialized_length+4],0 + cmp edx,edi + je output_offsets_ok + mov ecx,[edi+ValueDefinition.value_length] + sub ecx,sizeof.AreaHeader + sub ecx,[ebx+AreaHeader.base_address_length] + jz recount_prior_uninitialized_length + output_offsets_ok: + retn + +get_current_address_value: +; out: +; esi - address in format of VALTYPE_NUMERIC value +; ecx = length of address value +; note: the returned value is placed in assembly workspace + mov eax,[current_area] + mov esi,[eax+ValueDefinition.value] + mov ebx,[eax+ValueDefinition.value_length] + mov edx,assembly_workspace + mov edi,[edx+Workspace.memory_start] + mov ecx,[esi+AreaHeader.base_address_length] + add ecx,4 + call reserve_workspace + mov ecx,[esi+AreaHeader.base_address_length] + sub ebx,ecx + sub ebx,sizeof.AreaHeader + add ebx,[esi+AreaHeader.uninitialized_length] + ; jc internal_error + add esi,sizeof.AreaHeader + xor eax,eax + stosd + lodsd + mov ecx,eax + xor edx,edx + jecxz offset_added_to_base_address + add_offset_to_base_address: + lodsb + add al,bl + setc dl + stosb + shr ebx,8 + add ebx,edx + loop add_offset_to_base_address + offset_added_to_base_address: + mov edx,[assembly_workspace.memory_start] + add edx,4 + mov eax,ebx + cmp byte [esi-1],80h + cmc + sbb eax,0 + stosd + optimize_base_address: + movsx eax,byte [edi-2] + cmp ah,[edi-1] + jne base_address_ready + dec edi + cmp edi,edx + jne optimize_base_address + base_address_ready: + mov ecx,edi + sub ecx,edx + mov [edx-4],ecx + mov ecx,esi + measure_variable_terms: + lodsd + test eax,eax + jz variable_terms_measured + lodsd + add esi,eax + jmp measure_variable_terms + variable_terms_measured: + xchg ecx,esi + sub ecx,esi + mov al,cl + shr ecx,2 + rep movsd + mov cl,al + and cl,11b + rep movsb + mov esi,[assembly_workspace.memory_start] + mov ecx,edi + sub ecx,esi + retn + +get_output_length: +; out: +; edx:eax = length of current output (not counting uninitialized data) +; preserves: esi + mov ebx,[current_output_area_entry] + mov eax,dword [ebx+OutputArea.cached_offset] + mov edx,dword [ebx+OutputArea.cached_offset+4] + mov edi,[ebx+OutputArea.definition] + mov ecx,[edi+ValueDefinition.value_length] + mov edi,[edi+ValueDefinition.value] + sub ecx,sizeof.AreaHeader + sub ecx,[edi+AreaHeader.base_address_length] + jz current_area_entirely_uninitialized + add eax,ecx + adc edx,0 + retn + current_area_entirely_uninitialized: + sub eax,dword [prior_uninitialized_length] + sbb edx,dword [prior_uninitialized_length+4] + retn + +get_output_position: +; out: +; edx:eax = current position in the output (including uninitialized data) +; preserves: esi + mov ebx,[current_output_area_entry] + mov eax,dword [ebx+OutputArea.cached_offset] + mov edx,dword [ebx+OutputArea.cached_offset+4] + mov edi,[ebx+OutputArea.definition] + mov ecx,[edi+ValueDefinition.value_length] + mov edi,[edi+ValueDefinition.value] + sub ecx,sizeof.AreaHeader + sub ecx,[edi+AreaHeader.base_address_length] + add ecx,[edi+AreaHeader.uninitialized_length] + add eax,ecx + adc edx,0 + retn + +create_output_path: +; in: +; ebx - base path and name +; esi - file extension +; ecx = length of the extension +; out: +; edx - output path (generated in temporary storage) + push ecx + mov edi,ebx + xor al,al + or ecx,-1 + repne scasb + dec edi + mov ecx,edi + locate_extension: + cmp edi,ebx + je copy_path_name + dec edi + mov al,[edi] + cmp al,'\' + je copy_path_name + cmp al,'/' + je copy_path_name + cmp al,'.' + jne locate_extension + mov ecx,edi + copy_path_name: + sub ecx,ebx + push ecx + mov edi,[preprocessing_workspace.memory_start] + inc ecx + mov edx,preprocessing_workspace + call reserve_workspace + pop ecx + xchg esi,ebx + rep movsb + mov esi,ebx + pop ecx + mov ebx,ecx + add ecx,2 + call reserve_workspace + mov ecx,ebx + jecxz extension_attached + mov al,'.' + stosb + rep movsb + extension_attached: + xor al,al + stosb + mov edx,[preprocessing_workspace.memory_start] + retn + +write_output_file: +; in: +; ebx - source path +; edi - output path +; out: +; cf set when write failed +; note: +; when output path is null, source path is used with replaced or attached extension + mov [base_path],edi + xor eax,eax + mov [output_failures],eax + mov dword [uninitialized_length],eax + mov dword [uninitialized_length+4],eax + mov edx,edi + test edx,edx + jnz create_output_file + mov [base_path],ebx + mov esi,[output_extension] + mov ecx,[output_extension_length] + call create_output_path + create_output_file: + call create + jc output_write_failed + mov esi,[initial_output_area_entry] + write_area: + mov edx,[esi+OutputArea.definition] + mov eax,[edx+ValueDefinition.value] + mov ecx,[edx+ValueDefinition.value_length] + sub ecx,[eax+AreaHeader.base_address_length] + sub ecx,sizeof.AreaHeader + jz write_next_area + mov eax,dword [uninitialized_length] + or eax,dword [uninitialized_length+4] + jz write_initialized_data + write_uninitialized_data: + mov edi,[assembly_workspace.memory_start] + mov ecx,1000h shr 2 + xor eax,eax + rep stosd + mov ecx,1000h + cmp dword [uninitialized_length+4],0 + jne portion_length_ok + cmp ecx,dword [uninitialized_length] + jbe portion_length_ok + mov ecx,dword [uninitialized_length] + portion_length_ok: + sub dword [uninitialized_length],ecx + sbb dword [uninitialized_length+4],0 + mov edx,[assembly_workspace.memory_start] + call write + jc file_write_failed + mov eax,dword [uninitialized_length] + or eax,dword [uninitialized_length+4] + jnz write_uninitialized_data + write_initialized_data: + mov edx,[esi+OutputArea.definition] + mov eax,[edx+ValueDefinition.value] + mov ecx,[edx+ValueDefinition.value_length] + mov edx,[eax+AreaHeader.base_address_length] + add edx,sizeof.AreaHeader + sub ecx,edx + add edx,eax + call write + jc file_write_failed + write_next_area: + mov edx,[esi+OutputArea.definition] + mov eax,[edx+ValueDefinition.value] + mov eax,[eax+AreaHeader.uninitialized_length] + add dword [uninitialized_length],eax + adc dword [uninitialized_length+4],0 + cmp esi,[current_output_area_entry] + je close_output_file + add esi,sizeof.OutputArea + jmp write_area + close_output_file: + call close + mov ebx,[auxiliary_output_areas] + mov edi,write_auxiliary_output_area + call iterate_through_map + cmp [output_failures],0 + jne output_write_failed + clc + retn + file_write_failed: + call close + output_write_failed: + stc + retn + +write_auxiliary_output_area: +; in: +; eax = ValueDefinition, null for cached extension not used for auxiliary output +; esi - file extension +; ecx = length of the extension + test eax,eax + jz auxiliary_output_processed + mov ebx,[base_path] + test ebx,ebx + jz auxiliary_output_processed + push eax + call create_output_path + call create + pop edx + jc auxiliary_file_creation_failed + mov eax,[edx+ValueDefinition.value] + mov ecx,[edx+ValueDefinition.value_length] + mov edx,[eax+AreaHeader.base_address_length] + add edx,sizeof.AreaHeader + sub ecx,edx + add edx,eax + call write + jc auxiliary_file_write_failed + call close + auxiliary_output_processed: + retn + auxiliary_file_write_failed: + call close + auxiliary_file_creation_failed: + inc [output_failures] + retn diff --git a/x86_64_sse2_x87/fasm/source/reader.inc b/x86_64_sse2_x87/fasm/source/reader.inc new file mode 100644 index 0000000..f59b2d6 --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/reader.inc @@ -0,0 +1,451 @@ + +struct FileData + length dq ? + cache dd ? ; pointer to FileCache +ends + +struct FileCache + offset dq ? + length dd ? + next dd ? ; pointer to another FileCache +ends + +read_source: +; in: +; esi - source path +; out: +; eax - tokenized source, null when file not found +; esi - source path in persistent storage + mov ebx,[file_source_cache] + xor ecx,ecx + call get_from_map + jc read_source_file + cmp eax,-1 + je get_erroneous_source + retn + read_source_file: + mov edx,esi + call open + jc source_file_not_found + xor eax,eax + mov edx,eax + mov cl,2 + call lseek + jc error_reading_file + test edx,edx + jnz out_of_memory + push eax + xor eax,eax + mov edx,eax + mov cl,al + call lseek + jc error_reading_file + pop ecx + inc ecx + mov [source_text_length],ecx + call malloc + mov [source_text],eax + mov edx,eax + mov ecx,[source_text_length] + dec ecx + mov byte [edx+ecx],0 + call read + jc error_reading_file + call close + push esi + call tokenize_source + mov eax,[source_text] + call mfree + pop esi + mov eax,[tokenization_buffer] + xor ecx,ecx + mov ebx,[file_source_cache] + call put_into_map + mov eax,[tokenization_buffer] + retn + source_file_not_found: + xor eax,eax + xor ecx,ecx + mov ebx,[file_source_cache] + call put_into_map + xor eax,eax + retn + error_reading_file: + or eax,-1 + xor ecx,ecx + mov ebx,[file_source_cache] + call put_into_map + get_erroneous_source: + mov ebx,esi + mov edx,_error_reading_file + call register_error + mov eax,zero_value + retn + +use_source: +; in: +; esi - ASCIIZ source string +; out: +; eax - tokenized source +; esi - source text in persistent storage + mov edi,esi + xor al,al + or ecx,-1 + repne scasb + not ecx + mov [source_text_length],ecx + mov ebx,[memory_source_cache] + xor eax,eax + call get_from_map + jc adapt_memory_source + retn + adapt_memory_source: + mov [source_text],esi + call tokenize_source + mov eax,[tokenization_buffer] + mov esi,[source_text] + mov ecx,[source_text_length] + mov ebx,[memory_source_cache] + call put_into_map + mov eax,[tokenization_buffer] + retn + +tokenize_source: +; in: +; [source_text] - ASCIIZ text +; [source_text_length] = length of text (including terminating character) +; out: +; [tokenization_buffer] - tokenized source +; [tokenization_buffer_length] = length of tokenized source + mov ecx,[source_text_length] + shl ecx,1 + add ecx,18 + call malloc_growable + mov [tokenization_buffer],eax + mov [tokenization_buffer_length],ecx + add eax,ecx + sub eax,[source_text] + sub eax,[source_text_length] + mov [buffer_end_offset],eax + mov esi,[source_text] + mov edi,[tokenization_buffer] + mov [last_token],0Ah + tokenize: + mov eax,[buffer_end_offset] + add eax,esi + sub eax,edi + cmp eax,18 + jae tokenization_buffer_reserve_ok + mov ecx,esi + sub ecx,[source_text] + mov eax,[source_text_length] + mul [tokenization_buffer_length] + div ecx + mov ecx,eax + add ecx,18 + mov eax,[tokenization_buffer] + call realloc + sub edi,[tokenization_buffer] + add edi,eax + mov [tokenization_buffer],eax + mov [tokenization_buffer_length],ecx + add eax,ecx + sub eax,[source_text] + sub eax,[source_text_length] + mov [buffer_end_offset],eax + tokenization_buffer_reserve_ok: + movzx eax,byte [esi] + inc esi + mov ah,[characters+eax] + cmp ah,20h + je control_character + test ah,ah + jnz make_name_token + character_token: + stosb + mov [last_token],al + jmp tokenize + make_string_token: + mov dl,al + mov byte [edi],22h + mov [last_token],22h + add edi,5 + xor ecx,ecx + copy_string: + mov al,[esi] + cmp al,0Dh + je broken_string + cmp al,0Ah + je broken_string + cmp al,1Ah + je broken_string + test al,al + jz broken_string + inc esi + cmp al,dl + jne copy_string_character + cmp byte [esi],al + jne finish_string_token + inc esi + copy_string_character: + mov [edi+ecx],al + inc ecx + jmp copy_string + broken_string: + mov byte [edi-5],27h + finish_string_token: + mov al,[edi-5] + mov [edi-4],ecx + add edi,ecx + jmp tokenize + make_name_token: + cmp al,22h + je make_string_token + cmp al,27h + je make_string_token + mov byte [edi],1Ah + mov [last_token],1Ah + add edi,5 + xor ebx,ebx + mov ecx,FNV_OFFSET + mov edx,ecx + hash_name: + mov [edi+ebx],al + inc ebx + xor cl,al + xor dl,ah + imul ecx,FNV_PRIME + imul edx,FNV_PRIME + movzx eax,byte [esi] + inc esi + mov ah,[characters+eax] + cmp ah,20h + je finish_name_token + test ah,ah + jnz hash_name + finish_name_token: + mov [edi-4],ebx + add edi,ebx + mov [edi],ecx + mov [edi+4],edx + xor ecx,ecx + mov [edi+8],ecx + add edi,12 + cmp ah,20h + jne character_token + control_character: + cmp al,20h + je whitespace + cmp al,9 + je whitespace + cmp [last_token],20h + je mark_end_of_line + inc edi + mark_end_of_line: + mov byte [edi-1],0Ah + mov [last_token],0Ah + cmp al,0Dh + je cr + cmp al,0Ah + je lf + xor al,al + stosb + mov ecx,edi + mov eax,[tokenization_buffer] + sub ecx,eax + call realloc + mov [tokenization_buffer],eax + mov [tokenization_buffer_length],ecx + retn + cr: + cmp byte [esi],0Ah + jne tokenize + inc esi + jmp tokenize + lf: + cmp byte [esi],0Dh + jne tokenize + inc esi + jmp tokenize + whitespace: + cmp [last_token],0Ah + je tokenize + cmp [last_token],20h + je tokenize + mov al,20h + stosb + mov [last_token],al + jmp tokenize + +get_file_data: +; in: +; esi - file path +; out: +; ebx - FileData, null when file not found +; esi - file path in persistent storage +; preserves: edi + mov ebx,[file_data_cache] + xor ecx,ecx + call get_from_map + jc initialize_file_data + mov ebx,eax + retn + initialize_file_data: + mov edx,esi + call open + jc remember_file_not_found + push edi + mov ecx,sizeof.FileData + call malloc_fixed + mov edi,eax + xor eax,eax + mov edx,eax + mov cl,2 + call lseek + jc file_not_seekable + mov dword [edi+FileData.length],eax + mov dword [edi+FileData.length+4],edx + call close + mov eax,edi + xor ecx,ecx + mov [eax+FileData.cache],ecx + mov ebx,[file_data_cache] + call put_into_map + mov ebx,edi + pop edi + retn + file_not_seekable: + pop edi + remember_file_not_found: + xor eax,eax + mov ecx,eax + mov ebx,[file_data_cache] + call put_into_map + xor ebx,ebx + retn + +read_file_data: +; in: +; esi - file path +; ebx - FileData +; edi - buffer for data +; [file_offset] = offset of data +; [data_length] = length of data +; out: +; cf set when read failed +; preserves: esi + mov [file_data],ebx + lea eax,[ebx+FileData.cache] + mov [file_cache_pointer],eax + mov ebx,[eax] + read_from_file_cache: + mov ecx,[data_length] + test ecx,ecx + jz file_data_read + test ebx,ebx + jz new_trailing_file_cache_entry + mov eax,dword [file_offset] + mov edx,dword [file_offset+4] + sub eax,dword [ebx+FileCache.offset] + sbb edx,dword [ebx+FileCache.offset+4] + jc new_file_cache_entry + jnz next_entry + mov edx,[ebx+FileCache.length] + sub edx,eax + jbe next_entry + cmp ecx,edx + jbe length_to_read_ok + mov ecx,edx + length_to_read_ok: + sub [data_length],ecx + add dword [file_offset],ecx + adc dword [file_offset+4],0 + mov edx,esi + lea esi,[ebx+sizeof.FileCache+eax] + mov al,cl + shr ecx,2 + rep movsd + mov cl,al + and cl,11b + rep movsb + mov esi,edx + next_entry: + lea eax,[ebx+FileCache.next] + mov [file_cache_pointer],eax + mov ebx,[eax] + jmp read_from_file_cache + file_data_read: + clc + retn + new_trailing_file_cache_entry: + mov ebx,[file_data] + mov ecx,dword [ebx+FileData.length] + mov edx,dword [ebx+FileData.length+4] + cmp ecx,dword [file_offset] + jne measure_cache_gap + cmp edx,dword [file_offset+4] + jne measure_cache_gap + stc + retn + new_file_cache_entry: + mov ecx,dword [ebx+FileCache.offset] + mov edx,dword [ebx+FileCache.offset+4] + measure_cache_gap: + mov eax,dword [file_offset] + and eax,not 0FFFh + sub ecx,eax + sbb edx,dword [file_offset+4] + jnz compute_aligned_length + cmp ecx,[data_length] + jbe read_into_cache + compute_aligned_length: + mov eax,dword [file_offset] + and eax,0FFFh + add eax,[data_length] + dec eax + shr eax,12 + inc eax + shl eax,12 + test edx,edx + jnz use_aligned_length + cmp eax,ecx + jae read_into_cache + use_aligned_length: + mov ecx,eax + read_into_cache: + push ecx + add ecx,sizeof.FileCache + call malloc_fixed + mov ebx,eax + mov eax,[file_cache_pointer] + mov edx,ebx + xchg edx,[eax] + mov [ebx+FileCache.next],edx + pop [ebx+FileCache.length] + mov eax,dword [file_offset] + and eax,not 0FFFh + mov edx,dword [file_offset+4] + mov dword [ebx+FileCache.offset],eax + mov dword [ebx+FileCache.offset+4],edx + push ebx edi + mov edi,ebx + mov edx,esi + call open + jc file_access_error + mov eax,dword [edi+FileCache.offset] + mov edx,dword [edi+FileCache.offset+4] + xor cl,cl + call lseek + jc file_access_error + lea edx,[edi+sizeof.FileCache] + mov ecx,[edi+FileCache.length] + call read + jc file_access_error + call close + pop edi ebx + jmp read_from_file_cache + file_access_error: + pop edi ebx + stc + retn + \ No newline at end of file diff --git a/x86_64_sse2_x87/fasm/source/symbols.inc b/x86_64_sse2_x87/fasm/source/symbols.inc new file mode 100644 index 0000000..2ed12fe --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/symbols.inc @@ -0,0 +1,1561 @@ + +struct SymbolTree_Root + attributes db ? ; SYMTREE_# + flags db ? ; NAMESPACE_# + parameters db ? ; SPECPARM_# + reserved db ? + parent_branch dd ? ; pointer to namespace SymbolTree_Foliage + current_label dd ? ; pointer to selected SymbolTree_Foliage + chain dd ? ; pointer to another SymbolTree_Root + ; root_node SymbolTree_Node +ends + +struct SymbolTree_Node + branches rd 1 shl TREE_NODE_BITS +ends + +struct SymbolTree_LocalNode + branches rd 1 shl LOCAL_TREE_NODE_BITS +ends + +struct SymbolTree_Foliage + name_kind db ? ; NAME_# + flags db ? ; FOLIAGE_# + reserved dw ? + name_length dd ? + name_data dd ? + root dd ? ; pointer to SymbolTree_Root + next dd ? ; pointer to another SymbolTree_Foliage + child_namespace dd ? ; pointer to descendant SymbolTree_Root + ; first_leaf SymbolTree_Leaf +ends + +struct SymbolTree_Leaf + class db ? ; SYMCLASS_# + flags db ? ; SYM_# + extra_flags db ? ; SYMX_# + reserved db ? + definition dd ? ; pointer to ValueDefinition + retired_definition dd ? + last_use_pass dd ? + next dd ? ; pointer to another SymbolTree_Leaf within SymbolTree_Foliage + branch dd ? ; pointer to SymbolTree_Foliage + fallback_neighbour dd ? ; pointer to another SymbolTree_Leaf + fallback_parent dd ? ; pointer to another SymbolTree_Leaf +ends + +struct ValueDefinition + type db ? ; VALTYPE_# + flags db ? ; VAL_# + attribute db ? + reserved db ? + pass dd ? + value dd ? + value_length dd ? + block_length dd ? + reference_count dd ? ; number of distinct references to current value + previous dd ? ; pointer to another ValueDefinition + interlink dd ? +ends + +TREE_HEIGHT = 4 +TREE_NODE_BITS = 5 +LOCAL_TREE_HEIGHT = 1 +LOCAL_TREE_NODE_BITS = 6 + +FNV_PRIME = 16777619 +FNV_OFFSET = 2166136261 + +SYMTREE_LOCAL = 1 +SYMTREE_WITH_CASEINSENSITIVE_PARAMETERS = 2 + +NAMESPACE_UNATTACHED = 1 +NAMESPACE_CURRENT_LABEL_TO_CONFIRM = 2 +NAMESPACE_CALM = 10h +NAMESPACE_LOCAL = 20h + +SPECPARM_COUNTER = 1 +SPECPARM_LIMIT = 2 + +FOLIAGE_WITH_TOKEN = 1 + +NAME_CASESENSITIVE = 0 +NAME_CASEINSENSITIVE = 1 +NAME_NUMERIC = 2 +NAME_ABSTRACT = 3 + +SYMCLASS_EXPRESSION = 0 +SYMCLASS_INSTRUCTION = 1 +SYMCLASS_STRUCTURE = 2 +SYMCLASS_PARAMETER = 3 +SYMCLASS_CALM_LOCATION = 10h + +SYM_CONSTANT = 1 +SYM_VARIABLE = 2 +SYM_PREDICTED = 4 +SYM_PREDICTED_DEFINED = 8 +SYM_USAGE_PREDICTED = 10h +SYM_PREDICTED_USED = 20h +SYM_LINK = 40h +SYM_LINK_PREDICTED = 80h + +SYMX_INSTRUCTION_PREDICTED = 1 + +VALTYPE_RESERVED = 0 +VALTYPE_SYMBOLIC = 1 +VALTYPE_NUMERIC = 2 +VALTYPE_STRING = 3 +VALTYPE_FLOAT = 4 +VALTYPE_ELEMENT = 5 +VALTYPE_AREA = 6 +VALTYPE_CALM = 7 +VALTYPE_SYMBOLIC_SEQUENCE = 11h +VALTYPE_NUMERIC_SEQUENCE = 12h +VALTYPE_PLAIN = 20h +VALTYPE_NATIVE_COMMAND = 40h +VALTYPE_NATIVE_COMPARATOR = 41h +VALTYPE_NATIVE_FUNCTION = 42h +VALTYPE_NATIVE_PREPOSITION = 43h + +VAL_INTERNAL = 1 +VAL_IN_USE = 2 +VAL_UNCONDITIONAL = 4 ; for SYMCLASS_INSTRUCTION and SYMCLASS_STRUCTURE +VAL_UNARY = 4 ; for SYMCLASS_EXPRESSION +VAL_NONRECURSIVE = 8 +VAL_SHIFTABLE = 10h +VAL_DETACHED = 20h + +RECOGNIZE_CASE_INSENSITIVE = 1 +RECOGNIZE_DEFINITION = 2 + +recognize_symbol: +; in: +; ebx - SymbolTree_Root (namespace), null for standard recognition regime +; ecx = name length +; esi - name followed by two hashes (as in name token) +; dl = SYMCLASS_# +; dh = any combination of RECOGNIZE_# flags +; [recognition_context.base_namespace] - namespace for standard recognition regime +; [name_volatile] = non-zero when name is provided in a temporary storage +; [name_token] = null or pointer to the contents of a preprocessed token +; out: +; ebx - SymbolTree_Leaf +; edx - SymbolTree_Foliage +; edi - SymbolTree_Root +; [name_volatile] zeroed when name has been moved to persistent storage +; note: +; when RECOGNIZE_DEFINITION option is not selected and no defined symbol of requested class is found, +; the provided result is as if RECOGNIZE_DEFINITION option was specified and expression class requested + mov [symbol_class],dl + test dh,RECOGNIZE_DEFINITION + setnz [symbol_expected] + or [symbol_required],1 + test dh,RECOGNIZE_CASE_INSENSITIVE + mov al,NAME_CASEINSENSITIVE + mov edx,[esi+ecx+4] + mov [case_insensitive_hash],edx + jnz name_kind_ok + mov al,NAME_CASESENSITIVE + mov edx,[esi+ecx] + mov [case_sensitive_hash],edx + name_kind_ok: + mov [name_kind],al + test ebx,ebx + jz standard_recognition + call scan_namespace + jnc symbol_recognized + mov [kept_symbol],ebx + mov [kept_symbol_flags],al + mov edi,[symbol_root] + cmp [name_kind],NAME_CASEINSENSITIVE + je no_defined_symbol + mov [current_symbol],ebx + mov ebx,[ebx+SymbolTree_Leaf.fallback_neighbour] + test ebx,ebx + jnz check_namespace_fallback + mov [name_kind],NAME_CASEINSENSITIVE + mov edx,[case_insensitive_hash] + mov ebx,edi + call scan_namespace + mov eax,[current_symbol] + mov [eax+SymbolTree_Leaf.fallback_neighbour],ebx + jc no_defined_symbol + symbol_recognized: + mov edi,[symbol_root] + retn + standard_recognition: + mov ebx,[recognition_context.base_namespace] + call scan_namespace + jnc symbol_recognized + mov [kept_symbol],ebx + mov [kept_symbol_flags],al + mov edi,[symbol_root] + find_in_wider_scope: + mov edx,[case_insensitive_hash] + mov [current_symbol],ebx + cmp [name_kind],NAME_CASEINSENSITIVE + je find_in_namespace_chain + mov ebx,[ebx+SymbolTree_Leaf.fallback_neighbour] + test ebx,ebx + jnz check_fallback_neighbour + mov [name_kind],NAME_CASEINSENSITIVE + mov ebx,[symbol_root] + call scan_namespace + mov eax,[current_symbol] + mov [eax+SymbolTree_Leaf.fallback_neighbour],ebx + jnc symbol_recognized + no_neighbour_found: + mov ebx,[current_symbol] + mov [name_kind],NAME_CASESENSITIVE + mov edx,[case_sensitive_hash] + find_in_namespace_chain: + mov ebx,[ebx+SymbolTree_Leaf.fallback_parent] + test ebx,ebx + jnz check_fallback_parent + mov edi,[symbol_root] + mov ebx,[edi+SymbolTree_Root.parent_branch] + test ebx,ebx + jz no_defined_symbol + mov ebx,[ebx+SymbolTree_Foliage.root] + call scan_namespace + mov eax,[current_symbol] + mov [eax+SymbolTree_Leaf.fallback_parent],ebx + jc find_in_wider_scope + mov edi,[symbol_root] + retn + check_fallback_neighbour: + call get_available_value + jc no_neighbour_found + fallback_neighbour_ok: + mov edx,[ebx+SymbolTree_Leaf.branch] + mov edi,[edx+SymbolTree_Foliage.root] + retn + check_fallback_parent: + call get_available_value + mov edx,[ebx+SymbolTree_Leaf.branch] + mov edi,[edx+SymbolTree_Foliage.root] + mov [symbol_root],edi + jc find_in_wider_scope + retn + check_namespace_fallback: + call get_available_value + jnc fallback_neighbour_ok + no_defined_symbol: + cmp [symbol_class],SYMCLASS_EXPRESSION + je return_kept_symbol + mov [symbol_class],SYMCLASS_EXPRESSION + or [symbol_expected],1 + mov ebx,[kept_symbol] + mov ebx,[ebx+SymbolTree_Leaf.branch] + mov edi,[ebx+SymbolTree_Foliage.root] + jmp scan_symbol_branch + return_kept_symbol: + mov ebx,[kept_symbol] + mov edx,[ebx+SymbolTree_Leaf.branch] + mov edi,[edx+SymbolTree_Foliage.root] + mov al,[kept_symbol_flags] + mov [ebx+SymbolTree_Leaf.flags],al + retn + +scan_namespace: +; in: +; ebx - SymbolTree_Root +; edx = hash +; ecx = name length +; esi - name +; [name_kind] = NAME_# +; [name_volatile] = non-zero when name is provided in a temporary storage +; [name_token] = null or pointer to the contents of a preprocessed token +; [symbol_class] = SYMCLASS_# +; [symbol_required] = non-zero when symbol needs to be added if not found +; [symbol_expected] = non-zero when symbol needs not be checked for its value availability +; out: +; ebx - SymbolTree_Leaf, null when nothing found +; edx - SymbolTree_Foliage, null when no such branch exists +; [symbol_root] - SymbolTree_Root +; [symbol_branch] - SymbolTree_Foliage +; [name_volatile] zeroed when name has been moved to persistent storage +; when [symbol_expected] = 0: +; cf set when symbol not found or has no defined value +; al = copy of symbol prediction flags before they were affected by this function +; when [symbol_expected] = 1: +; cf set when symbol not found +; preserves: ecx, [esi] + mov [symbol_root],ebx + test [ebx+SymbolTree_Root.attributes],SYMTREE_LOCAL + jnz scan_local_namespace + add ebx,sizeof.SymbolTree_Root + if TREE_HEIGHT > 1 + mov edi,TREE_HEIGHT + follow_tree: + mov eax,edx + and eax,(1 shl TREE_NODE_BITS)-1 + shr edx,TREE_NODE_BITS + lea ebx,[ebx+eax*4] + else + and edx,(1 shl TREE_NODE_BITS)-1 + lea ebx,[ebx+edx*4] + end if + mov eax,[ebx] + test eax,eax + jz unregistered_hash + mov ebx,eax + if TREE_HEIGHT > 1 + dec edi + jnz follow_tree + end if + scan_foliage_branches: + movzx eax,[name_kind] + cmp [ebx+SymbolTree_Foliage.name_kind],al + jne next_foliage_branch + cmp [ebx+SymbolTree_Foliage.name_length],ecx + jne next_foliage_branch + mov edi,[ebx+SymbolTree_Foliage.name_data] + cmp esi,edi + je scan_symbol_branch + cmp al,NAME_ABSTRACT + je next_foliage_branch + jecxz scan_symbol_branch + repe cmpsb + jne names_not_identical + mov esi,edi + mov ecx,[ebx+SymbolTree_Foliage.name_length] + sub esi,ecx + and [name_volatile],0 + mov edx,[name_token] + test edx,edx + jz scan_symbol_branch + test [ebx+SymbolTree_Foliage.flags],FOLIAGE_WITH_TOKEN + jz use_token_for_foliage + lea eax,[esi-4] + xchg [edx],eax + lea edx,[esi-4] + mov [eax+4+ecx+8],edx + jmp scan_symbol_branch + use_token_for_foliage: + mov esi,[edx] + add esi,4 + mov [ebx+SymbolTree_Foliage.name_data],esi + or [ebx+SymbolTree_Foliage.flags],FOLIAGE_WITH_TOKEN + jmp scan_symbol_branch + hash_collision: + add esi,ecx + mov ecx,[ebx+SymbolTree_Foliage.name_length] + sub esi,ecx + next_foliage_branch: + mov eax,[ebx+SymbolTree_Foliage.next] + test eax,eax + jz unregistered_name + mov ebx,eax + jmp scan_foliage_branches + names_not_identical: + cmp al,NAME_CASEINSENSITIVE + jne hash_collision + dec esi + dec edi + inc ecx + case_insensitive_compare: + lodsb + mov dl,[characters+eax] + mov al,[edi] + inc edi + dec ecx + cmp dl,[characters+eax] + jne hash_collision + test ecx,ecx + jnz case_insensitive_compare + mov ecx,[ebx+SymbolTree_Foliage.name_length] + sub esi,ecx + scan_symbol_branch: + ; in: + ; ebx - SymbolTree_Foliage + ; [symbol_class] = SYMCLASS_# + ; [symbol_required] = non-zero when symbol needs to be added if not found + ; [symbol_expected] = non-zero when symbol needs not be checked for its value availability + ; out: + ; ebx - SymbolTree_Leaf, null when nothing found + ; edx - SymbolTree_Foliage + ; [symbol_branch] - SymbolTree_Foliage + ; when [symbol_expected] = 0: + ; cf set when symbol not found or has no defined value + ; al = copy of symbol prediction flags before they were affected by this function + ; when [symbol_expected] = 1: + ; cf set when symbol not found + ; preserves: ecx, esi, edi + mov [symbol_branch],ebx + add ebx,sizeof.SymbolTree_Foliage + mov dl,[symbol_class] + scan_leaves: + mov al,[ebx+SymbolTree_Leaf.class] + cmp al,dl + je leaf_found + mov eax,[ebx+SymbolTree_Leaf.next] + test eax,eax + jz leaves_scanned + mov ebx,eax + jmp scan_leaves + leaf_found: + cmp [symbol_expected],0 + jne symbol_found + call get_available_value + jnc symbol_found + no_defined_symbol_found: + mov edx,[symbol_branch] + stc + retn + symbol_found: + mov edx,[symbol_branch] + clc + retn + leaves_scanned: + mov al,[symbol_required] + or al,[symbol_expected] + jnz attach_symbol_leaf + xor ebx,ebx + jmp no_defined_symbol_found + unregistered_name: + cmp [symbol_required],0 + je name_not_found + push ecx + lea ebx,[ebx+SymbolTree_Foliage.next] + jmp attach_foliage_branch + name_not_found: + xor ebx,ebx + xor edx,edx + stc + retn + unregistered_hash: + cmp [symbol_required],0 + je name_not_found + push ecx + if TREE_HEIGHT > 1 + expand_tree: + dec edi + jz attach_foliage_branch + mov ecx,sizeof.SymbolTree_Node + call create_tree_element + mov [ebx],eax + mov ebx,eax + mov eax,edx + and eax,(1 shl TREE_NODE_BITS)-1 + shr edx,TREE_NODE_BITS + lea ebx,[ebx+eax*4] + jmp expand_tree + end if + attach_foliage_branch: + mov ecx,sizeof.SymbolTree_Foliage + sizeof.SymbolTree_Leaf + call create_tree_element + mov [ebx],eax + mov [symbol_branch],eax + mov ebx,eax + pop ecx + mov eax,[symbol_root] + mov [ebx+SymbolTree_Foliage.root],eax + mov al,[name_kind] + mov [ebx+SymbolTree_Foliage.name_kind],al + mov [ebx+SymbolTree_Foliage.name_length],ecx + cmp al,NAME_ABSTRACT + je symbol_name_stored + mov edx,[name_token] + test edx,edx + jnz symbol_name_from_token + cmp [name_volatile],0 + je symbol_name_stored + call store_string + and [name_volatile],0 + jmp symbol_name_stored + symbol_name_from_token: + or [ebx+SymbolTree_Foliage.flags],FOLIAGE_WITH_TOKEN + mov esi,[edx] + add esi,4 + symbol_name_stored: + mov [ebx+SymbolTree_Foliage.name_data],esi + lea ebx,[ebx+sizeof.SymbolTree_Foliage] + jmp fill_new_symbol_leaf + attach_symbol_leaf: + push ecx + mov ecx,sizeof.SymbolTree_Leaf + call create_tree_element + mov [ebx+SymbolTree_Leaf.next],eax + mov ebx,eax + pop ecx + fill_new_symbol_leaf: + mov al,[symbol_class] + mov [ebx+SymbolTree_Leaf.class],al + mov edx,[symbol_branch] + cmp al,SYMCLASS_PARAMETER + jne namespace_flags_updated + cmp [name_kind],NAME_CASEINSENSITIVE + jne namespace_flags_updated + mov eax,[edx+SymbolTree_Foliage.root] + or [eax+SymbolTree_Root.attributes],SYMTREE_WITH_CASEINSENSITIVE_PARAMETERS + namespace_flags_updated: + mov [ebx+SymbolTree_Leaf.branch],edx + cmp [symbol_expected],0 + jne symbol_found + xor al,al + or [ebx+SymbolTree_Leaf.flags],SYM_PREDICTED + jmp no_defined_symbol_found + scan_local_namespace: + add ebx,sizeof.SymbolTree_Root + if LOCAL_TREE_HEIGHT > 1 + mov edi,LOCAL_TREE_HEIGHT + follow_local_tree: + mov eax,edx + and eax,(1 shl LOCAL_TREE_NODE_BITS)-1 + shr edx,LOCAL_TREE_NODE_BITS + lea ebx,[ebx+eax*4] + else + and edx,(1 shl LOCAL_TREE_NODE_BITS)-1 + lea ebx,[ebx+edx*4] + end if + mov eax,[ebx] + test eax,eax + jz local_unregistered_hash + mov ebx,eax + if LOCAL_TREE_HEIGHT > 1 + dec edi + jnz follow_local_tree + end if + jmp scan_foliage_branches + local_unregistered_hash: + cmp [symbol_required],0 + je name_not_found + push ecx + if LOCAL_TREE_HEIGHT > 1 + expand_local_tree: + dec edi + jz attach_foliage_branch + mov ecx,sizeof.SymbolTree_LocalNode + call create_tree_element + mov [ebx],eax + mov ebx,eax + mov eax,edx + and eax,(1 shl LOCAL_TREE_NODE_BITS)-1 + shr edx,LOCAL_TREE_NODE_BITS + lea ebx,[ebx+eax*4] + jmp expand_local_tree + else + jmp attach_foliage_branch + end if + +get_abstract_symbol: +; in: +; eax:ecx = symbol identifier +; ebx - SymbolTree_Root, null for local scope +; dl = SYMCLASS_# +; out: +; ebx - SymbolTree_Leaf +; edx - SymbolTree_Foliage +; edi - SymbolTree_Root + mov [symbol_class],dl + test ebx,ebx + jnz scope_selected + mov ebx,[current_context.base_namespace] + scope_selected: + mov esi,eax + mov edx,FNV_OFFSET + mov [minor_identifier],ecx + mov ecx,4 + hash_major_identifier: + xor dl,al + imul edx,FNV_PRIME + shr eax,8 + loop hash_major_identifier + mov eax,[minor_identifier] + mov ecx,4 + hash_minor_identifier: + xor dl,al + imul edx,FNV_PRIME + shr eax,8 + loop hash_minor_identifier + mov ecx,[minor_identifier] + mov [name_kind],NAME_ABSTRACT + or [symbol_required],1 + or [symbol_expected],1 + call scan_namespace + mov edi,[symbol_root] + retn + +create_tree_element: +; in: ecx = length of element +; out: eax - pointer to allocated and zeroed memory +; preserves: ebx, ecx, edx, esi, edi + sub [tree_reserve_length],ecx + jc expand_tree_reserve + mov eax,[tree_reserve] + add [tree_reserve],ecx + retn + expand_tree_reserve: + push ecx edx edi + mov ecx,10000h + call malloc_fixed + mov edx,eax + xchg edx,[tree_blocks] + mov [eax],edx + lea edi,[eax+4] + sub ecx,4 + mov [tree_reserve],edi + mov [tree_reserve_length],ecx + mov edx,eax + xor eax,eax + shr ecx,2 + rep stosd + mov eax,edx + pop edi edx ecx + jmp create_tree_element + +store_string: +; in: esi - string, ecx = length +; out: esi - stored string +; preserves: ebx, ecx, edi + cmp ecx,[storage_free_space_length] + ja expand_storage + storage_ready: + push ecx edi + mov edi,[storage_free_space] + add [storage_free_space],ecx + sub [storage_free_space_length],ecx + move_to_storage: + mov eax,edi + shr ecx,1 + jnc movsb_ok + movsb + movsb_ok: + shr ecx,1 + jnc movsw_ok + movsw + movsw_ok: + rep movsd + mov esi,eax + pop edi ecx + retn + expand_storage: + cmp ecx,100h + jae store_long_string + push ecx + mov ecx,10000h + call malloc_fixed + mov edx,eax + xchg edx,[storage_blocks] + mov [eax],edx + add eax,4 + sub ecx,4 + mov [storage_free_space],eax + mov [storage_free_space_length],ecx + pop ecx + jmp storage_ready + store_long_string: + push ecx + add ecx,4 + call malloc_fixed + mov edx,[storage_blocks] + test edx,edx + jz long_first_string + mov ecx,eax + xchg ecx,[edx] + mov [eax],ecx + long_string_storage_ready: + add eax,4 + mov ecx,[esp] + push edi + mov edi,eax + jmp move_to_storage + long_first_string: + mov [storage_blocks],eax + mov [eax],edx + jmp long_string_storage_ready + +get_symbol_namespace: +; in: +; edx - SymbolTree_Foliage +; out: +; ebx - SymbolTree_Root of child namespace +; preserves: edx, esi, edi + mov ebx,[edx+SymbolTree_Foliage.child_namespace] + test ebx,ebx + jnz symbol_namespace_ok + mov ecx,sizeof.SymbolTree_Root + sizeof.SymbolTree_Node + call create_tree_element + mov [eax+SymbolTree_Root.parent_branch],edx + mov [edx+SymbolTree_Foliage.child_namespace],eax + mov ebx,eax + symbol_namespace_ok: + retn +get_local_namespace: +; same as get_symbol_namespace + mov ebx,[edx+SymbolTree_Foliage.child_namespace] + test ebx,ebx + jnz symbol_namespace_ok + mov ecx,sizeof.SymbolTree_Root + sizeof.SymbolTree_LocalNode + call create_tree_element + or [eax+SymbolTree_Root.attributes],SYMTREE_LOCAL + or [eax+SymbolTree_Root.flags],NAMESPACE_LOCAL + mov [edx+SymbolTree_Foliage.child_namespace],eax + mov ebx,eax + retn + +get_local_anchor: +; in: +; eax = instruction identifier +; ecx = context identifier +; out: +; edx - SymbolTree_Foliage where a local namespace can be anchored + xor ebx,ebx + mov dl,SYMCLASS_PARAMETER + call get_abstract_symbol + mov eax,[ebx+SymbolTree_Leaf.definition] + test eax,eax + jz create_anchor_counter + mov ecx,[current_pass] + cmp ecx,[eax+ValueDefinition.pass] + je increment_anchor_counter + mov [eax+ValueDefinition.pass],ecx + and [eax+ValueDefinition.value],0 + jmp increment_anchor_counter + create_anchor_counter: + mov ecx,sizeof.ValueDefinition + call create_tree_element + mov [ebx+SymbolTree_Leaf.definition],eax + inc [eax+ValueDefinition.reference_count] + mov [eax+ValueDefinition.type],VALTYPE_PLAIN + mov ecx,[current_pass] + mov [eax+ValueDefinition.pass],ecx + increment_anchor_counter: + mov ecx,[eax+ValueDefinition.value] + inc [eax+ValueDefinition.value] + jecxz local_anchor_ready + mov eax,ebx + xor ebx,ebx + mov dl,SYMCLASS_PARAMETER + call get_abstract_symbol + local_anchor_ready: + retn + +get_available_value: +; in: +; ebx - SymbolTree_Leaf +; out: +; cf set if symbol is not considered defined +; edx - ValueDefinition +; al = copy of symbol prediction flags before they were affected by this function +; preserves: ebx, ecx, esi, edi + mov edx,[ebx+SymbolTree_Leaf.definition] + test [ebx+SymbolTree_Leaf.flags],SYM_VARIABLE + jnz get_variable_value + test edx,edx + jz symbol_predicted_undefined + mov al,[edx+ValueDefinition.flags] + test al,VAL_INTERNAL + jnz symbol_defined + not al + test al,VAL_IN_USE + VAL_NONRECURSIVE + jz symbol_undefined + mov eax,[current_pass] + sub eax,[edx+ValueDefinition.pass] + jz symbol_defined + cmp eax,1 + ja symbol_predicted_undefined + symbol_predicted_defined: + mov al,[ebx+SymbolTree_Leaf.flags] + or [ebx+SymbolTree_Leaf.flags],SYM_PREDICTED + SYM_PREDICTED_DEFINED + ; clc + retn + symbol_defined: + mov al,[ebx+SymbolTree_Leaf.flags] + clc + retn + symbol_predicted_undefined: + mov al,[ebx+SymbolTree_Leaf.flags] + or al,SYM_PREDICTED + and al,not SYM_PREDICTED_DEFINED + xchg [ebx+SymbolTree_Leaf.flags],al + stc + retn + get_variable_value: + test edx,edx + jz symbol_undefined + inspect_definition: + test [edx+ValueDefinition.flags],VAL_INTERNAL + jnz symbol_defined + test [edx+ValueDefinition.flags],VAL_IN_USE + jnz try_previous_definition + mov eax,[current_pass] + cmp eax,[edx+ValueDefinition.pass] + je symbol_defined + cmp edx,[ebx+SymbolTree_Leaf.definition] + je retire_outdated_definition + try_previous_definition: + mov edx,[edx+ValueDefinition.previous] + test edx,edx + jnz inspect_definition + symbol_undefined: + mov al,[ebx+SymbolTree_Leaf.flags] + stc + retn + retire_outdated_definition: + mov eax,edx + dec [edx+ValueDefinition.reference_count] + xchg edx,[ebx+SymbolTree_Leaf.retired_definition] + xchg edx,[eax+ValueDefinition.previous] + mov [ebx+SymbolTree_Leaf.definition],edx + jmp get_variable_value + +mark_symbol_as_used: +; in: +; ebx - SymbolTree_Leaf +; edx - ValueDefinition, null when value is unavailable but used nonetheless +; preserves: ebx, ecx, edx, esi, edi +; note: +; normally setting SymbolTree_Leaf.last_use_pass is enough, but this function +; improves prediction quality of USED operator; + mov eax,[current_pass] + mov [ebx+SymbolTree_Leaf.last_use_pass],eax + test edx,edx + jz mark_used_fallbacks + retn + mark_used_fallbacks: + push ebx edx + mark_fallback_neighbour: + mov edx,[ebx+SymbolTree_Leaf.fallback_neighbour] + test edx,edx + jz mark_fallback_parent + mov [edx+SymbolTree_Leaf.last_use_pass],eax + mark_fallback_parent: + mov ebx,[ebx+SymbolTree_Leaf.fallback_parent] + test ebx,ebx + jz used_fallbacks_marked + mov [ebx+SymbolTree_Leaf.last_use_pass],eax + jmp mark_fallback_neighbour + used_fallbacks_marked: + pop edx ebx + retn + +use_available_value: +; same as get_available_value, but also includes operation of mark_symbol_as_used + mov eax,[current_pass] + mov [ebx+SymbolTree_Leaf.last_use_pass],eax + call get_available_value + jc use_unavailable_value + retn + use_unavailable_value: + call mark_used_fallbacks + stc + retn + +create_constant_value_definition: +; in: +; ebx - SymbolTree_Leaf +; out: +; edx - ValueDefinition possibly containing value to update, null if definition forbidden +; preserves: ebx, esi, edi + test [ebx+SymbolTree_Leaf.flags],SYM_VARIABLE + jnz symbol_already_defined + or [ebx+SymbolTree_Leaf.flags],SYM_CONSTANT + mov eax,[ebx+SymbolTree_Leaf.definition] + test eax,eax + jz add_value_definition + test [eax+ValueDefinition.flags],VAL_INTERNAL + jnz symbol_already_defined + mov ecx,[eax+ValueDefinition.pass] + cmp ecx,[current_pass] + jne reset_value + symbol_already_defined: + mov edx,_conflicting_definition + call register_error + xor edx,edx + retn +update_value_definition: +; in: +; ebx - SymbolTree_Leaf +; out: +; edx - ValueDefinition possibly containing value to update, null if definition forbidden +; preserves: ebx, esi, edi + mov eax,[ebx+SymbolTree_Leaf.definition] + test eax,eax + jz add_value_definition + test [eax+ValueDefinition.flags],VAL_INTERNAL + jnz value_redefinition + mov ecx,[eax+ValueDefinition.pass] + cmp ecx,[current_pass] + jne reset_value + test [ebx+SymbolTree_Leaf.flags],SYM_CONSTANT or SYM_LINK + jnz constant_redefined + mov edx,eax + or [ebx+SymbolTree_Leaf.flags],SYM_VARIABLE + test [ebx+SymbolTree_Leaf.flags],SYM_PREDICTED + jz dynamic_value_definition_ready + and [ebx+SymbolTree_Leaf.flags],not SYM_PREDICTED + test [ebx+SymbolTree_Leaf.flags],SYM_PREDICTED_DEFINED + jz dynamic_value_definition_ready + or [next_pass_needed],1 + dynamic_value_definition_ready: + cmp [edx+ValueDefinition.reference_count],1 + ja dynamic_value_in_use + retn + dynamic_value_in_use: + mov eax,[edx+ValueDefinition.previous] + mov [ebx+SymbolTree_Leaf.definition],eax + dec [edx+ValueDefinition.reference_count] + mov eax,edx + xchg eax,[ebx+SymbolTree_Leaf.retired_definition] + mov [edx+ValueDefinition.previous],eax +create_value_definition: +; in: +; ebx - SymbolTree_Leaf +; out: +; edx - ValueDefinition possibly containing value to update, null if definition forbidden +; preserves: ebx, esi, edi + mov eax,[ebx+SymbolTree_Leaf.definition] + test eax,eax + jz add_value_definition + test [eax+ValueDefinition.flags],VAL_INTERNAL + jnz value_redefinition + mov ecx,[eax+ValueDefinition.pass] + cmp ecx,[current_pass] + je value_redefinition + reset_value: + test [ebx+SymbolTree_Leaf.flags],SYM_LINK + jnz reset_link_value + mov edx,[ebx+SymbolTree_Leaf.retired_definition] + retire_previous_values: + dec [eax+ValueDefinition.reference_count] + xchg edx,[eax+ValueDefinition.previous] + xchg eax,edx + test eax,eax + jz previous_values_retired + test [eax+ValueDefinition.flags],VAL_INTERNAL + jz retire_previous_values + previous_values_retired: + mov [ebx+SymbolTree_Leaf.definition],edx + xchg eax,[edx+ValueDefinition.previous] + mov [ebx+SymbolTree_Leaf.retired_definition],eax + cmp [edx+ValueDefinition.reference_count],0 + jne add_value_definition + inc [edx+ValueDefinition.reference_count] + test [ebx+SymbolTree_Leaf.flags],SYM_VARIABLE + jnz reset_value_type + retn + reset_link_value: + and [ebx+SymbolTree_Leaf.flags],not (SYM_LINK or SYM_LINK_PREDICTED) + mov eax,[ebx+SymbolTree_Leaf.last_use_pass] + cmp eax,[current_pass] + jne reset_current_link_value + or [next_pass_needed],1 + reset_current_link_value: + xor eax,eax + xchg eax,[ebx+SymbolTree_Leaf.definition] + dec [eax+ValueDefinition.reference_count] + jnz reset_previous_link_value + test [eax+ValueDefinition.flags],VAL_DETACHED + jz reset_previous_link_value + mov ecx,eax + xchg eax,[retired_definition] + mov [ecx+ValueDefinition.previous],eax + reset_previous_link_value: + xor eax,eax + xchg eax,[ebx+SymbolTree_Leaf.retired_definition] + test eax,eax + jz add_value_definition + dec [eax+ValueDefinition.reference_count] + jnz add_value_definition + test [eax+ValueDefinition.flags],VAL_DETACHED + jz add_value_definition + mov ecx,eax + xchg eax,[retired_definition] + mov [ecx+ValueDefinition.previous],eax + jmp add_value_definition + constant_redefined: + mov edx,_conflicting_definition + call register_error + xor edx,edx + retn + value_redefinition: + cmp [eax+ValueDefinition.type],VALTYPE_ELEMENT + je constant_redefined + test [ebx+SymbolTree_Leaf.flags],SYM_CONSTANT or SYM_LINK + jnz constant_redefined + test [ebx+SymbolTree_Leaf.flags],SYM_VARIABLE + jnz add_value_definition + or [ebx+SymbolTree_Leaf.flags],SYM_VARIABLE + test [ebx+SymbolTree_Leaf.flags],SYM_PREDICTED + jz add_value_definition + and [ebx+SymbolTree_Leaf.flags],not SYM_PREDICTED + test [ebx+SymbolTree_Leaf.flags],SYM_PREDICTED_DEFINED + jz add_value_definition + or [next_pass_needed],1 + add_value_definition: + lea ecx,[ebx+SymbolTree_Leaf.retired_definition] + retrieve_retired_value: + mov eax,[ecx] + test eax,eax + jz new_value_definition + cmp [eax+ValueDefinition.reference_count],0 + jne retired_value_immutable + inc [eax+ValueDefinition.reference_count] + mov edx,[eax+ValueDefinition.previous] + mov [ecx],edx + mov [eax+ValueDefinition.type],VALTYPE_RESERVED + jmp adopt_value_definition + retired_value_immutable: + lea ecx,[eax+ValueDefinition.previous] + jmp retrieve_retired_value + new_value_definition: + mov ecx,sizeof.ValueDefinition + call create_tree_element + mov ecx,eax + xchg ecx,[value_definition_chain] + mov [eax+ValueDefinition.interlink],ecx + assert VALTYPE_RESERVED = 0 + inc [eax+ValueDefinition.reference_count] + adopt_value_definition: + mov edx,eax + xchg eax,[ebx+SymbolTree_Leaf.definition] + mov [edx+ValueDefinition.previous],eax + test [ebx+SymbolTree_Leaf.flags],SYM_VARIABLE + jnz reset_value_type + test eax,eax + jz value_definition_ready + mov ecx,eax + xchg ecx,[ebx+SymbolTree_Leaf.retired_definition] + xchg ecx,[eax+ValueDefinition.previous] + mov [edx+ValueDefinition.previous],ecx + ; test ecx,ecx + ; jnz internal_error + mov ecx,[eax+ValueDefinition.pass] + mov [edx+ValueDefinition.pass],ecx + mov cl,[eax+ValueDefinition.type] + mov [edx+ValueDefinition.type],cl + mov ecx,[eax+ValueDefinition.value_length] + mov [edx+ValueDefinition.value_length],ecx + jecxz value_definition_ready + push esi edi + mov esi,[eax+ValueDefinition.value] + mov edi,[edx+ValueDefinition.value] + cmp ecx,[edx+ValueDefinition.block_length] + jbe duplicate_value + push edx + cmp [edx+ValueDefinition.block_length],0 + je reallocate_for_duplicate + push ecx + xor eax,eax + xchg eax,[edx+ValueDefinition.value] + call mfree + pop ecx + reallocate_for_duplicate: + mov edi,ecx + call malloc + pop edx + mov [edx+ValueDefinition.value],eax + mov [edx+ValueDefinition.block_length],ecx + mov ecx,edi + mov edi,eax + duplicate_value: + mov eax,ecx + shr ecx,2 + rep movsd + mov cl,al + and cl,11b + rep movsb + pop edi esi + value_definition_ready: + retn + reset_value_type: + mov [edx+ValueDefinition.type],VALTYPE_RESERVED + retn + +remove_value_definition: +; in: +; ebx - SymbolTree_Leaf +; edx - ValueDefinition, null to remove the present value +; edi - SymbolTree_Leaf where to move the value, null for plain remove +; out: +; cf set if there was no value to remove +; when cf = 0: +; edx - ValueDefinition that got removed +; preserves: ebx, esi, edi + test [ebx+SymbolTree_Leaf.flags],SYM_VARIABLE + jnz variable_ready + test [ebx+SymbolTree_Leaf.flags],SYM_CONSTANT or SYM_LINK + jnz cannot_apply_to_constant + or [ebx+SymbolTree_Leaf.flags],SYM_VARIABLE + test [ebx+SymbolTree_Leaf.flags],SYM_PREDICTED + jz variable_ready + and [ebx+SymbolTree_Leaf.flags],not SYM_PREDICTED + test [ebx+SymbolTree_Leaf.flags],SYM_PREDICTED_DEFINED + jz variable_ready + or [next_pass_needed],1 + variable_ready: + lea ecx,[ebx+SymbolTree_Leaf.definition] + test edx,edx + jnz find_definition_to_remove + mov edx,[ecx] + test edx,edx + jz no_definition_to_remove + test [edx+ValueDefinition.flags],VAL_INTERNAL + jnz no_definition_to_remove + cmp [edx+ValueDefinition.type],VALTYPE_RESERVED + je no_definition_to_remove + mov eax,[edx+ValueDefinition.pass] + cmp eax,[current_pass] + jne no_definition_to_remove + remove_definition: + mov eax,[edx+ValueDefinition.previous] + test edi,edi + jnz move_definition + mov [ecx],eax + mov eax,[ebx+SymbolTree_Leaf.retired_definition] + mov [edx+ValueDefinition.previous],eax + dec [edx+ValueDefinition.reference_count] + mov [ebx+SymbolTree_Leaf.retired_definition],edx + clc + retn + move_definition: + cmp edi,edx + je definition_moved + test [edi+SymbolTree_Leaf.flags],SYM_VARIABLE + jnz append_moved_definition + test [edi+SymbolTree_Leaf.flags],SYM_CONSTANT or SYM_LINK + jnz cannot_apply_to_constant + or [edi+SymbolTree_Leaf.flags],SYM_VARIABLE + test [edi+SymbolTree_Leaf.flags],SYM_PREDICTED + jz append_moved_definition + and [ebx+SymbolTree_Leaf.flags],not SYM_PREDICTED + test [ebx+SymbolTree_Leaf.flags],SYM_PREDICTED_DEFINED + jz append_moved_definition + or [next_pass_needed],1 + append_moved_definition: + mov [ecx],eax + mov eax,[edi+SymbolTree_Leaf.definition] + mov [edx+ValueDefinition.previous],eax + mov [edi+SymbolTree_Leaf.definition],edx + definition_moved: + clc + retn + find_definition_to_remove: + mov eax,[ecx] + cmp eax,edx + je remove_definition + test eax,eax + jz no_definition_to_remove + lea ecx,[eax+ValueDefinition.previous] + jmp find_definition_to_remove + cannot_apply_to_constant: + mov edx,_cannot_apply_to_constant + call register_error + no_definition_to_remove: + stc + retn + +assign_value: +; in: +; ebx - SymbolTree_Leaf +; edx - ValueDefinition prepared to receive the new value +; esi - value +; ecx = length of value +; [value_type] = VALTYPE_# +; preserves: ebx, edx + and [edx+ValueDefinition.flags],0 + mov eax,[current_pass] + sub eax,[edx+ValueDefinition.pass] + add [edx+ValueDefinition.pass],eax + test [ebx+SymbolTree_Leaf.flags],SYM_VARIABLE + jnz reuse_value_block + cmp eax,[current_pass] + je reuse_value_block + mov edi,[ebx+SymbolTree_Leaf.last_use_pass] + cmp edi,[current_pass] + jne reuse_value_block + cmp ecx,[edx+ValueDefinition.value_length] + je update_value + or [next_pass_needed],1 + reuse_value_block: + mov [edx+ValueDefinition.value_length],ecx + mov edi,[edx+ValueDefinition.value] + cmp ecx,[edx+ValueDefinition.block_length] + jbe write_value + push edx + push ecx + cmp [edx+ValueDefinition.block_length],0 + je new_value + xor eax,eax + xchg eax,[edx+ValueDefinition.value] + call mfree + new_value: + pop ecx + call malloc + pop edx + mov [edx+ValueDefinition.value],eax + mov [edx+ValueDefinition.block_length],ecx + mov edi,eax + write_value: + mov al,[value_type] + mov [edx+ValueDefinition.type],al + mov eax,[edx+ValueDefinition.value_length] + mov ecx,eax + shr ecx,2 + rep movsd + mov cl,al + and cl,11b + rep movsb + value_written: + retn + update_value: + mov edi,[edx+ValueDefinition.value] + cmp eax,1 + ja rewrite_value + mov al,[value_type] + cmp [edx+ValueDefinition.type],al + jne rewrite_value + jecxz value_written + cmp al,VALTYPE_SYMBOLIC + je update_symbolic_value + mov eax,[edx+ValueDefinition.value_length] + mov ecx,eax + shr ecx,2 + repe cmpsd + jne value_dword_changed + mov cl,al + and cl,11b + repe cmpsb + je value_updated + inc ecx + dec esi + dec edi + or [next_pass_needed],1 + rep movsb + value_updated: + retn + value_dword_changed: + or [next_pass_needed],1 + inc ecx + sub esi,4 + sub edi,4 + rep movsd + mov cl,al + and cl,11b + rep movsb + retn + update_symbolic_value: + mov ecx,[edx+ValueDefinition.value_length] + push edx + call compare_symbolic_values + pop edx + jecxz value_updated + value_changed: + or [next_pass_needed],1 + mov eax,ecx + shr ecx,2 + rep movsd + mov cl,al + and cl,11b + rep movsb + retn + rewrite_value: + or [next_pass_needed],1 + jmp write_value + +expand_value: +; in: +; edx - ValueDefinition +; ebx = number of additional zero bytes to append to the value +; preserves: ebx, edx, esi + mov eax,[edx+ValueDefinition.value_length] + mov edi,[edx+ValueDefinition.value] + add edi,eax + add eax,ebx + jc out_of_memory + cmp eax,[edx+ValueDefinition.block_length] + jbe append_zero_bytes + push eax + bsr ecx,eax + dec cl + shr eax,cl + inc eax + shl eax,cl + mov ecx,eax + pop eax + cmp ecx,eax + jbe out_of_memory + mov eax,[edx+ValueDefinition.value] + push edx + call realloc + pop edx + mov [edx+ValueDefinition.value],eax + mov [edx+ValueDefinition.block_length],ecx + mov edi,eax + add edi,[edx+ValueDefinition.value_length] + append_zero_bytes: + add [edx+ValueDefinition.value_length],ebx + xor eax,eax + mov ecx,ebx + shr ecx,2 + rep stosd + mov cl,bl + and cl,11b + rep stosb + retn + +update_value_link: +; in: +; ebx - SymbolTree_Leaf +; edx - ValueDefinition to link +; preserves: ebx, edx +; note: value must be from the current pass + mov eax,[ebx+SymbolTree_Leaf.definition] + test eax,eax + jz values_detached + mov ecx,[eax+ValueDefinition.pass] + cmp ecx,[current_pass] + je symbol_already_defined + test [ebx+SymbolTree_Leaf.flags],SYM_LINK + jnz update_established_link + detach_and_retire_values: + test eax,eax + jz values_detached + dec [eax+ValueDefinition.reference_count] + or [eax+ValueDefinition.flags],VAL_DETACHED + mov ecx,eax + xchg eax,[retired_definition] + xchg eax,[ecx+ValueDefinition.previous] + jmp detach_and_retire_values + values_detached: + mov [ebx+SymbolTree_Leaf.definition],eax + mov eax,[ebx+SymbolTree_Leaf.retired_definition] + detach_retired_values: + test eax,eax + jz retired_values_detached + or [eax+ValueDefinition.flags],VAL_DETACHED + mov ecx,eax + xchg eax,[retired_definition] + xchg eax,[ecx+ValueDefinition.previous] + jmp detach_retired_values + retired_values_detached: + mov [ebx+SymbolTree_Leaf.retired_definition],eax + or [ebx+SymbolTree_Leaf.flags],SYM_LINK + jmp link_new_value + update_established_link: + mov ecx,[current_pass] + cmp ecx,[ebx+SymbolTree_Leaf.last_use_pass] + jne link_new_value + or [ebx+SymbolTree_Leaf.flags],SYM_LINK_PREDICTED + link_new_value: + ; cmp ecx,[edx+ValueDefinition.pass] + ; jne internal_error + mov eax,edx + inc [eax+ValueDefinition.reference_count] + xchg eax,[ebx+SymbolTree_Leaf.definition] + xchg eax,[ebx+SymbolTree_Leaf.retired_definition] + test eax,eax + jz link_updated + dec [eax+ValueDefinition.reference_count] + jnz link_updated + test [eax+ValueDefinition.flags],VAL_DETACHED + jz link_updated + mov ecx,eax + xchg eax,[retired_definition] + mov [ecx+ValueDefinition.previous],eax + link_updated: + retn + +detect_mispredictions: +; in: ebx - SymbolTree_Root +; note: +; while it is looking for mispredicted definitions, it also prepares the tree for the next pass + mov edx,[tree_stack_base] + browse_from_root: + xor eax,eax + mov [ebx+SymbolTree_Root.current_label],eax + and [ebx+SymbolTree_Root.flags],not NAMESPACE_CURRENT_LABEL_TO_CONFIRM + test [ebx+SymbolTree_Root.attributes],SYMTREE_LOCAL + jnz browse_local_tree + add ebx,sizeof.SymbolTree_Root + mov eax,[tree_stack_end] + sub eax,TREE_HEIGHT*8+16 + cmp edx,eax + jbe tree_stack_prepared + mov eax,[tree_stack_base] + sub edx,eax + mov ecx,[tree_stack_end] + sub ecx,eax + add ecx,TREE_HEIGHT*8+16 + push edx + call grow_stack + pop edx + add edx,eax + mov [tree_stack_base],eax + add eax,ecx + mov [tree_stack_end],eax + tree_stack_prepared: + mov ecx,TREE_HEIGHT + browse_node: + mov edi,ebx + dec cl + browse_branch: + cmp dword [edi],0 + je branch_browsed + test cl,cl + jnz deeper_node + mov esi,[edi] + browse_foliage: + mov eax,[esi+SymbolTree_Foliage.child_namespace] + test eax,eax + jz subtree_browsed + mov [edx],ebx + mov [edx+4],ecx + mov [edx+8],esi + mov [edx+12],edi + add edx,16 + mov ebx,eax + jmp browse_from_root + browse_local_tree: + add ebx,sizeof.SymbolTree_Root + mov eax,[tree_stack_end] + sub eax,LOCAL_TREE_HEIGHT*8+16 + cmp edx,eax + jbe local_tree_stack_prepared + mov eax,[tree_stack_base] + sub edx,eax + mov ecx,[tree_stack_end] + sub ecx,eax + add ecx,LOCAL_TREE_HEIGHT*8+16 + push edx + call grow_stack + pop edx + add edx,eax + mov [tree_stack_base],eax + add eax,ecx + mov [tree_stack_end],eax + local_tree_stack_prepared: + mov ecx,LOCAL_TREE_HEIGHT + 1 shl 8 + jmp browse_node + subtree_browsed: + mov eax,esi + add eax,sizeof.SymbolTree_Foliage + inspect_leaf: + test [eax+SymbolTree_Leaf.flags],SYM_LINK_PREDICTED + jz link_prediction_ok + push eax ecx edx esi edi + mov edx,[eax+SymbolTree_Leaf.definition] + mov ecx,[eax+SymbolTree_Leaf.retired_definition] + mov esi,dword [edx+ValueDefinition.type] + sub esi,dword [ecx+ValueDefinition.type] + test esi,0FFh + jnz link_value_mispredicted + mov esi,[ecx+ValueDefinition.value] + mov ecx,[ecx+ValueDefinition.value_length] + mov edi,[edx+ValueDefinition.value] + mov edx,[edx+ValueDefinition.value_length] + cmp ecx,edx + jne link_value_mispredicted + jecxz reset_link_prediction + shr ecx,2 + repe cmpsd + jne link_value_mispredicted + mov cl,dl + and cl,11b + repe cmpsb + je reset_link_prediction + link_value_mispredicted: + or [next_pass_needed],1 + reset_link_prediction: + and [eax+SymbolTree_Leaf.flags],not SYM_LINK_PREDICTED + pop edi esi edx ecx eax + link_prediction_ok: + test [eax+SymbolTree_Leaf.flags],SYM_VARIABLE + jnz inspect_next_leaf + and [eax+SymbolTree_Leaf.flags],not SYM_CONSTANT + test [eax+SymbolTree_Leaf.flags],SYM_PREDICTED or SYM_USAGE_PREDICTED + jz inspect_next_leaf + push edx + test [eax+SymbolTree_Leaf.flags],SYM_PREDICTED + jz definition_prediction_ok + mov edx,[eax+SymbolTree_Leaf.definition] + test edx,edx + jz detect_misprediction_of_undefined + test [edx+ValueDefinition.flags],VAL_INTERNAL + jnz detect_misprediction_of_defined + mov edx,[edx+ValueDefinition.pass] + cmp edx,[current_pass] + je detect_misprediction_of_defined + detect_misprediction_of_undefined: + test [eax+SymbolTree_Leaf.flags],SYM_PREDICTED_DEFINED + jz reset_prediction + or [next_pass_needed],1 + jmp reset_prediction + detect_misprediction_of_defined: + test [eax+SymbolTree_Leaf.flags],SYM_PREDICTED_DEFINED + jnz reset_prediction + or [next_pass_needed],1 + reset_prediction: + and [eax+SymbolTree_Leaf.flags],not SYM_PREDICTED + definition_prediction_ok: + test [eax+SymbolTree_Leaf.flags],SYM_USAGE_PREDICTED + jz usage_prediction_ok + mov edx,[eax+SymbolTree_Leaf.last_use_pass] + cmp edx,[current_pass] + je detect_misprediction_of_used + detect_misprediction_of_unused: + test [eax+SymbolTree_Leaf.flags],SYM_PREDICTED_USED + jz reset_usage_prediction + or [next_pass_needed],1 + jmp reset_usage_prediction + detect_misprediction_of_used: + test [eax+SymbolTree_Leaf.flags],SYM_PREDICTED_USED + jnz reset_usage_prediction + or [next_pass_needed],1 + reset_usage_prediction: + and [eax+SymbolTree_Leaf.flags],not SYM_USAGE_PREDICTED + usage_prediction_ok: + pop edx + jmp inspect_next_leaf + inspect_next_leaf: + mov eax,[eax+SymbolTree_Leaf.next] + test eax,eax + jnz inspect_leaf + mov esi,[esi+SymbolTree_Foliage.next] + test esi,esi + jnz browse_foliage + branch_browsed: + test ch,ch + jnz local_branch_browsed + add edi,4 + lea eax,[ebx+sizeof.SymbolTree_Node] + cmp edi,eax + jne browse_branch + inc cl + cmp cl,TREE_HEIGHT + je tree_browsed + sub edx,8 + mov ebx,[edx] + mov edi,[edx+4] + jmp branch_browsed + local_branch_browsed: + add edi,4 + lea eax,[ebx+sizeof.SymbolTree_LocalNode] + cmp edi,eax + jne browse_branch + inc cl + cmp cl,LOCAL_TREE_HEIGHT + je tree_browsed + sub edx,8 + mov ebx,[edx] + mov edi,[edx+4] + jmp local_branch_browsed + deeper_node: + mov [edx],ebx + mov [edx+4],edi + add edx,8 + mov ebx,[edi] + jmp browse_node + tree_browsed: + cmp edx,[tree_stack_base] + je complete_tree_browsed + sub edx,16 + mov ebx,[edx] + mov ecx,[edx+4] + mov esi,[edx+8] + mov edi,[edx+12] + jmp subtree_browsed + complete_tree_browsed: + retn diff --git a/x86_64_sse2_x87/fasm/source/tables.inc b/x86_64_sse2_x87/fasm/source/tables.inc new file mode 100644 index 0000000..6daa0d2 --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/tables.inc @@ -0,0 +1,633 @@ + +OPERATOR_PRECEDENCE_MASK = 7Fh +OPERATOR_RIGHT_ASSOCIATIVE = 80h + +SIZE_BYTE = 1 +SIZE_WORD = 2 +SIZE_DWORD = 4 +SIZE_PWORD = 6 +SIZE_QWORD = 8 +SIZE_TWORD = 10 +SIZE_DQWORD = 16 +SIZE_QQWORD = 32 +SIZE_DQQWORD = 64 + +PREPOSITION_AT = 0 +PREPOSITION_FROM = 1 +PREPOSITION_AS = 2 +PREPOSITION_DUP = 3 + +control_characters db 0,9,0Ah,0Dh,1Ah,20h +.count = $-control_characters + +syntactical_characters db '+-/*=<>()[]{}:?!,.|&~#`\;' +.count = $-syntactical_characters + +include_variable db 'INCLUDE',0 + +separating_operators: + + db '+',VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNARY,1 + dd calculate_to_number + + db '-',VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNARY,1 + dd calculate_neg + + db '+',VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,1 + dd calculate_add + + db '-',VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,1 + dd calculate_sub + + db '*',VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,2 + dd calculate_mul + + db '/',VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,2 + dd calculate_div + + db 0 + +symbols: + + db 3,'not',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNARY,6 + dd calculate_not + + db 3,'mod',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,3 + dd calculate_mod + + db 3,'xor',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,4 + dd calculate_xor + + db 3,'and',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,4 + dd calculate_and + + db 2,'or',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,4 + dd calculate_or + + db 3,'shl',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,5 + dd calculate_shl + + db 3,'shr',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,5 + dd calculate_shr + + db 5,'bswap',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,5 + dd calculate_bswap + + db 3,'bsf',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNARY,6 + dd calculate_bsf + + db 3,'bsr',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNARY,6 + dd calculate_bsr + + db 6,'string',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNARY,0 + dd calculate_to_string + + db 5,'float',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNARY,10 + dd calculate_to_float + + db 5,'trunc',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNARY,10 + dd extract_integer_part + + db 6,'sizeof',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNARY,10 + dd extract_size + + db 8,'lengthof',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNARY,10 + dd count_bytes + + db 10,'elementsof',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNARY,10 + dd count_elements + + db 7,'element',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,7 + dd extract_element + + db 5,'scale',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,7 + dd extract_scale + + db 8,'metadata',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,7 + dd extract_metadata + + db 9,'elementof',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,8 + OPERATOR_RIGHT_ASSOCIATIVE + dd extract_element_reverse + + db 7,'scaleof',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,8 + OPERATOR_RIGHT_ASSOCIATIVE + dd extract_scale_reverse + + db 10,'metadataof',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,8 + OPERATOR_RIGHT_ASSOCIATIVE + dd extract_metadata_reverse + + db 7,'element',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd define_element + + db 3,'equ',SYMCLASS_STRUCTURE,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd define_symbolic_variable + + db 5,'reequ',SYMCLASS_STRUCTURE,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd redefine_symbolic_variable + + db 6,'define',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd define_raw_symbolic_variable + + db 8,'redefine',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd redefine_raw_symbolic_variable + + db 7,'restore',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SYMCLASS_EXPRESSION + dd restore_variables + + db 9,'namespace',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd set_namespace + + db 7,'display',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd display_data + + db 3,'err',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd custom_error + + db 7,'include',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNCONDITIONAL,0 + dd include_source + + db 4,'eval',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd evaluate_string + + db 6,'assert',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd assert_condition + + db 7,'defined',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_COMPARATOR,VAL_INTERNAL,0 + dd check_if_defined + + db 4,'used',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_COMPARATOR,VAL_INTERNAL,0 + dd check_if_used + + db 8,'definite',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_COMPARATOR,VAL_INTERNAL,0 + dd check_if_defined_earlier + + db 6,'eqtype',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_COMPARATOR,VAL_INTERNAL,0 + dd check_if_type_equal + + db 2,'eq',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_COMPARATOR,VAL_INTERNAL,0 + dd check_if_value_equal + + db 10,'relativeto',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_COMPARATOR,VAL_INTERNAL,0 + dd check_if_relative + + db 2,'if',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNCONDITIONAL,0 + dd conditional_block + + db 5,'match',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNCONDITIONAL,0 + dd match_block + + db 6,'rmatch',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNCONDITIONAL,0 + dd raw_match_block + + db 8,'rawmatch',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNCONDITIONAL,0 + dd raw_match_block + + db 5,'while',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNCONDITIONAL,0 + dd conditionally_repeated_block + + db 6,'repeat',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNCONDITIONAL,0 + dd repeated_block + + db 7,'iterate',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNCONDITIONAL,0 + dd iterator_block + + db 4,'rept',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNCONDITIONAL,0 + dd repeated_block + + db 3,'irp',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNCONDITIONAL,0 + dd iterator_block + + db 4,'irpv',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNCONDITIONAL,0 + dd variable_iterator_block + + db 4,'indx',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd set_iterator_index + + db 5,'break',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd break_directive + + db 4,'else',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNCONDITIONAL,0 + dd else_directive + + db 3,'end',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNCONDITIONAL,0 + dd end_directive + + db 8,'postpone',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNCONDITIONAL,0 + dd postponed_block + + db 5,'macro',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNCONDITIONAL,0 + dd define_macro + + db 5,'struc',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNCONDITIONAL,0 + dd define_struc + + db 3,'esc',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNCONDITIONAL,0 + dd escape_directive + + db 5,'local',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNCONDITIONAL,0 + dd local_directive + + db 8,'outscope',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNCONDITIONAL,0 + dd outscope_directive + + db 5,'purge',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SYMCLASS_INSTRUCTION + dd restore_variables + + db 7,'restruc',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SYMCLASS_STRUCTURE + dd restore_variables + + db 7,'mvmacro',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SYMCLASS_INSTRUCTION + dd move_variable_values + + db 7,'mvstruc',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SYMCLASS_STRUCTURE + dd move_variable_values + + db 15,'calminstruction',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNCONDITIONAL,0 + dd define_calm_instruction + + db 14,'removecomments',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd choose_to_remove_comments + + db 14,'retaincomments',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd choose_to_retain_comments + + db 12,'combinelines',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd choose_to_combine_lines + + db 12,'isolatelines',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd choose_to_isolate_lines + + db 3,'org',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd set_base_address + + db 7,'section',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd begin_new_section + + db 10,'restartout',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd restart_output + + db 6,'format',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd format_directive + + db 4,'emit',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd define_data + + db 4,'emit',SYMCLASS_STRUCTURE,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd define_labeled_data + + db 3,'dbx',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd define_data + + db 3,'dbx',SYMCLASS_STRUCTURE,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd define_labeled_data + + db 2,'db',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SIZE_BYTE + dd define_data + + db 2,'db',SYMCLASS_STRUCTURE,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SIZE_BYTE + dd define_labeled_data + + db 2,'dw',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SIZE_WORD + dd define_data + + db 2,'dw',SYMCLASS_STRUCTURE,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SIZE_WORD + dd define_labeled_data + + db 2,'dd',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SIZE_DWORD + dd define_data + + db 2,'dd',SYMCLASS_STRUCTURE,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SIZE_DWORD + dd define_labeled_data + + db 2,'dp',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SIZE_PWORD + dd define_data + + db 2,'dp',SYMCLASS_STRUCTURE,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SIZE_PWORD + dd define_labeled_data + + db 2,'dq',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SIZE_QWORD + dd define_data + + db 2,'dq',SYMCLASS_STRUCTURE,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SIZE_QWORD + dd define_labeled_data + + db 2,'dt',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SIZE_TWORD + dd define_data + + db 2,'dt',SYMCLASS_STRUCTURE,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SIZE_TWORD + dd define_labeled_data + + db 3,'ddq',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SIZE_DQWORD + dd define_data + + db 3,'ddq',SYMCLASS_STRUCTURE,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SIZE_DQWORD + dd define_labeled_data + + db 3,'dqq',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SIZE_QQWORD + dd define_data + + db 3,'dqq',SYMCLASS_STRUCTURE,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SIZE_QQWORD + dd define_labeled_data + + db 4,'ddqq',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SIZE_DQQWORD + dd define_data + + db 4,'ddqq',SYMCLASS_STRUCTURE,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SIZE_DQQWORD + dd define_labeled_data + + db 2,'rb',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SIZE_BYTE + dd reserve_data + + db 2,'rb',SYMCLASS_STRUCTURE,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SIZE_BYTE + dd reserve_labeled_data + + db 2,'rw',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SIZE_WORD + dd reserve_data + + db 2,'rw',SYMCLASS_STRUCTURE,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SIZE_WORD + dd reserve_labeled_data + + db 2,'rd',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SIZE_DWORD + dd reserve_data + + db 2,'rd',SYMCLASS_STRUCTURE,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SIZE_DWORD + dd reserve_labeled_data + + db 2,'rp',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SIZE_PWORD + dd reserve_data + + db 2,'rp',SYMCLASS_STRUCTURE,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SIZE_PWORD + dd reserve_labeled_data + + db 2,'rq',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SIZE_QWORD + dd reserve_data + + db 2,'rq',SYMCLASS_STRUCTURE,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SIZE_QWORD + dd reserve_labeled_data + + db 2,'rt',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SIZE_TWORD + dd reserve_data + + db 2,'rt',SYMCLASS_STRUCTURE,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SIZE_TWORD + dd reserve_labeled_data + + db 3,'rdq',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SIZE_DQWORD + dd reserve_data + + db 3,'rdq',SYMCLASS_STRUCTURE,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SIZE_DQWORD + dd reserve_labeled_data + + db 3,'rqq',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SIZE_QQWORD + dd reserve_data + + db 3,'rqq',SYMCLASS_STRUCTURE,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SIZE_QQWORD + dd reserve_labeled_data + + db 4,'rdqq',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SIZE_DQQWORD + dd reserve_data + + db 4,'rdqq',SYMCLASS_STRUCTURE,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,SIZE_DQQWORD + dd reserve_labeled_data + + db 4,'file',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd include_data + + db 4,'file',SYMCLASS_STRUCTURE,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd include_labeled_data + + db 1,'$',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_FUNCTION,VAL_INTERNAL,0 + dd current_address_value + + db 2,'$$',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_FUNCTION,VAL_INTERNAL,0 + dd base_address_value + + db 2,'$@',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_FUNCTION,VAL_INTERNAL,0 + dd truncated_address_value + + db 2,'$%',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_FUNCTION,VAL_INTERNAL,0 + dd current_position_value + + db 3,'$%%',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_FUNCTION,VAL_INTERNAL,0 + dd truncated_position_value + + db 2,'%t',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_FUNCTION,VAL_INTERNAL,0 + dd current_time_value + + db 8,'__time__',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_FUNCTION,VAL_INTERNAL,0 + dd current_time_value + + db 10,'__source__',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_FUNCTION,VAL_INTERNAL,0 + dd main_file_name_value + + db 8,'__file__',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_FUNCTION,VAL_INTERNAL,0 + dd current_file_name_value + + db 8,'__line__',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_FUNCTION,VAL_INTERNAL,0 + dd current_line_number_value + + db 4,'byte',SYMCLASS_EXPRESSION,VALTYPE_PLAIN,VAL_INTERNAL,0 + dd SIZE_BYTE + + db 4,'word',SYMCLASS_EXPRESSION,VALTYPE_PLAIN,VAL_INTERNAL,0 + dd SIZE_WORD + + db 5,'dword',SYMCLASS_EXPRESSION,VALTYPE_PLAIN,VAL_INTERNAL,0 + dd SIZE_DWORD + + db 5,'pword',SYMCLASS_EXPRESSION,VALTYPE_PLAIN,VAL_INTERNAL,0 + dd SIZE_PWORD + + db 5,'fword',SYMCLASS_EXPRESSION,VALTYPE_PLAIN,VAL_INTERNAL,0 + dd SIZE_PWORD + + db 5,'qword',SYMCLASS_EXPRESSION,VALTYPE_PLAIN,VAL_INTERNAL,0 + dd SIZE_QWORD + + db 5,'tword',SYMCLASS_EXPRESSION,VALTYPE_PLAIN,VAL_INTERNAL,0 + dd SIZE_TWORD + + db 5,'tbyte',SYMCLASS_EXPRESSION,VALTYPE_PLAIN,VAL_INTERNAL,0 + dd SIZE_TWORD + + db 6,'dqword',SYMCLASS_EXPRESSION,VALTYPE_PLAIN,VAL_INTERNAL,0 + dd SIZE_DQWORD + + db 5,'xword',SYMCLASS_EXPRESSION,VALTYPE_PLAIN,VAL_INTERNAL,0 + dd SIZE_DQWORD + + db 6,'qqword',SYMCLASS_EXPRESSION,VALTYPE_PLAIN,VAL_INTERNAL,0 + dd SIZE_QQWORD + + db 5,'yword',SYMCLASS_EXPRESSION,VALTYPE_PLAIN,VAL_INTERNAL,0 + dd SIZE_QQWORD + + db 7,'dqqword',SYMCLASS_EXPRESSION,VALTYPE_PLAIN,VAL_INTERNAL,0 + dd SIZE_DQQWORD + + db 5,'zword',SYMCLASS_EXPRESSION,VALTYPE_PLAIN,VAL_INTERNAL,0 + dd SIZE_DQQWORD + + db 5,'label',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd label_directive + + db 7,'virtual',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd virtual_block + + db 4,'load',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd load_value + + db 5,'store',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd store_value + + db 2,'at',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_PREPOSITION,VAL_INTERNAL,0 + dd PREPOSITION_AT + + db 4,'from',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_PREPOSITION,VAL_INTERNAL,0 + dd PREPOSITION_FROM + + db 2,'as',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_PREPOSITION,VAL_INTERNAL,0 + dd PREPOSITION_AS + + db 3,'dup',SYMCLASS_EXPRESSION,VALTYPE_NATIVE_PREPOSITION,VAL_INTERNAL,0 + dd PREPOSITION_DUP + + db 0 + +db 3,'end' + + db 9,'namespace',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd end_namespace + + db 2,'if',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNCONDITIONAL,0 + dd end_conditional_block + + db 5,'match',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNCONDITIONAL,0 + dd end_match_block + + db 6,'rmatch',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNCONDITIONAL,0 + dd end_raw_match_block + + db 8,'rawmatch',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNCONDITIONAL,0 + dd end_raw_match_block + + db 5,'while',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNCONDITIONAL,0 + dd end_conditionally_repeated_block + + db 6,'repeat',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNCONDITIONAL,0 + dd end_repeated_block + + db 7,'iterate',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNCONDITIONAL,0 + dd end_iterator_block + + db 4,'rept',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNCONDITIONAL,0 + dd end_repeated_block + + db 3,'irp',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNCONDITIONAL,0 + dd end_iterator_block + + db 4,'irpv',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNCONDITIONAL,0 + dd end_variable_iterator_block + + db 8,'postpone',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNCONDITIONAL,0 + dd end_postponed_block + + db 5,'macro',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNCONDITIONAL,0 + dd end_macro + + db 5,'struc',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNCONDITIONAL,0 + dd end_struc + + db 7,'virtual',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd end_virtual_block + + db 0 + +db 4,'else' + + db 2,'if',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNCONDITIONAL,0 + dd else_conditional_block + + db 5,'match',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNCONDITIONAL,0 + dd else_match_block + + db 6,'rmatch',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNCONDITIONAL,0 + dd else_raw_match_block + + db 8,'rawmatch',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNCONDITIONAL,0 + dd else_raw_match_block + + db 0 + +db 6,'format' + + db 6,'binary',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd format_binary + + db 0 + +db 15,'calminstruction' + + db 5,'local',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd alm_local + + db 7,'arrange',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd alm_arrange + + db 5,'match',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd alm_match + + db 8,'assemble',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd alm_assemble + + db 9,'transform',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd alm_transform + + db 9,'stringify',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd alm_stringify + + db 7,'publish',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd alm_publish + + db 4,'take',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd alm_take + + db 7,'compute',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd alm_compute + + db 5,'check',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd alm_check + + db 4,'jyes',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd alm_jyes + + db 3,'jno',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd alm_jno + + db 4,'jump',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd alm_jump + + db 4,'call',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd alm_call + + db 4,'exit',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd alm_exit + + db 3,'end',SYMCLASS_INSTRUCTION,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL + VAL_UNCONDITIONAL,0 + dd alm_end + + db 15,'calminstruction',SYMCLASS_STRUCTURE,VALTYPE_NATIVE_COMMAND,VAL_INTERNAL,0 + dd alm_end + +db 0 + +zero_value dd 0 + dd 0 + +singular_value dd 1 + db 1 + dd 0 + +subtraction_operator dd EXPR_OPERATOR,calculate_sub + dd 0 + +area_metadata dd 2 + db '::' diff --git a/x86_64_sse2_x87/fasm/source/variables.inc b/x86_64_sse2_x87/fasm/source/variables.inc new file mode 100644 index 0000000..046425c --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/variables.inc @@ -0,0 +1,309 @@ + +characters db 256 dup ? + +maximum_number_of_errors dd ? +maximum_depth_of_stack dd ? + +variables: + + temporary_value dd 4 dup ? + accumulator dd 3 dup ? + + source_file dd ? + + include_paths dd ? + + storage_blocks dd ? + storage_free_space dd ? + storage_free_space_length dd ? + + tree_blocks dd ? + tree_reserve dd ? + tree_reserve_length dd ? + + value_definition_chain dd ? + retired_definition dd ? + + file_source_cache dd ? + memory_source_cache dd ? + file_data_cache dd ? + + operator_table dd ? + root_namespace dd ? + root_parameter_namespace dd ? + local_parameter_namespace dd ? + interceptor_symbol dd ? + label_interceptor_symbol dd ? + proxy_number dd ? + + source_context dd ? + source_context_maximum_length dd ? + current_pass dd ? + current_context RecognitionContext + local_namespace dd ? + parameter_namespace dd ? + + directives_stack dd ? + directives_stack_base dd ? + directives_stack_end dd ? + + current_counter dd ? + counters_stack_base dd ? + counters_stack_end dd ? + + first_error dd ? + + current_area dd ? + output_areas_list dd ? + output_areas_list_end dd ? + initial_output_area_entry dd ? + current_output_area_entry dd ? + prior_uninitialized_length dq ? + virtual_area dd ? + auxiliary_output_areas dd ? + + base_path dd ? + output_extension dd ? + output_extension_length dd ? + output_failures dd ? + + preprocessing_workspace Workspace + assembly_workspace Workspace + identifier_workspace Workspace + value_workspace Workspace + expression_workspace Workspace + calculation_workspace Workspace + auxiliary_workspace Workspace + + line_start dd ? + line_end dd ? + line_embeddings dd ? + line_embeddings_maximum_length dd ? + number_of_line_embeddings dd ? + preprocessed_context dd ? + line_context dd ? + embedded_context dd ? + number_of_enclosings dd ? + + recognition_context RecognitionContext + name_token dd ? + case_sensitive_hash dd ? + case_insensitive_hash dd ? + kept_symbol dd ? + current_symbol dd ? + tree_stack_base dd ? + tree_stack_end dd ? + symbol_root dd ? + symbol_branch dd ? + minor_identifier dd ? + + source_end dd ? + symbol_start dd ? + symbol_data dd ? + symbol_value_start dd ? + symbol_value_end dd ? + + interceptor dd ? + label_branch dd ? + label_leaf dd ? + label_interceptor dd ? + label_instruction_start dd ? + label_instruction_context dd ? + + display_buffer dd ? + display_buffer_length dd ? + display_data_length dd ? + + macro_buffer dd ? + macro_buffer_length dd ? + macro_leaf dd ? + macro_end_position dd ? + + constituent_symbol dd ? + constituent_value dd ? + constituent_whitespace dd ? + + alm_namespace dd ? + calm_code_buffer Workspace + calm_code_cursor dd ? + calm_literals_buffer Workspace + calm_literals_cursor dd ? + calm_auxiliary_buffer Workspace + calm_auxiliary_cursor dd ? + calm_line_number dd ? + calm_instruction_number dd ? + calm_value dd ? + calm_literals dd ? + calm_source_pointer dd ? + calm_rollback_offset dd ? + + value_position dd ? + kept_value dd ? + converter_position dd ? + expression_position dd ? + expression_end dd ? + operator_stack dd ? + operator_stack_position dd ? + operator_stack_base dd ? + operator_stack_end dd ? + calculation_position dd ? + source_term dd ? + destination_term dd ? + temporary_terms dd ? + free_temporary_terms dd ? + temporary_floats dd ? + iterations dd ? + exponent dd ? + multiplier dd ? + divisor dd ? + long_dividend_term dd ? + long_divisor_term dd ? + long_divisor_length dd ? + division_temporary_terms dd ? + mantissa_tail dd ? + predicted_shift dd ? + + condition_stack dd ? + condition_stack_base dd ? + condition_stack_end dd ? + initial_parentheses dd ? + comparator dd ? + outer_expression dd ? + + assembly_stack_base dd ? + assembly_stack_end dd ? + previous_symbol_end dd ? + further_whitespace dd ? + zero_digits dd ? + decimal_places dd ? + literal_exponent dd ? + update_function dd ? + argument_start dd ? + macro_parameters_context dd ? + new_local_namespace dd ? + file_name dd ? + string_end dd ? + message_end dd ? + data_area_symbol dd ? + data_area dd ? + data_offset dd ? + file_offset dq ? + data_length dd ? + local_path dd ? + + error_line_start dd ? + error_line_end dd ? + error_symbol dd ? + last_source_entry dd ? + last_file_source_entry dd ? + preprocessed_text_end dd ? + + source_text dd ? + source_text_length dd ? + tokenization_buffer dd ? + tokenization_buffer_length dd ? + buffer_end_offset dd ? + file_data dd ? + file_cache_pointer dd ? + + address_length dd ? + data_unit_length dd ? + uninitialized_length dq ? + + directive_block dd ? + number_of_iterations dd ? + number_of_parameters dd ? + number_of_values dd ? + parameter_index dd ? + value_index dd ? + value dd ? + value_length dd ? + metadata_length dd ? + sequence_header_cursor dd ? + sequence_header_length dd ? + expression_sequence_cursor dd ? + expression_sequence_end dd ? + context_boundary dd ? + whitespace_boundary dd ? + transforming_namespace dd ? + + pattern_start dd ? + pattern_end dd ? + matched_context dd ? + substitutions_end dd ? + stored_position dd ? + stored_substitution dd ? + stored_context dd ? + stored_pattern dd ? + brackets dd ? + + instruction_value dd ? + instruction_branch dd ? + instruction_body dd ? + + number_of_lines dd ? + + preprocessing_mode db ? + assembly_mode db ? + next_pass_needed db ? + shift_tracking db ? + + name_kind db ? + name_volatile db ? + symbol_class db ? + symbol_required db ? + symbol_expected db ? + kept_symbol_flags db ? + chosen_class db ? + expected_class db ? + symbol_definition db ? + symbol_solid db ? + recognizer_setting db ? + label_solid db ? + value_type db ? + waiting_for_digit db ? + literal_exponent_sign db ? + literal_fractional_part db ? + float_literal_status db ? + use_raw_values db ? + + last_token db ? + breakpoint_token db ? + current_constituent db ? + kept_value_in_workspace db ? + leave_opening_parentheses db ? + operator_argument_expected db ? + term_type db ? + bit_shift db ? + shift_overflow db ? + multiplier_sign db ? + high_bits_count db ? + first_sign db ? + second_sign db ? + comparator_priority db ? + result_type db ? + defined_element db ? + + source_context_affected db ? + raw_value db ? + substitution_active db ? + stored_substitution_activity db ? + unprocessed_matching db ? + whitespace_matching db ? + macro_definition_active db ? + macro_flags db ? + macro_greedy db ? + calm_definition_active db ? + + size_specified db ? + + message_volatile db ? + displayed_byte db ? + + alm_statement db ? + calm_result db ? + hidden_context db ? + +variables_end: + + trace_mode db ? diff --git a/x86_64_sse2_x87/fasm/source/version.inc b/x86_64_sse2_x87/fasm/source/version.inc new file mode 100644 index 0000000..c4818fe --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/version.inc @@ -0,0 +1 @@ +VERSION equ "jg8x" \ No newline at end of file diff --git a/x86_64_sse2_x87/fasm/source/windows/dll/fasmg.asm b/x86_64_sse2_x87/fasm/source/windows/dll/fasmg.asm new file mode 100644 index 0000000..1d8328f --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/windows/dll/fasmg.asm @@ -0,0 +1,254 @@ + +match ,{ + + include 'win32a.inc' + include 'localptr.inc' + +} match -,{ +else + + include 'selfhost.inc' + +end match +_ equ } + + format PE large NX DLL + entry DllEntryPoint + +include '../../version.inc' + +struct MEMORY_REGION + address dd ? + size dd ? +ends + +section '.text' code executable + + include '../../assembler.inc' + include '../../symbols.inc' + include '../../expressions.inc' + include '../../conditions.inc' + include '../../floats.inc' + include '../../directives.inc' + include '../../calm.inc' + include '../../errors.inc' + include '../../map.inc' + include '../../reader.inc' + include '../../output.inc' + include '../../console.inc' + + DllEntryPoint: + mov eax,1 + retn 12 + + fasmg_GetVersion: + mov eax,version_string + retn + + fasmg_Assemble: + + virtual at ebp - LOCAL_VARIABLES_SIZE + + LocalVariables: + + include '../../variables.inc' + + maximum_number_of_passes dd ? + + timestamp dq ? + systemtime SYSTEMTIME + filetime FILETIME + + memory dd ? + systmp dd ? + + rb (LocalVariables - $) and 11b + + LOCAL_VARIABLES_SIZE = $ - LocalVariables + + assert $ - ebp = 0 + + previous_frame dd ? + stored_edi dd ? + stored_esi dd ? + stored_ebx dd ? + return_address dd ? + + FunctionParameters: + + source_string dd ? + source_path dd ? + output_region dd ? + output_path dd ? + stdout dd ? + stderr dd ? + + FUNCTION_PARAMETERS_SIZE = $ - FunctionParameters + + end virtual + + push ebx esi edi + enter LOCAL_VARIABLES_SIZE,0 + + call system_init + + mov [maximum_number_of_passes],100 + mov [maximum_number_of_errors],1000 + mov [maximum_depth_of_stack],10000 + + xor al,al + call assembly_init + + assemble: + mov esi,[source_string] + mov edx,[source_path] + call assembly_pass + jc assembly_done + + mov eax,[current_pass] + cmp eax,[maximum_number_of_passes] + jb assemble + + call show_display_data + call assembly_shutdown + call system_shutdown + mov eax,-2 + leave + pop edi esi ebx + retn FUNCTION_PARAMETERS_SIZE + + assembly_done: + + call show_display_data + + cmp [first_error],0 + jne assembly_failed + + mov esi,[output_region] + test esi,esi + jz output_copied + call get_output_length + test edx,edx + jnz out_of_memory + mov [value_length],eax + xchg eax,[esi+MEMORY_REGION.size] + cmp [esi+MEMORY_REGION.address],0 + je new_region_for_output + cmp eax,[value_length] + jae copy_output + invoke VirtualAlloc,[esi+MEMORY_REGION.address],[esi+MEMORY_REGION.size],MEM_COMMIT,PAGE_READWRITE + test eax,eax + jnz copy_output + invoke VirtualFree,[esi+MEMORY_REGION.address],0,MEM_RELEASE + new_region_for_output: + invoke VirtualAlloc,0,[esi+MEMORY_REGION.size],MEM_COMMIT,PAGE_READWRITE + test eax,eax + jz out_of_memory + mov [esi+MEMORY_REGION.address],eax + copy_output: + mov edi,[esi+MEMORY_REGION.address] + xor eax,eax + mov dword [file_offset],eax + mov dword [file_offset+4],eax + call read_from_output + output_copied: + + mov ebx,[source_path] + mov edi,[output_path] + mov eax,ebx + or eax,edi + jz output_written + call write_output_file + jc write_failed + output_written: + + call assembly_shutdown + call system_shutdown + xor eax,eax + leave + pop edi esi ebx + retn FUNCTION_PARAMETERS_SIZE + + assembly_failed: + mov eax,[first_error] + xor ecx,ecx + count_errors: + inc ecx + mov eax,[eax+Error.next] + test eax,eax + jnz count_errors + push ecx + call show_errors + call assembly_shutdown + call system_shutdown + pop eax + leave + pop edi esi ebx + retn FUNCTION_PARAMETERS_SIZE + + write_failed: + call assembly_shutdown + call system_shutdown + mov eax,-3 + leave + pop edi esi ebx + retn FUNCTION_PARAMETERS_SIZE + + out_of_memory: + call assembly_shutdown + call system_shutdown + mov eax,-1 + leave + pop edi esi ebx + retn FUNCTION_PARAMETERS_SIZE + + include 'system.inc' + +section '.rdata' data readable + +data import + + library kernel32,'KERNEL32.DLL' + + import kernel32,\ + CloseHandle,'CloseHandle',\ + CreateFile,'CreateFileA',\ + ExitProcess,'ExitProcess',\ + GetCommandLine,'GetCommandLineA',\ + GetEnvironmentVariable,'GetEnvironmentVariableA',\ + GetStdHandle,'GetStdHandle',\ + GetSystemTime,'GetSystemTime',\ + GetTickCount,'GetTickCount',\ + HeapAlloc,'HeapAlloc',\ + HeapCreate,'HeapCreate',\ + HeapDestroy,'HeapDestroy',\ + HeapFree,'HeapFree',\ + HeapReAlloc,'HeapReAlloc',\ + HeapSize,'HeapSize',\ + VirtualAlloc,'VirtualAlloc',\ + VirtualFree,'VirtualFree',\ + ReadFile,'ReadFile',\ + SetFilePointer,'SetFilePointer',\ + SystemTimeToFileTime,'SystemTimeToFileTime',\ + WriteFile,'WriteFile',\ + GetLastError,'GetLastError' + +end data + +align 4 + +data export + + export 'FASMG.DLL',\ + fasmg_GetVersion,'fasmg_GetVersion',\ + fasmg_Assemble,'fasmg_Assemble' + +end data + + include '../../tables.inc' + include '../../messages.inc' + + version_string db VERSION,0 + +section '.reloc' fixups data readable discardable + \ No newline at end of file diff --git a/x86_64_sse2_x87/fasm/source/windows/dll/localptr.inc b/x86_64_sse2_x87/fasm/source/windows/dll/localptr.inc new file mode 100644 index 0000000..54cb19e --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/windows/dll/localptr.inc @@ -0,0 +1,28 @@ + +macro pushd arg +{ + if arg eqtype +ebp & arg relativeto ebp + if arg - ebp + push eax + lea eax,[arg] + xchg eax,[esp] + else + push ebp + end if + else if ~ arg eq + pushd arg + end if +} + +macro mov dest,src +{ + if src eqtype +ebp & src relativeto ebp + if src - ebp + lea dest,[src] + else + mov dest,ebp + end if + else + mov dest,src + end if +} diff --git a/x86_64_sse2_x87/fasm/source/windows/dll/selfhost.inc b/x86_64_sse2_x87/fasm/source/windows/dll/selfhost.inc new file mode 100644 index 0000000..d21f188 --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/windows/dll/selfhost.inc @@ -0,0 +1,266 @@ + +include '../../../examples/x86/include/80386.inc' + +macro format?.PE? settings + PE.Settings.Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE or IMAGE_FILE_32BIT_MACHINE or IMAGE_FILE_LINE_NUMS_STRIPPED or IMAGE_FILE_LOCAL_SYMS_STRIPPED + PE.Settings.DllCharacteristics = 0 + PE.Settings.Stamp = +VERSION + PE.Settings.LegacyHeaders = 0 + local seq + define seq settings: + while 1 + match :, seq + break + else match =DLL? more, seq + PE.Settings.Characteristics = PE.Settings.Characteristics or IMAGE_FILE_DLL + redefine seq more + else match =large? more, seq + PE.Settings.Characteristics = PE.Settings.Characteristics or IMAGE_FILE_LARGE_ADDRESS_AWARE + redefine seq more + else match =WDM? more, seq + PE.Settings.DllCharacteristics = PE.Settings.DllCharacteristics or IMAGE_DLLCHARACTERISTICS_WDM_DRIVER + redefine seq more + else match =NX? more, seq + PE.Settings.DllCharacteristics = PE.Settings.DllCharacteristics or IMAGE_DLLCHARACTERISTICS_NX_COMPAT + redefine seq more + else match =at? base =on? stub :, seq + PE.Settings.ImageBase = base + PE.Settings.Stub = stub + break + else match =at? base :, seq + PE.Settings.ImageBase = base + break + else match =on? stub :, seq + PE.Settings.Stub = stub + break + else + match =GUI? more, seq + PE.Settings.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI + redefine seq more + else match =console? more, seq + PE.Settings.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI + redefine seq more + else match =native? more, seq + PE.Settings.Subsystem = IMAGE_SUBSYSTEM_NATIVE + PE.Settings.SectionAlignment = 32 + PE.Settings.FileAlignment = 32 + redefine seq more + else match =EFI? more, seq + PE.Settings.Magic = 0x20B + PE.Settings.Subsystem = IMAGE_SUBSYSTEM_EFI_APPLICATION + redefine seq more + else match =EFIboot? more, seq + PE.Settings.Magic = 0x20B + PE.Settings.Subsystem = IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER + redefine seq more + else match =EFIruntime? more, seq + PE.Settings.Magic = 0x20B + PE.Settings.Subsystem = IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER + redefine seq more + else + err 'invalid argument' + break + end match + match V.v more, seq + PE.Settings.MajorSubsystemVersion = V + PE.Settings.MinorSubsystemVersion = v + redefine seq more + end match + end match + end while + if PE.Settings.Characteristics and IMAGE_FILE_DLL + format binary as 'dll' + else + format binary as 'exe' + end if + include '../../../examples/x86/include/format/pe.inc' + use32 +end macro + +macro struct? name + macro ends?! + end namespace + esc end struc + virtual at 0 + name name + sizeof.name = $ + end virtual + purge ends? + end macro + esc struc name + label . : sizeof.name + namespace . +end macro + +calminstruction invoke? proc*,args& + local tmp, tmpst, stack + match , args + jyes go + collect: + match tmpst=,args, args + take stack, tmpst + jyes collect + push: + match tmp], args + jyes regular + check args relativeto ebp & args - ebp + jno regular + arrange tmp, =push =eax + assemble tmp + arrange tmp, =lea =eax,[args] + assemble tmp + arrange tmp, =xchg =eax,[=esp] + assemble tmp + jump next + regular: + arrange tmp, =pushd args + assemble tmp + next: + take args, stack + jyes push + go: + arrange tmp, =call [proc] + assemble tmp +end calminstruction + +calminstruction mov? dest*,src* + local tmp + match tmp], src + jyes regular + check src relativeto ebp & src - ebp + jno regular + arrange tmp, =lea dest,[src] + assemble tmp + exit + regular: + arrange tmp, =mov dest,src + assemble tmp +end calminstruction + +macro library? definitions& + PE.Imports: + iterate , definitions + if ~ name.redundant + dd RVA name.lookup,0,0,RVA name.str,RVA name.address + end if + name.referred = 1 + end iterate + dd 0,0,0,0,0 + iterate , definitions + if ~ name.redundant + name.str db string,0 + align 2 + end if + end iterate +end macro + +macro import? name,definitions& + align 4 + if defined name.referred + name.lookup: + iterate , definitions + if used label + if string eqtype '' + dd RVA name.label + else + dd 80000000h + string + end if + end if + end iterate + if $ > name.lookup + name.redundant = 0 + dd 0 + else + name.redundant = 1 + end if + name.address: + iterate , definitions + if used label + if string eqtype '' + label dd RVA name.label + else + label dd 80000000h + string + end if + end if + end iterate + if ~ name.redundant + dd 0 + end if + iterate , definitions + if used label & string eqtype '' + name.label dw 0 + db string,0 + align 2 + end if + end iterate + end if +end macro + +macro export dllname,exports& + iterate , exports + + local module,addresses,names,ordinal,count + count = %% + dd 0,0,0,RVA module,1 + dd count,count,RVA addresses,RVA names,RVA ordinal + addresses: + repeat count + indx % + dd RVA label + end repeat + names: + repeat count + dd RVA names.name#% + end repeat + ordinal: + repeat count + dw %-1 + end repeat + module db dllname,0 + repeat count + indx % + names.name#% db string,0 + end repeat + + local x,y,z,str1,str2,v1,v2 + x = count shr 1 + while x > 0 + y = x + while y < count + z = y + while z-x >= 0 + load v1:dword from names+z*4 + str1 = ($-(RVA $))+v1 + load v2:dword from names+(z-x)*4 + str2 = ($-(RVA $))+v2 + while v1 > 0 + load v1:byte from str1+%-1 + load v2:byte from str2+%-1 + if v1 <> v2 + break + end if + end while + if v1 < v2 + load v1:dword from names+z*4 + load v2:dword from names+(z-x)*4 + store v1:dword at names+(z-x)*4 + store v2:dword at names+z*4 + load v1:word from ordinal+z*2 + load v2:word from ordinal+(z-x)*2 + store v1:word at ordinal+(z-x)*2 + store v2:word at ordinal+z*2 + else + break + end if + z = z-x + end while + y = y+1 + end while + x = x shr 1 + end while + + break + end iterate +end macro + +include '../kernel32.inc' diff --git a/x86_64_sse2_x87/fasm/source/windows/dll/system.inc b/x86_64_sse2_x87/fasm/source/windows/dll/system.inc new file mode 100644 index 0000000..463ea64 --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/windows/dll/system.inc @@ -0,0 +1,213 @@ + +LINE_FEED equ 13,10 + +system_init: + invoke HeapCreate,0,20000h,0 + mov [memory],eax + test eax,eax + jz out_of_memory + invoke GetSystemTime,systemtime + invoke SystemTimeToFileTime,systemtime,filetime + mov ebx,[filetime.dwLowDateTime] + mov eax,[filetime.dwHighDateTime] + sub ebx,116444736000000000 and 0FFFFFFFFh + sbb eax,116444736000000000 shr 32 + xor edx,edx + mov ecx,10000000 + div ecx + mov dword [timestamp+4],eax + mov eax,ebx + div ecx + mov dword [timestamp],eax + retn + +system_shutdown: + cmp [memory],0 + je memory_released + invoke HeapDestroy,[memory] + memory_released: + retn + +malloc: +malloc_fixed: +malloc_growable: +; in: ecx = requested size +; out: eax - allocated block, ecx = allocated size, on error jumps to out_of_memory (does not return) +; preserves: ebx, esi, edi +; note: +; use of malloc_fixed hints that block will be kept as is until the end of assembly +; use of malloc_growable hints that block is likely to be resized + invoke HeapAlloc,[memory],0,ecx + test eax,eax + jz out_of_memory + memory_allocated: + push eax + invoke HeapSize,[memory],0,eax + mov ecx,eax + pop eax + cmp ecx,-1 + je out_of_memory + retn +realloc: +; in: eax - memory block, ecx = requested size +; out: eax - resized block, ecx = allocated size, on error jumps to out_of_memory (does not return) +; preserves: ebx, esi, edi + invoke HeapReAlloc,[memory],0,eax,ecx + test eax,eax + jnz memory_allocated + jmp out_of_memory +mfree: +; in: eax - memory block +; out: cf set on error +; preserves: ebx, esi, edi +; note: eax may have value 0 or -1, it should be treated as invalid input then + test eax,eax + jz interface_error + cmp eax,-1 + je interface_error + invoke HeapFree,[memory],0,eax + test eax,eax + jz interface_error + clc + retn + interface_error: + stc + retn + +open: +; in: edx - path to file +; out: ebx = file handle, cf set on error +; preserves: esi, edi + invoke CreateFile,edx,GENERIC_READ,FILE_SHARE_READ,0,OPEN_EXISTING,0,0 + cmp eax,-1 + je interface_error + mov ebx,eax + clc + retn +create: +; in: edx - path to file +; out: ebx = file handle, cf set on error +; preserves: esi, edi + invoke CreateFile,edx,GENERIC_WRITE,0,0,CREATE_ALWAYS,0,0 + cmp eax,-1 + je interface_error + mov ebx,eax + clc + retn +write: +; in: ebx = file handle, edx - data, ecx = number of bytes +; out: cf set on error +; preserves: ebx, esi, edi + push ecx + invoke WriteFile,ebx,edx,ecx,systmp,0 + pop ecx + test eax,eax + jz interface_error + cmp ecx,[systmp] + jne interface_error + clc + retn +read: +; in: ebx = file handle, edx - buffer, ecx = number of bytes +; out: cf set on error +; preserves: ebx, esi, edi + push ecx + invoke ReadFile,ebx,edx,ecx,systmp,0 + pop ecx + test eax,eax + jz interface_error + cmp ecx,[systmp] + jne interface_error + clc + retn +close: +; in: ebx = file handle +; preserves: ebx, esi, edi + invoke CloseHandle,ebx + retn +lseek: +; in: ebx = file handle, cl = method, edx:eax = offset +; out: edx:eax = new offset from the beginning of file, cf set on error +; preserves: ebx, esi, edi + movzx ecx,cl + mov [systmp],edx + invoke SetFilePointer,ebx,eax,systmp,ecx + cmp eax,-1 + jne lseek_ok + invoke GetLastError + test eax,eax + jnz interface_error + not eax + lseek_ok: + mov edx,[systmp] + clc + retn + +get_timestamp: +; out: edx:eax = timestamp +; preserves: ebx, ecx, esi, edi + mov eax,dword [timestamp] + mov edx,dword [timestamp+4] + retn + +display_string: +; in: +; esi - string +; ecx = string length, zero for ASCIIZ string +; preserves: ebx, esi + push ebx + mov ebx,[stdout] + jmp write_string +display_error_string: +; in: +; esi - string +; ecx = string length, zero for ASCIIZ string +; preserves: ebx, esi + push ebx + mov ebx,[stderr] + write_string: + test ebx,ebx + jz hidden_display + test ecx,ecx + jnz write_string_to_stdout + xor al,al + mov edi,esi + or ecx,-1 + repne scasb + neg ecx + sub ecx,2 + write_string_to_stdout: + mov edx,esi + write_portion_to_stdout: + mov eax,51200 + cmp ecx,eax + jbe final_write_to_stdout + sub ecx,eax + add eax,edx + push eax ecx + invoke WriteFile,ebx,edx,51200,systmp,0 + pop ecx edx + jmp write_portion_to_stdout + final_write_to_stdout: + invoke WriteFile,ebx,edx,ecx,systmp,0 + hidden_display: + pop ebx + retn + +get_environment_variable: +; in: +; esi - name +; edi - buffer for value +; ecx = size of buffer +; out: +; eax = length of value +; preserves: ebx, esi, edi + push ecx + invoke GetEnvironmentVariable,esi,edi,ecx + pop ecx + cmp eax,ecx + jae environment_variable_ready + mov byte [edi+eax],0 + environment_variable_ready: + inc eax + retn diff --git a/x86_64_sse2_x87/fasm/source/windows/fasmg.asm b/x86_64_sse2_x87/fasm/source/windows/fasmg.asm new file mode 100644 index 0000000..7b0652c --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/windows/fasmg.asm @@ -0,0 +1,529 @@ + +match ,{ + + include 'win32a.inc' + +} match -,{ +else + + include 'selfhost.inc' + +end match +_ equ } + + + format PE large NX console 4.0 + entry start + +include '../version.inc' + +section '.text' code executable + + include 'system.inc' + + start: + + call system_init + + call get_arguments + mov bl,al + cmp [no_logo],0 + jne logo_ok + mov esi,_logo + xor ecx,ecx + call display_string + logo_ok: + test bl,bl + jnz display_usage_information + + xor al,al + movzx ecx,[verbosity_level] + jecxz init + or al,TRACE_ERROR_STACK + dec ecx + jz init + or al,TRACE_DISPLAY + init: + call assembly_init + + invoke GetTickCount + mov [timer],eax + + assemble: + mov esi,[initial_commands] + mov edx,[source_path] + call assembly_pass + jc assembly_done + + mov eax,[current_pass] + cmp eax,[maximum_number_of_passes] + jb assemble + + call show_display_data + + mov esi,_error_prefix + xor ecx,ecx + call display_error_string + mov esi,_code_cannot_be_generated + xor ecx,ecx + call display_error_string + mov esi,_message_suffix + xor ecx,ecx + call display_error_string + + jmp assembly_failed + + assembly_done: + + call show_display_data + + cmp [first_error],0 + jne assembly_failed + + cmp [no_logo],0 + jne summary_done + mov eax,[current_pass] + xor edx,edx + call itoa + call display_string + mov esi,_passes + cmp [current_pass],1 + jne display_passes_suffix + mov esi,_pass + display_passes_suffix: + xor ecx,ecx + call display_string + invoke GetTickCount + sub eax,[timer] + xor edx,edx + add eax,50 + mov ecx,1000 + div ecx + mov ebx,eax + mov eax,edx + xor edx,edx + mov ecx,100 + div ecx + mov [timer],eax + xchg eax,ebx + or ebx,eax + jz display_output_length + xor edx,edx + call itoa + call display_string + mov esi,_message_suffix + mov ecx,1 + call display_string + mov eax,[timer] + xor edx,edx + call itoa + call display_string + mov esi,_seconds + xor ecx,ecx + call display_string + display_output_length: + call get_output_length + push eax edx + call itoa + call display_string + pop edx eax + mov esi,_bytes + cmp eax,1 + jne display_bytes_suffix + test edx,edx + jnz display_bytes_suffix + mov esi,_byte + display_bytes_suffix: + xor ecx,ecx + call display_string + mov esi,_new_line + xor ecx,ecx + call display_string + summary_done: + + mov ebx,[source_path] + mov edi,[output_path] + call write_output_file + jc write_failed + + call assembly_shutdown + call system_shutdown + + invoke ExitProcess,0 + + assembly_failed: + + call show_errors + + call assembly_shutdown + call system_shutdown + + invoke ExitProcess,2 + + write_failed: + mov ebx,_write_failed + jmp fatal_error + + out_of_memory: + mov ebx,_out_of_memory + jmp fatal_error + + fatal_error: + + mov esi,_error_prefix + xor ecx,ecx + call display_error_string + mov esi,ebx + xor ecx,ecx + call display_error_string + mov esi,_message_suffix + xor ecx,ecx + call display_error_string + + call assembly_shutdown + call system_shutdown + + invoke ExitProcess,3 + + display_usage_information: + + mov esi,_usage + xor ecx,ecx + call display_string + + call system_shutdown + + invoke ExitProcess,1 + + get_arguments: + xor eax,eax + mov [initial_commands],eax + mov [source_path],eax + mov [output_path],eax + mov [no_logo],al + mov [verbosity_level],al + mov [maximum_number_of_passes],100 + mov [maximum_number_of_errors],1 + mov [maximum_depth_of_stack],10000 + invoke GetCommandLine + mov esi,eax + mov edi,eax + or ecx,-1 + xor al,al + repne scasb + sub edi,esi + mov ecx,edi + call malloc + mov edi,eax + get_argument: + xor ah,ah + read_character: + lodsb + test al,al + jz no_more_arguments + cmp al,22h + je switch_quote + cmp ax,20h + je end_argument + stosb + jmp read_character + end_argument: + xor al,al + stosb + find_next_argument: + mov al,[esi] + test al,al + jz no_more_arguments + cmp al,20h + jne next_argument_found + inc esi + jmp find_next_argument + switch_quote: + xor ah,1 + jmp read_character + next_argument_found: + cmp al,'-' + je get_option + cmp al,'/' + je get_option + cmp [source_path],0 + je get_source_path + cmp [output_path],0 + je get_output_path + error_in_arguments: + or al,-1 + retn + get_source_path: + mov [source_path],edi + jmp get_argument + get_output_path: + mov [output_path],edi + jmp get_argument + no_more_arguments: + cmp [source_path],0 + je error_in_arguments + xor al,al + stosb + retn + get_option: + inc esi + lodsb + cmp al,'e' + je set_errors_limit + cmp al,'E' + je set_errors_limit + cmp al,'i' + je insert_initial_command + cmp al,'I' + je insert_initial_command + cmp al,'p' + je set_passes_limit + cmp al,'P' + je set_passes_limit + cmp al,'r' + je set_recursion_limit + cmp al,'R' + je set_recursion_limit + cmp al,'v' + je set_verbose_mode + cmp al,'V' + je set_verbose_mode + cmp al,'n' + je set_no_logo + cmp al,'N' + jne error_in_arguments + set_no_logo: + or [no_logo],-1 + mov al,[esi] + cmp al,20h + je find_next_argument + test al,al + jnz error_in_arguments + jmp find_next_argument + set_verbose_mode: + call get_option_value + jc error_in_arguments + cmp edx,2 + ja error_in_arguments + mov [verbosity_level],dl + jmp find_next_argument + set_errors_limit: + call get_option_value + jc error_in_arguments + test edx,edx + jz error_in_arguments + mov [maximum_number_of_errors],edx + jmp find_next_argument + set_recursion_limit: + call get_option_value + jc error_in_arguments + test edx,edx + jz error_in_arguments + mov [maximum_depth_of_stack],edx + jmp find_next_argument + set_passes_limit: + call get_option_value + jc error_in_arguments + test edx,edx + jz error_in_arguments + mov [maximum_number_of_passes],edx + jmp find_next_argument + get_option_value: + xor eax,eax + mov edx,eax + find_option_value: + cmp byte [esi],20h + jne get_option_digit + inc esi + jmp find_option_value + get_option_digit: + lodsb + cmp al,20h + je option_value_ok + test al,al + jz option_value_ok + sub al,30h + jc invalid_option_value + cmp al,9 + ja invalid_option_value + imul edx,10 + jo invalid_option_value + add edx,eax + jc invalid_option_value + jmp get_option_digit + option_value_ok: + dec esi + clc + ret + invalid_option_value: + stc + ret + insert_initial_command: + push edi + find_command_segment: + cmp byte [esi],20h + jne command_segment_found + inc esi + jmp find_command_segment + command_segment_found: + xor ah,ah + cmp byte [esi],22h + jne measure_command_segment + inc esi + inc ah + measure_command_segment: + mov ebx,esi + scan_command_segment: + mov ecx,esi + mov al,[esi] + test al,al + jz command_segment_measured + cmp ax,20h + je command_segment_measured + cmp ax,22h + je command_segment_measured + inc esi + cmp al,22h + jne scan_command_segment + command_segment_measured: + sub ecx,ebx + mov edi,[initial_commands] + lea eax,[ecx+2] + test edi,edi + jz allocate_initial_commands_buffer + mov edx,[initial_commands_length] + add edi,edx + add eax,edx + cmp eax,[initial_commands_maximum_length] + ja grow_initial_commands_buffer + copy_initial_command: + xchg esi,ebx + rep movsb + mov esi,ebx + sub edi,[initial_commands] + mov [initial_commands_length],edi + mov al,[esi] + test al,al + jz initial_command_ready + cmp al,20h + jne command_segment_found + initial_command_ready: + mov edi,[initial_commands] + add edi,[initial_commands_length] + mov ax,0Ah + stosw + inc [initial_commands_length] + pop edi + jmp find_next_argument + allocate_initial_commands_buffer: + push ecx + mov ecx,eax + call malloc + mov [initial_commands],eax + mov [initial_commands_maximum_length],ecx + mov edi,eax + pop ecx + jmp copy_initial_command + grow_initial_commands_buffer: + push ecx + mov ecx,eax + mov eax,[initial_commands] + call realloc + mov [initial_commands],eax + mov [initial_commands_maximum_length],ecx + mov edi,eax + add edi,[initial_commands_length] + pop ecx + jmp copy_initial_command + + include '../symbols.inc' + include '../assembler.inc' + include '../expressions.inc' + include '../conditions.inc' + include '../floats.inc' + include '../directives.inc' + include '../calm.inc' + include '../errors.inc' + include '../map.inc' + include '../reader.inc' + include '../output.inc' + include '../console.inc' + +section '.bss' readable writeable + + include '../variables.inc' + + source_path dd ? + output_path dd ? + maximum_number_of_passes dd ? + + initial_commands dd ? + initial_commands_length dd ? + initial_commands_maximum_length dd ? + + stdout dd ? + stderr dd ? + memory dd ? + timestamp dq ? + systemtime SYSTEMTIME + filetime FILETIME + systmp dd ? + + timer dd ? + verbosity_level db ? + no_logo db ? + +section '.rdata' data readable + +data import + + library kernel32,'KERNEL32.DLL' + + import kernel32,\ + CloseHandle,'CloseHandle',\ + CreateFile,'CreateFileA',\ + ExitProcess,'ExitProcess',\ + GetCommandLine,'GetCommandLineA',\ + GetEnvironmentVariable,'GetEnvironmentVariableA',\ + GetStdHandle,'GetStdHandle',\ + GetSystemTime,'GetSystemTime',\ + GetTickCount,'GetTickCount',\ + HeapAlloc,'HeapAlloc',\ + HeapCreate,'HeapCreate',\ + HeapDestroy,'HeapDestroy',\ + HeapFree,'HeapFree',\ + HeapReAlloc,'HeapReAlloc',\ + HeapSize,'HeapSize',\ + ReadFile,'ReadFile',\ + SetFilePointer,'SetFilePointer',\ + SystemTimeToFileTime,'SystemTimeToFileTime',\ + WriteFile,'WriteFile',\ + GetLastError,'GetLastError' + +end data + + _logo db 'flat assembler version g.',VERSION,13,10,0 + + _usage db 'Usage: fasmg source [output]',13,10 + db 'Optional settings:',13,10 + db ' -e limit Set the maximum number of displayed errors (default 1)',13,10 + db ' -p limit Set the maximum allowed number of passes (default 100)',13,10 + db ' -r limit Set the maximum depth of the stack (default 10000)',13,10 + db ' -v flag Enable or disable showing all lines from the stack (default 0)',13,10 + db ' -i command Insert instruction at the beginning of source',13,10 + db ' -n Do not show logo nor summary',13,10 + db 0 + + _pass db ' pass, ',0 + _passes db ' passes, ',0 + _dot db '.' + _seconds db ' seconds, ',0 + _byte db ' byte.',0 + _bytes db ' bytes.',0 + + _write_failed db 'failed to write the output file',0 + _out_of_memory db 'not enough memory to complete the assembly',0 + _code_cannot_be_generated db 'could not generate code within the allowed number of passes',0 + + include '../tables.inc' + include '../messages.inc' diff --git a/x86_64_sse2_x87/fasm/source/windows/kernel32.inc b/x86_64_sse2_x87/fasm/source/windows/kernel32.inc new file mode 100644 index 0000000..67be33a --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/windows/kernel32.inc @@ -0,0 +1,96 @@ + +struct FILETIME + dwLowDateTime dd ? + dwHighDateTime dd ? +ends + +struct SYSTEMTIME + wYear dw ? + wMonth dw ? + wDayOfWeek dw ? + wDay dw ? + wHour dw ? + wMinute dw ? + wSecond dw ? + wMilliseconds dw ? +ends + +; Page access flags + +PAGE_NOACCESS = 001h +PAGE_READONLY = 002h +PAGE_READWRITE = 004h +PAGE_WRITECOPY = 008h +PAGE_EXECUTE = 010h +PAGE_EXECUTE_READ = 020h +PAGE_EXECUTE_READWRITE = 040h +PAGE_EXECUTE_WRITECOPY = 080h +PAGE_GUARD = 100h +PAGE_NOCACHE = 200h + +; Memory allocation flags + +MEM_COMMIT = 001000h +MEM_RESERVE = 002000h +MEM_DECOMMIT = 004000h +MEM_RELEASE = 008000h +MEM_FREE = 010000h +MEM_PRIVATE = 020000h +MEM_MAPPED = 040000h +MEM_RESET = 080000h +MEM_TOP_DOWN = 100000h + +; Heap allocation flags + +HEAP_NO_SERIALIZE = 1 +HEAP_GENERATE_EXCEPTIONS = 4 +HEAP_ZERO_MEMORY = 8 + +; Device handles + +INVALID_HANDLE_VALUE = -1 +STD_INPUT_HANDLE = -10 +STD_OUTPUT_HANDLE = -11 +STD_ERROR_HANDLE = -12 + +; Access rights + +DELETE_RIGHT = 00010000h +READ_CONTROL = 00020000h +WRITE_DAC = 00040000h +WRITE_OWNER = 00080000h +SYNCHRONIZE = 00100000h +STANDARD_RIGHTS_READ = READ_CONTROL +STANDARD_RIGHTS_WRITE = READ_CONTROL +STANDARD_RIGHTS_EXECUTE = READ_CONTROL +STANDARD_RIGHTS_REQUIRED = 000F0000h +STANDARD_RIGHTS_ALL = 001F0000h +SPECIFIC_RIGHTS_ALL = 0000FFFFh +ACCESS_SYSTEM_SECURITY = 01000000h +MAXIMUM_ALLOWED = 02000000h +GENERIC_READ = 80000000h +GENERIC_WRITE = 40000000h +GENERIC_EXECUTE = 20000000h +GENERIC_ALL = 10000000h +PROCESS_TERMINATE = 00000001h +PROCESS_CREATE_THREAD = 00000002h +PROCESS_VM_OPERATION = 00000008h +PROCESS_VM_READ = 00000010h +PROCESS_VM_WRITE = 00000020h +PROCESS_DUP_HANDLE = 00000040h +PROCESS_CREATE_PROCESS = 00000080h +PROCESS_SET_QUOTA = 00000100h +PROCESS_SET_INFORMATION = 00000200h +PROCESS_QUERY_INFORMATION = 00000400h +PROCESS_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED or SYNCHRONIZE or 0FFFh +FILE_SHARE_READ = 00000001h +FILE_SHARE_WRITE = 00000002h +FILE_SHARE_DELETE = 00000004h + +; CreateFile actions + +CREATE_NEW = 1 +CREATE_ALWAYS = 2 +OPEN_EXISTING = 3 +OPEN_ALWAYS = 4 +TRUNCATE_EXISTING = 5 diff --git a/x86_64_sse2_x87/fasm/source/windows/selfhost.inc b/x86_64_sse2_x87/fasm/source/windows/selfhost.inc new file mode 100644 index 0000000..9c1c68c --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/windows/selfhost.inc @@ -0,0 +1,171 @@ + +include '../../examples/x86/include/80386.inc' + +macro format?.PE? settings + PE.Settings.Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE or IMAGE_FILE_32BIT_MACHINE or IMAGE_FILE_LINE_NUMS_STRIPPED or IMAGE_FILE_LOCAL_SYMS_STRIPPED + PE.Settings.DllCharacteristics = 0 + PE.Settings.Stamp = +VERSION + local seq + define seq settings: + while 1 + match :, seq + break + else match =DLL? more, seq + PE.Settings.Characteristics = PE.Settings.Characteristics or IMAGE_FILE_DLL + redefine seq more + else match =large? more, seq + PE.Settings.Characteristics = PE.Settings.Characteristics or IMAGE_FILE_LARGE_ADDRESS_AWARE + redefine seq more + else match =WDM? more, seq + PE.Settings.DllCharacteristics = PE.Settings.DllCharacteristics or IMAGE_DLLCHARACTERISTICS_WDM_DRIVER + redefine seq more + else match =NX? more, seq + PE.Settings.DllCharacteristics = PE.Settings.DllCharacteristics or IMAGE_DLLCHARACTERISTICS_NX_COMPAT + redefine seq more + else match =at? base =on? stub :, seq + PE.Settings.ImageBase = base + PE.Settings.Stub = stub + break + else match =at? base :, seq + PE.Settings.ImageBase = base + break + else match =on? stub :, seq + PE.Settings.Stub = stub + break + else + match =GUI? more, seq + PE.Settings.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI + redefine seq more + else match =console? more, seq + PE.Settings.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI + redefine seq more + else match =native? more, seq + PE.Settings.Subsystem = IMAGE_SUBSYSTEM_NATIVE + PE.Settings.SectionAlignment = 32 + PE.Settings.FileAlignment = 32 + redefine seq more + else match =EFI? more, seq + PE.Settings.Magic = 0x20B + PE.Settings.Subsystem = IMAGE_SUBSYSTEM_EFI_APPLICATION + redefine seq more + else match =EFIboot? more, seq + PE.Settings.Magic = 0x20B + PE.Settings.Subsystem = IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER + redefine seq more + else match =EFIruntime? more, seq + PE.Settings.Magic = 0x20B + PE.Settings.Subsystem = IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER + redefine seq more + else + err 'invalid argument' + break + end match + match V.v more, seq + PE.Settings.MajorSubsystemVersion = V + PE.Settings.MinorSubsystemVersion = v + redefine seq more + end match + end match + end while + if PE.Settings.Characteristics and IMAGE_FILE_DLL + format binary as 'dll' + else + format binary as 'exe' + end if + include '../../examples/x86/include/format/pe.inc' + use32 +end macro + +macro struct? name + macro ends?! + end namespace + esc end struc + virtual at 0 + name name + sizeof.name = $ + end virtual + purge ends? + end macro + esc struc name + label . : sizeof.name + namespace . +end macro + +calminstruction invoke? proc*,args& + local tmp, stack + match , args + jyes go + collect: + match tmp=,args, args + take stack, tmp + jyes collect + push: + arrange args, =pushd args + assemble args + take args, stack + jyes push + go: + arrange proc, =call [proc] + assemble proc +end calminstruction + +macro library? definitions& + PE.Imports: + iterate , definitions + if ~ name.redundant + dd RVA name.lookup,0,0,RVA name.str,RVA name.address + end if + name.referred = 1 + end iterate + dd 0,0,0,0,0 + iterate , definitions + if ~ name.redundant + name.str db string,0 + align 2 + end if + end iterate +end macro + +macro import? name,definitions& + align 4 + if defined name.referred + name.lookup: + iterate , definitions + if used label + if string eqtype '' + dd RVA name.label + else + dd 80000000h + string + end if + end if + end iterate + if $ > name.lookup + name.redundant = 0 + dd 0 + else + name.redundant = 1 + end if + name.address: + iterate , definitions + if used label + if string eqtype '' + label dd RVA name.label + else + label dd 80000000h + string + end if + end if + end iterate + if ~ name.redundant + dd 0 + end if + iterate , definitions + if used label & string eqtype '' + name.label dw 0 + db string,0 + align 2 + end if + end iterate + end if +end macro + +include 'kernel32.inc' diff --git a/x86_64_sse2_x87/fasm/source/windows/system.inc b/x86_64_sse2_x87/fasm/source/windows/system.inc new file mode 100644 index 0000000..bda9a18 --- /dev/null +++ b/x86_64_sse2_x87/fasm/source/windows/system.inc @@ -0,0 +1,217 @@ + +LINE_FEED equ 13,10 + +system_init: + invoke GetStdHandle,STD_OUTPUT_HANDLE + mov [stdout],eax + invoke GetStdHandle,STD_ERROR_HANDLE + mov [stderr],eax + invoke HeapCreate,0,20000h,0 + mov [memory],eax + test eax,eax + jz out_of_memory + invoke GetSystemTime,systemtime + invoke SystemTimeToFileTime,systemtime,filetime + mov ebx,[filetime.dwLowDateTime] + mov eax,[filetime.dwHighDateTime] + sub ebx,116444736000000000 and 0FFFFFFFFh + sbb eax,116444736000000000 shr 32 + xor edx,edx + mov ecx,10000000 + div ecx + mov dword [timestamp+4],eax + mov eax,ebx + div ecx + mov dword [timestamp],eax + retn + +system_shutdown: + cmp [memory],0 + je memory_released + invoke HeapDestroy,[memory] + memory_released: + invoke CloseHandle,[stdout] + invoke CloseHandle,[stderr] + retn + +malloc: +malloc_fixed: +malloc_growable: +; in: ecx = requested size +; out: eax - allocated block, ecx = allocated size, on error jumps to out_of_memory (does not return) +; preserves: ebx, esi, edi +; note: +; use of malloc_fixed hints that block will be kept as is until the end of assembly +; use of malloc_growable hints that block is likely to be resized + invoke HeapAlloc,[memory],0,ecx + test eax,eax + jz out_of_memory + memory_allocated: + push eax + invoke HeapSize,[memory],0,eax + mov ecx,eax + pop eax + cmp ecx,-1 + je out_of_memory + retn +realloc: +; in: eax - memory block, ecx = requested size +; out: eax - resized block, ecx = allocated size, on error jumps to out_of_memory (does not return) +; preserves: ebx, esi, edi + invoke HeapReAlloc,[memory],0,eax,ecx + test eax,eax + jnz memory_allocated + jmp out_of_memory +mfree: +; in: eax - memory block +; out: cf set on error +; preserves: ebx, esi, edi +; note: eax may have value 0 or -1, it should be treated as invalid input then + test eax,eax + jz interface_error + cmp eax,-1 + je interface_error + invoke HeapFree,[memory],0,eax + test eax,eax + jz interface_error + clc + retn + interface_error: + stc + retn + +open: +; in: edx - path to file +; out: ebx = file handle, cf set on error +; preserves: esi, edi + invoke CreateFile,edx,GENERIC_READ,FILE_SHARE_READ,0,OPEN_EXISTING,0,0 + cmp eax,-1 + je interface_error + mov ebx,eax + clc + retn +create: +; in: edx - path to file +; out: ebx = file handle, cf set on error +; preserves: esi, edi + invoke CreateFile,edx,GENERIC_WRITE,0,0,CREATE_ALWAYS,0,0 + cmp eax,-1 + je interface_error + mov ebx,eax + clc + retn +write: +; in: ebx = file handle, edx - data, ecx = number of bytes +; out: cf set on error +; preserves: ebx, esi, edi + push ecx + invoke WriteFile,ebx,edx,ecx,systmp,0 + pop ecx + test eax,eax + jz interface_error + cmp ecx,[systmp] + jne interface_error + clc + retn +read: +; in: ebx = file handle, edx - buffer, ecx = number of bytes +; out: cf set on error +; preserves: ebx, esi, edi + push ecx + invoke ReadFile,ebx,edx,ecx,systmp,0 + pop ecx + test eax,eax + jz interface_error + cmp ecx,[systmp] + jne interface_error + clc + retn +close: +; in: ebx = file handle +; preserves: ebx, esi, edi + invoke CloseHandle,ebx + retn +lseek: +; in: ebx = file handle, cl = method, edx:eax = offset +; out: edx:eax = new offset from the beginning of file, cf set on error +; preserves: ebx, esi, edi + movzx ecx,cl + mov [systmp],edx + invoke SetFilePointer,ebx,eax,systmp,ecx + cmp eax,-1 + jne lseek_ok + invoke GetLastError + test eax,eax + jnz interface_error + not eax + lseek_ok: + mov edx,[systmp] + clc + retn + +get_timestamp: +; out: edx:eax = timestamp +; preserves: ebx, ecx, esi, edi + mov eax,dword [timestamp] + mov edx,dword [timestamp+4] + retn + +display_string: +; in: +; esi - string +; ecx = string length, zero for ASCIIZ string +; preserves: ebx, esi + push ebx + mov ebx,[stdout] + jmp write_string +display_error_string: +; in: +; esi - string +; ecx = string length, zero for ASCIIZ string +; preserves: ebx, esi + push ebx + mov ebx,[stderr] + write_string: + test ecx,ecx + jnz write_string_to_stdout + xor al,al + mov edi,esi + or ecx,-1 + repne scasb + neg ecx + sub ecx,2 + write_string_to_stdout: + mov edx,esi + write_portion_to_stdout: + mov eax,51200 + cmp ecx,eax + jbe final_write_to_stdout + sub ecx,eax + add eax,edx + push eax ecx + invoke WriteFile,ebx,edx,51200,systmp,0 + pop ecx edx + jmp write_portion_to_stdout + final_write_to_stdout: + invoke WriteFile,ebx,edx,ecx,systmp,0 + pop ebx + retn + +get_environment_variable: +; in: +; esi - name +; edi - buffer for value +; ecx = size of buffer +; out: +; eax = length of value +; preserves: ebx, esi, edi + push ecx + invoke GetEnvironmentVariable,esi,edi,ecx + pop ecx + cmp eax,ecx + jae environment_variable_ready + mov byte [edi+eax],0 + environment_variable_ready: + inc eax + retn + diff --git a/x86_64_sse2_x87/fmap.s b/x86_64_sse2_x87/fmap.s new file mode 100644 index 0000000..39f2d08 --- /dev/null +++ b/x86_64_sse2_x87/fmap.s @@ -0,0 +1,42 @@ +; vim: set filetype=fasm foldmethod=marker commentstring=;%s colorcolumn=101 : +define SYSTEM_LEGACY 1 +;*************************************************************************************************** +include 'x64.inc' +if ~SYSTEM_LEGACY + include 'ext/movbe.inc' + include 'ext/avx2.inc' +else + include 'ext/ssse3.inc' +end if +include 'format/elf64.inc' +use64 +;*************************************************************************************************** +define TRACE_DEBUG_ENABLED 0 +define TRACE_GENERIC_ENABLED 0 +define TRACE_NODE_IDX_ENABLED 0 +define TRACE_WAYS_IDX_ENABLED 0 +define TRACE_RELATIONS_IDX_ENABLED 0 +define TRACE_RELATIONS_2ND_PASS_ENABLED 0 +define TRACE_FEATURES_ENABLED 0 +;--------------------------------------------------------------------------------------------------- +include 'util_macros.s' +;*************************************************************************************************** +include 'linux.s' +;*************************************************************************************************** +section '.text' executable writeable align 64 +include 'libc/text.s' +include 'inflate/zlib/text.s' +include 'pbf/text.s' +include 'text.s' +;*************************************************************************************************** +section '.data' writeable align 64 +include 'libc/data.s' +include 'inflate/zlib/data.s' +include 'pbf/data.s' +include 'data.s' +;*************************************************************************************************** +section '.bss' writeable align 64 +include 'libc/bss.s' +include 'inflate/zlib/bss.s' +include 'pbf/bss.s' +include 'bss.s' diff --git a/x86_64_sse2_x87/inflate/zlib/bss.s b/x86_64_sse2_x87/inflate/zlib/bss.s new file mode 100644 index 0000000..a5fb0c2 --- /dev/null +++ b/x86_64_sse2_x87/inflate/zlib/bss.s @@ -0,0 +1,36 @@ +; vim: set filetype=fasm foldmethod=marker commentstring=;%s colorcolumn=101 : +namespace inflate +align 64 +z_stream rb z_stream_t.bytes_n +align 64 +module.handle rq 1 +inflateInit rq 1 +inflateEnd rq 1 +inflate rq 1 +;--------------------------------------------------------------------------------------------------- +z_stream_t: namespace z_stream_t + next_in := 0 ; qw, next input byte + avail_in := 8 ; dw, number of bytes available at next_in + ;dw pad + total_in := 16 ; qw, total number of input bytes read so far + + next_out := 24 ; qw, next output byte will go here + avail_out := 32 ; dw, remaining free space at next_out + ;dw pad + total_out := 40 ; qw, total number of bytes output so far + + msg := 48 ; qw, last error message, NULL if no error + state := 56 ; qw, not visible by applications + + zalloc := 64 ; qw, used to allocate the internal state + zfree := 72 ; qw, used to free the internal state + opaque := 80 ; qw, private data object passed to zalloc and zfree + + data_type := 88 ; dw, best guess about the data type: binary or tex + ; dw pad + ; for deflate, or the decoding state for inflate + adler := 96 ; qw, Adler-32 or CRC-32 value of the uncompressed data + reserved := 104 ; qw, reserved for future use + bytes_n := 112 +end namespace ; z_stream_t +end namespace diff --git a/x86_64_sse2_x87/inflate/zlib/data.s b/x86_64_sse2_x87/inflate/zlib/data.s new file mode 100644 index 0000000..b20ab35 --- /dev/null +++ b/x86_64_sse2_x87/inflate/zlib/data.s @@ -0,0 +1,12 @@ +; vim: set filetype=fasm foldmethod=marker commentstring=;%s colorcolumn=101 : +namespace inflate +module.name db 'libz.so.1',0 +inflateInit.sym db 'inflateInit_',0 ; inflateInit is only a macro +inflateEnd.sym db 'inflateEnd',0 +inflate.sym db 'inflate',0 +zlib_version db '1.2.11',0 ; needed for the real inflateInit_ +msg: namespace msg + module_open_failed db 'unable to dlopen "%s" module',10,0 + sym_resolution_failed db 'unable to dlsym "%s" symbol',10,0 +end namespace ; msg +end namespace diff --git a/x86_64_sse2_x87/inflate/zlib/text.s b/x86_64_sse2_x87/inflate/zlib/text.s new file mode 100644 index 0000000..f0a8b29 --- /dev/null +++ b/x86_64_sse2_x87/inflate/zlib/text.s @@ -0,0 +1,124 @@ +; vim: set filetype=fasm foldmethod=marker commentstring=;%s colorcolumn=101 : +inflate: namespace inflate +init_once: + enter 0,0 + and rsp, not 0xf + + lea rdi, [module.name] + mov rsi, 0x0000000000000001 ; RTLD_LAZY + call PLT.dlopen + test rax, rax + jz .err_module_open_failed + mov [module.handle], rax + + symbol inflateInit + symbol inflateEnd + symbol inflate + stc +.exit: + leave + ret +error module_open, module_open_failed, module.name +error module_inflateInit_sym, sym_resolution_failed, inflateInit.sym +error module_inflateEnd_sym, sym_resolution_failed, inflateEnd.sym +error module_inflate_sym, sym_resolution_failed, inflate.sym +;--------------------------------------------------------------------------------------------------- +; in: rdi = inflate_bytes_start +; in: rsi = inflate_bytes_n_max +; in: rdx = deflate_bytes_start +; in: rcx = deflate_bytes_n +define inflate_bytes_start rbx +define inflate_bytes_n_max r12 +define inflate_bytes_n_max_dword r12d ; 32bits dword +define deflate_bytes_start r13 +define deflate_bytes_n r14 +define deflate_bytes_n_dword r14d ; 32bits dword +align_nops +do: + enter 4 * 8, 0 + and rsp, not 0xf ; align the stack here for external calls + mov [rbp - 8 * 1], inflate_bytes_start + mov [rbp - 8 * 2], inflate_bytes_n_max + mov [rbp - 8 * 3], deflate_bytes_start + mov [rbp - 8 * 4], deflate_bytes_n + + mov inflate_bytes_start, rdi + mov inflate_bytes_n_max, rsi + mov deflate_bytes_start, rdx + mov deflate_bytes_n, rcx + + mov qword [z_stream + z_stream_t.zalloc], 0 + mov qword [z_stream + z_stream_t.zfree], 0 + mov dword [z_stream + z_stream_t.avail_in], 0 + mov qword [z_stream + z_stream_t.next_in], 0 + lea rdi, [z_stream] + lea rsi, [zlib_version] + mov rdx, z_stream_t.bytes_n + call qword [inflateInit] + test rax, rax + jnz .exit_nok + + mov qword [z_stream + z_stream_t.next_in], deflate_bytes_start + mov dword [z_stream + z_stream_t.avail_in], deflate_bytes_n_dword + mov qword [z_stream + z_stream_t.next_out], inflate_bytes_start + mov dword [z_stream + z_stream_t.avail_out], inflate_bytes_n_max_dword + lea rdi, [z_stream] + mov rsi, 4 ; = Z_FINISH we want a 1 call inflate + call qword [inflate] + cmp rax, 1 ; = Z_STREAM_END + setne bl ; error or deflate not performed in 1 call (bad omens) + + lea rdi, [z_stream] + call qword [inflateEnd] + test bl, bl + jnz .exit_nok +.exit_ok: + stc +.exit: + mov inflate_bytes_start, [rbp - 8 * 1] + mov inflate_bytes_n_max, [rbp - 8 * 2] + mov deflate_bytes_start, [rbp - 8 * 3] + mov deflate_bytes_n, [rbp - 8 * 4] + leave + ret +.exit_nok: + clc + jmp .exit +;--------------------------------------------------------------------------------------------------- +purge inflate_bytes_start +purge inflate_bytes_n_max +purge inflate_bytes_n_max_dword +purge deflate_bytes_start +purge deflate_bytes_n +purge deflate_bytes_n_dword +;--------------------------------------------------------------------------------------------------- +fini_once: + enter 0,0 + and rsp, not 0xf + + mov rdi, [module.handle] + call PLT.dlclose ; 0 on success + + leave + ret +;--------------------------------------------------------------------------------------------------- +;{{{ macros +macro symbol name + mov rdi, [module.handle] + lea rsi, [name.sym] + call PLT.dlsym + test rax, rax + jz .err_module_#name#_sym_failed + mov [name], rax +end macro + +macro error name, fmt, s +.err_#name#_failed: + lea rdi, [msg.fmt] + lea rsi, [s] + call qword [libc.printf] + clc + jmp .exit +end macro +;}}} macros +end namespace diff --git a/x86_64_sse2_x87/ld.simple b/x86_64_sse2_x87/ld.simple new file mode 100644 index 0000000..86a1d8d --- /dev/null +++ b/x86_64_sse2_x87/ld.simple @@ -0,0 +1,64 @@ +/* + * We target -pie -z combreloc. + * We pack everything in one segment, then we don't optimize on boundaries + * related to the various system page sizes. + */ +/* default, big, little */ +OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64") +OUTPUT_ARCH(i386:x86-64) +ENTRY(fmap_entry) +/* Table of segments. They can overlay themselves depending on their content. */ +PHDRS +{ + headers_seg PT_PHDR PHDRS; + interp_seg PT_INTERP; + dynamic_seg PT_DYNAMIC; + rwe_seg PT_LOAD FILEHDR PHDRS FLAGS(7); +} +SECTIONS +{ + . = SIZEOF_HEADERS; + .interp : { *(.interp) } : interp_seg : rwe_seg + .hash : { *(.hash) } : rwe_seg + .gnu.hash : { *(.gnu.hash) } : rwe_seg + .dynsym : { *(.dynsym) } : rwe_seg + .dynstr : { *(.dynstr) } : rwe_seg + .gnu.version : { *(.gnu.version) } : rwe_seg + .gnu.version_r : { *(.gnu.version_r) } : rwe_seg + .gnu.version_d : { *(.gnu.version_d) } : rwe_seg + .rela.dyn : + { + *(.rela.init) + *(.rela.text .rela.text.*) + *(.rela.fini) + *(.rela.rodata .rela.rodata.*) + *(.rela.data .rela.data.*) + *(.rela.ctors) + *(.rela.dtors) + *(.rela.got) + *(.rela.bss .rela.bss.*) + *(.rela.ifunc) + } : rwe_seg + .rela.plt : { *(.rela.plt) } : rwe_seg + /* "command line order" concatened glibc crti.S and crtn.S (prolog) with the "magic" function _init which will make the linker pick it for DT_INIT */ + .init : { KEEP (*(SORT_NONE(.init))) } : rwe_seg + .plt : { *(.plt) } : rwe_seg + .plt.got : { *(.plt.got) } : rwe_seg + .text : { *(.text) } : rwe_seg + /* "command line order" concatened glibc crti.S and crtn.S (prolog) with the "magic" function _fini which will make the linker pick it for DT_FINI */ + .fini : { KEEP (*(SORT_NONE(.fini))) } : rwe_seg + .init_array : + { + /* we don't have any, but those "magic" symbols are required by glibc csu */ + PROVIDE_HIDDEN (__init_array_start = .); + PROVIDE_HIDDEN (__init_array_end = .); /* see above */ + } : rwe_seg + .dynamic : { *(.dynamic) } : dynamic_seg : rwe_seg + .got : { *(.got) } : rwe_seg + .got.plt : { *(.got.plt) } : rwe_seg + .data : { *(.data) } : rwe_seg + .rodata : { *(.rodata .rodata.*) } : rwe_seg + .bss : { *(.dynbss) *(.bss) } : rwe_seg + /DISCARD/ : { *(.note.GNU-stack) *(.eh_frame*) *(.note.ABI-tag) } +} + diff --git a/x86_64_sse2_x87/libc/bss.s b/x86_64_sse2_x87/libc/bss.s new file mode 100644 index 0000000..571bf08 --- /dev/null +++ b/x86_64_sse2_x87/libc/bss.s @@ -0,0 +1,9 @@ +; vim: set filetype=fasm foldmethod=marker commentstring=;%s colorcolumn=101 : +namespace libc +align 64 +libc.handle rq 1 +printf rq 1 +dprintf rq 1 +stdout rq 1 +fflush rq 1 +end namespace diff --git a/x86_64_sse2_x87/libc/data.s b/x86_64_sse2_x87/libc/data.s new file mode 100644 index 0000000..f5abbc6 --- /dev/null +++ b/x86_64_sse2_x87/libc/data.s @@ -0,0 +1,20 @@ +; vim: set filetype=fasm foldmethod=marker commentstring=;%s colorcolumn=101 : +namespace libc +libc.name db 'libc.so.6',0 +printf.sym db 'printf',0 +dprintf.sym db 'dprintf',0 +stdout.sym db 'stdout',0 +fflush.sym db 'fflush',0 +msg: namespace msg + err_libc_open_failed db 'ERROR:unable to open libc.so.6',10 + .bytes_n = $ - . + err_dprintf_dlsym_failed db 'ERROR:libc:unable to resolve dprintf',10 + .bytes_n = $ - . + err_printf_dlsym_failed db 'ERROR:libc:unable to resolve printf',10 + .bytes_n = $ - . + err_stdout_dlsym_failed db 'ERROR:libc:unable to resolve stdout',10 + .bytes_n = $ - . + err_fflush_dlsym_failed db 'ERROR:libc:unable to resolve fflush',10 + .bytes_n = $ - . +end namespace ; msg +end namespace ; libc diff --git a/x86_64_sse2_x87/libc/text.s b/x86_64_sse2_x87/libc/text.s new file mode 100644 index 0000000..3edec49 --- /dev/null +++ b/x86_64_sse2_x87/libc/text.s @@ -0,0 +1,102 @@ +; vim: set filetype=fasm foldmethod=marker commentstring=;%s colorcolumn=101 : +extrn dlopen +extrn dlsym +extrn dlclose +;--------------------------------------------------------------------------------------------------- +libc: namespace libc +align_nops +init_once: + ; we do align the stack here for the series of external calls + enter 0,0 + and rsp, not 0xf + + lea rdi, [libc.name] + mov rsi, 0x0000000000000001 ; RTLD_LAZY + call PLT.dlopen + test rax, rax + jz .err_libc_open_failed + mov [libc.handle], rax + + mov rdi, rax + lea rsi, [printf.sym] + call PLT.dlsym + test rax, rax + jz .err_libc_printf_dlsym_failed + mov [printf], rax + + mov rdi, [libc.handle] + lea rsi, [dprintf.sym] + call PLT.dlsym + test rax, rax + jz .err_libc_dprintf_dlsym_failed + mov [dprintf], rax + + mov rdi, [libc.handle] + lea rsi, [stdout.sym] + call PLT.dlsym + test rax, rax + jz .err_libc_stdout_dlsym_failed + mov [stdout], rax + + mov rdi, [libc.handle] + lea rsi, [fflush.sym] + call PLT.dlsym + test rax, rax + jz .err_libc_fflush_dlsym_failed + mov [fflush], rax + ;------------------------------------------------------------------------------------------- + stc + leave + ret + +.exit_nok: + clc + leave + ret + +.err_libc_open_failed: + mov rax, linux.write + mov rdi, 2 ; stderr + lea rsi, [msg.err_libc_open_failed] + mov rdx, msg.err_libc_open_failed.bytes_n + syscall + jmp .exit_nok +.err_libc_printf_dlsym_failed: + mov rax, linux.write + mov rdi, 2 ; stderr + lea rsi, [msg.err_printf_dlsym_failed] + mov rdx, msg.err_printf_dlsym_failed.bytes_n + syscall + jmp .exit_nok +.err_libc_dprintf_dlsym_failed: + mov rax, linux.write + mov rdi, 2 ; stderr + lea rsi, [msg.err_dprintf_dlsym_failed] + mov rdx, msg.err_dprintf_dlsym_failed.bytes_n + syscall + jmp .exit_nok +.err_libc_stdout_dlsym_failed: + mov rax, linux.write + mov rdi, 2 ; stderr + lea rsi, [msg.err_stdout_dlsym_failed] + mov rdx, msg.err_stdout_dlsym_failed.bytes_n + syscall + jmp .exit_nok +.err_libc_fflush_dlsym_failed: + mov rax, linux.write + mov rdi, 2 ; stderr + lea rsi, [msg.err_fflush_dlsym_failed] + mov rdx, msg.err_fflush_dlsym_failed.bytes_n + syscall + jmp .exit_nok +;--------------------------------------------------------------------------------------------------- +align_nops +fini_once: + enter 0,0 + and rsp, not 0xf + + mov rdi, [libc.handle] + call PLT.dlclose ; 0 on success + leave + ret +end namespace diff --git a/x86_64_sse2_x87/linux.s b/x86_64_sse2_x87/linux.s new file mode 100644 index 0000000..9317070 --- /dev/null +++ b/x86_64_sse2_x87/linux.s @@ -0,0 +1,47 @@ +; vim: set filetype=fasm foldmethod=marker commentstring=;%s colorcolumn=101 : +linux: namespace linux +;{{{ stat_t ---------------------------------------------------------------------------------------- +stat_t: namespace stat_t + dev :=0 ; qw/u + ino :=8 ; qw/u + nlink :=16 ; qw/u + + mode :=24 ; dw/u + uid :=28 ; dw/u + gid :=32 ; dw/u + ; 1 dw pad + rdev :=40 ; qw/u + sz :=48 ; qw/s + blk_sz :=56 ; qw/s + blks :=64 ; qw/s, num of 512-bytes blocks allocated + + atime :=72 ; qw/u + atime_nsec :=80 ; qw/u + mtime :=88 ; qw/u + mtime_nsec :=96 ; qw/u + ctime :=104 ; qw/u + ctime_nsec :=112 ; qw/u + bytes_n := 144 + ; 3 qws pad +end namespace ;}}} stat_t -------------------------------------------------------------------------- +errno.last := -4096 +; syscalls +fstat := 5 +write := 1 +open := 2 ; not on arm64, use openat +mmap := 9 +munmap := 11 +mremap := 25 +exit_group := 231 +openat := 257 +mkdirat := 258 +fstatat := 262 + +AT_FDCWD := -100 +O_RDONLY := 001b +O_WRONLY := 010b +O_RDWR := 100b +O_CREAT := 1000000b +O_TRUNC := 1000000000b +O_DIRECTORY := 010000000000000000b +end namespace diff --git a/x86_64_sse2_x87/math.s b/x86_64_sse2_x87/math.s new file mode 100644 index 0000000..6b65927 --- /dev/null +++ b/x86_64_sse2_x87/math.s @@ -0,0 +1,76 @@ +; vim: set filetype=fasm foldmethod=marker commentstring=;%s colorcolumn=101 : +geo_to_wm: +; May significantly change once we have an optimized taylor series for direct computation of wm +; coords, and once we switch to full avx2. +define scaled_zoom_p rdi ; in (1 << zoom) f64 +; in blk, vec2f64 of geo coords +define lon rsi + 8 * 0 ; f64 +define lat rsi + 8 * 1 ; f64 +; out blk pointed by rcx, 2 vec2s64: vec of global tile coords then vec of location coords into the tile +define tile_x rdx + 8 * 0 ; s64 +define tile_y rdx + 8 * 1 ; s64 +define tile_u rdx + 8 * 2 ; f64, location into the tile, [0,1[ +define tile_v rdx + 8 * 3 ; f64, location into the tile, [0,1[ +.f64: ; lat/lon are f64 + ; setup the floor fn (expect the "nearest" rounding mode being set) + fnstcw [.cw] + or word [.cw], 10000000000b ; round down = floor + fldcw [.cw] + ; --- x and y + fld qword [scaled_zoom_p] + fld st0 + ; --- x + fld qword [.f64.360_billion] + fld qword [.f64.180_billion] + fld qword [lon] + faddp ; lon + 180.0 + fdivrp ; (lon + 180.0) / 360.0 , billions are going away here + fmulp ; (lon + 180.0) / 360.0 * (1 << zoom) + fld st0 ; st0 = x, st1 = x + frndint ; st0 = floor(x) = tile_x, st1 = x + fsub st1, st0; st0 = floor(x) = tile_x, st1 = x - floor(x) = tile_u + fistp qword [tile_x] ; st0 = tile_u + fstp qword [tile_u] + ; --- y + fld qword [.f64.180_billion] + fld qword [lat] + fldpi + fmulp ; lat * PI + fdivrp ; lat * PI / 180.0 = lat_rad , billions are going away here + fsincos + fxch ; st0 = sin(lat_rad), st1 = cos(lat_rad) + fld1 ; st0 = 1.0, st1 = sin(lat_rad), st2 = cos(lat_rad) + faddp ; st0 = 1.0 + sin(lat_rad), st1 = cos(lat_rad) + fdivrp ; (1.0 + sin(lat_rad)) / cos(lat_rad) + fldln2 ; ln(2) + fxch + fyl2x ; arsinh(tan(la_rad)) = ln((1.0 + sin(lat_rad)) / cos(lat_rad)) + fldpi + fdivp ; arsinh(tan(la_rad) / PI + fld1 + fsubrp ; 1.0 - (arsinh(tan(la_rad)) / PI) + fld1 + fld1 + fscale ; 2.0 + fxch + ffree st0 ; pop 1.0 + fincstp + fdivp ; (1.0 - (arsinh(tan(la_rad)) / PI)) / 2.0 + fmulp ; (1.0 - (arsinh(tan(la_rad)) / PI)) / 2.0 * (1 << zoom) + fld st0 ; st0 = y, st1 = y + frndint ; st0 = floor(y) = tile_y, st1 = y + fsub st1, st0 ; st0 = floor(y) = tile_y, st1 = y - floor(y) = tile_v + fistp qword [tile_y] ; st0 = tile_v + fstp qword [tile_v] + ret +align 64 +.f64.360_billion dq 360000000000.0 +.f64.180_billion dq 180000000000.0 +.cw dw 0 +purge scaled_zoom_p +purge lat_p +purge lon_p +purge tile_x +purge tile_y +purge tile_v +purge tile_u diff --git a/x86_64_sse2_x87/pbf/blk/hdr/text.s b/x86_64_sse2_x87/pbf/blk/hdr/text.s new file mode 100644 index 0000000..e6bc583 --- /dev/null +++ b/x86_64_sse2_x87/pbf/blk/hdr/text.s @@ -0,0 +1,42 @@ +; vim: set filetype=fasm foldmethod=marker commentstring=;%s colorcolumn=101 : +hdr: namespace hdr +; we don't follow specs: we will try to go best effort in blk_primitive parsing +; scratch: rax rcx r11 r9 +; from blk +define p rbx ; in +define p_finish r12 ; in +;define flags r15 ; in/out +align_nops +parse: +.key_next: + cmp p, p_finish + je blob.hdr.parse.return_from_blk + varint_load r11, p ; key + mov r9, r11; = copy key + shr r11, 3 ; = field num + ; print strings we are aware of + cmp r11, 4 ; field num = HeaderBlock.required_features (see specs) ? + je .str_print + cmp r11, 5 ; field num = HeaderBlock.optional_features (see specs) ? + je .str_print + cmp r11, 16 ; field num = HeaderBlock.writingprogram (see specs) ? + je .str_print + cmp r11, 17 ; field num = HeaderBlock.source (see specs) ? + je .str_print + cmp r11, 34 ; field num = HeaderBlock.osmosis_replication_base_url (see specs) ? + je .str_print + and r9, 111b ; = field type + TRACE 'PBF:BLK:HDR:MSG:FIELD %lu OF TYPE %lu', r11, r9 + val_skip p, r9, r11 + jmp .key_next +align_nops +.str_print: + varint_load r9, p ; = str_sz + TRACE 'PBF:BLK:HDR:MSG:STRING %lu of VALUE "%.*s"', r11, r9, p + add p, r9 ; skip the str val + jmp .key_next +;--------------------------------------------------------------------------------------------------- +purge p +purge p_finish +;purge flags +end namespace ; hdr diff --git a/x86_64_sse2_x87/pbf/blk/primblk/primgrp/dense/bss.s b/x86_64_sse2_x87/pbf/blk/primblk/primgrp/dense/bss.s new file mode 100644 index 0000000..ccfc2c4 --- /dev/null +++ b/x86_64_sse2_x87/pbf/blk/primblk/primgrp/dense/bss.s @@ -0,0 +1,44 @@ +; vim: set filetype=fasm foldmethod=marker commentstring=;%s colorcolumn=101 : +namespace blk.primblk.primgrp.dense +align 64 +nodes rq 1 +nodes.finish rq 1 ; finish node, inited to aboves nodes +nodes.n rq 1 ; how many dense nodes in this pbf +nodes.idx rq 1 ; binary tree, bits from the node id +nodes.idx.finish rq 1 ; next available idx slot +namespace nodes ;{{{ ------------------------------------------------------------------------------- + bytes_n = 64 * 1024 * 1024 * 1024 ; 64GiB + node_t: namespace node_t ;{{{ -------------------------------------------------------------- + next := 0 ; ptr on next node (lookup for node-only features) + flags := 8 ; expected to be inited to 0 + flags.wm_computed := 0 + geo := 16 ; vec2f64 + geo.lon := 16 ; f64 + geo.lat := 24 ; f64 + wm.tile := 32 ; vec2s64/vec2f64 + wm.tile.xy := 32 ; vec2s64, >=0 + wm.tile.xy.x := 32 ; s64 + wm.tile.xy.y := 40 ; s64 + ; section which will go into the tile db -- START ================================= + wm.tile.uv := 48 ; vec2f64, location into the tile, [0,1[ + wm.tile.uv.u := 48 ; f64 + wm.tile.uv.v := 56 ; f64 + ; (key,val)s: + ; keys start with a byte containing their sz in bytes + ; vals start with a short containing their sz in bytes + ; the array is terminated with a 0-szed key + keys_vals := 64 + ; section which will go into the tile db -- END =================================== + end namespace ;}}} node_t ------------------------------------------------------------------ +end namespace ;}}} nodes --------------------------------------------------------------------------- +namespace nodes.idx ;{{{ ---------------------------------------------------------------------------- + slot_t: namespace slot_t + bit0 := 0 ; qw, ptr on idx slot for this bit + bit1 := 8 ; qw, idem + node := 16 ; qw, ptr on the node in the array + bytes_n := 24 + end namespace + n = 1000 * 1000 * 1000 ; 1 billion nodes + bytes_n = n * slot_t.bytes_n ; 24GB +end namespace ;}}} nodes.idx ------------------------------------------------------------------------ +end namespace diff --git a/x86_64_sse2_x87/pbf/blk/primblk/primgrp/dense/data.s b/x86_64_sse2_x87/pbf/blk/primblk/primgrp/dense/data.s new file mode 100644 index 0000000..7462bc4 --- /dev/null +++ b/x86_64_sse2_x87/pbf/blk/primblk/primgrp/dense/data.s @@ -0,0 +1,8 @@ +; vim: set filetype=fasm foldmethod=marker commentstring=;%s colorcolumn=101 : +namespace blk.primblk.primgrp.dense.nodes + msg: namespace msg + mmap_failed db 'ERROR(%ld):PBF:PRIMBLK:PRIMGRP:DENSE:NODES:unable to mmap nodes',10,0 + idx_mmap_failed db 'ERROR(%ld):PBF:PRIMBLK:PRIMGRP:DENSE:NODES:IDX:unable to mmap the index',10,0 + idx_nodes_n db 'PBF:BLK:PRIMBLK:DENSE:INIT_ONCE:reserving a memory mapping for an index able to handle %lu nodes',10,0 + end namespace +end namespace diff --git a/x86_64_sse2_x87/pbf/blk/primblk/primgrp/dense/text.s b/x86_64_sse2_x87/pbf/blk/primblk/primgrp/dense/text.s new file mode 100644 index 0000000..789aabf --- /dev/null +++ b/x86_64_sse2_x87/pbf/blk/primblk/primgrp/dense/text.s @@ -0,0 +1,588 @@ +; vim: set filetype=fasm foldmethod=marker commentstring=;%s colorcolumn=101 : +; XXX: We actually used this code unit as a training ground. Expect it to be very overkill. +dense: namespace dense +;{{{ init_once ------------------------------------------------------------------------------------- +align_nops +init_once: + TRACE 'PBF:BLK:PRIMBLK:PRIMGRP:DENSE:INIT_ONCE' + mov rbp, rsp + and rsp, not 0xf + + xor edi, edi + mov rsi, nodes.bytes_n + mov rdx, 1b or 10b ; PROT_READ | PROT_WRITE + mov r10, 0x22 ; MAP_PRIVATE | MAP_ANONYMOUS + xor r8d, r8d + xor r9d, r9d + mov rax, linux.mmap + syscall + cmp rax, linux.errno.last + jae .err_nodes_mmap_failed + mov [nodes], rax + mov [nodes.finish], rax + + lea rdi, [nodes.msg.idx_nodes_n] + mov rsi, nodes.idx.n + call qword [libc.printf] + + xor edi, edi + mov rsi, nodes.idx.bytes_n + mov rdx, 1b or 10b ; PROT_READ | PROT_WRITE + mov r10, 0x22 ; MAP_PRIVATE | MAP_ANONYMOUS + xor r8d, r8d + xor r9d, r9d + mov rax, linux.mmap + syscall + cmp rax, linux.errno.last + jae .err_nodes_idx_mmap_failed + mov [nodes.idx], rax + add rax, nodes.idx.slot_t.bytes_n + mov [nodes.idx.finish], rax ; next availabe idx slot + + stc ; return true + mov rsp, rbp + ret +.err_nodes_mmap_failed: + mov rdi, 2 ; stderr + mov rsi, rax ; errno + lea rsi, [nodes.msg.mmap_failed] + call qword [libc.dprintf] + clc ; return false + mov rsp, rbp + ret +.err_nodes_idx_mmap_failed: + mov rdi, 2 ; stderr + mov rsi, rax ; errno + lea rsi, [nodes.msg.idx_mmap_failed] + call qword [libc.dprintf] + clc ; return false + mov rsp, rbp + ret +;}}} init_once ------------------------------------------------------------------------------------- +; local memcpy ABI +define memcpy_dst rbx ; memcpy_dst(out) = memcpy_dst(in) + memcpy_bytes_n +define memcpy_src r11 ; clobbered, don't care +define memcpy_bytes_n r9 ; clobbered, don't care +define memcpy_link rcx ; the return addr +;=================================================================================================== +;{{{ parse ----------------------------------------------------------------------------------------- +; scratch ~ rax rcx r11 r9 +; from primgrp +define dense_p rbx ; in +define dense_p_finish r13 ; in +define flags r15 +define granularity rbp ; f64 +define strtbl_idx_p r8 +define lat_of_lon_of xmm3 ; f64/f64 +; local +define id_p r12 +define id_p_finish r14 +define lat_p r10 +define lon_p rdx +define keys_vals_p rsi ; can be 0 +align_nops +parse: + xor keys_vals_p, keys_vals_p ; = 0 because may not have any (see specs) +align_nops +.next_key: + cmp dense_p, dense_p_finish + je unserialize + varint_load r11, dense_p ; = key + mov r9, r11 ; = key copy + shr r11, 3 ; = field num + cmp r11, 1 + je .id_found + cmp r11, 8 + je .lat_found + cmp r11, 9 + je .lon_found + cmp r11, 10 + je .keys_vals_found + and r9, 111b ; = field type + TRACE 'PBF:BLK:PRIMBLK:PRIMGRP:DENSE:MSG:FIELD %lu OF TYPE %lu', r11, r9 + val_skip dense_p, r9, r11 + jmp .next_key +align_nops +.id_found: + varint_load r11, dense_p ; = packed_ids_sz + TRACE 'PBF:BLK:PRIMBLK:PRIMGRP:DENSE:ID:start = %p:size = %lu(0x%lx) bytes', dense_p, r11, r11 + mov id_p, dense_p + add dense_p, r11 ; dense_p + packed_ids_sz + mov id_p_finish, dense_p + jmp .next_key +align_nops +.lat_found: + varint_load r11, dense_p ;= packed_lat_sz + TRACE 'PBF:BLK:PRIMBLK:PRIMGRP:DENSE:LAT:start = %p:size = %lu(0x%lx) bytes', dense_p, r11, r11 + mov lat_p, dense_p + add dense_p, r11 ; dense_p + packed_lat_sz + jmp .next_key +align_nops +.lon_found: + varint_load r11, dense_p ;= packed_lon_sz + TRACE 'PBF:BLK:PRIMBLK:PRIMGRP:DENSE:LON:start = %p:size = %lu(0x%lx) bytes', dense_p, r11, r11 + mov lon_p, dense_p + add dense_p, r11 ; p + packed_lon_sz + jmp .next_key +align_nops +.keys_vals_found: + varint_load r11, dense_p ;= packed_keys_vals_sz + TRACE 'PBF:BLK:PRIMBLK:PRIMGRP:DENSE:KEYS_VALS:start = %p:size = %lu(0x%lx) bytes', dense_p, r11, r11 + mov keys_vals_p, dense_p + add dense_p, r11 ; dense_p + packed_keys_vals_sz + jmp .next_key +purge dense_p_finish +purge dense_p +;}}} parse ----------------------------------------------------------------------------------------- +;{{{ unserialize ----------------------------------------------------------------------------------- +; scratch ~ rax rcx r11 r9 xmm7 xmm6 xmm5 +; from primgrp +;define flags r15 +;define granularity rbp ; f64 +;define strtbl_idx_p r8 +;define lat_of_lon_of xmm3 ; f64/f64 +; local from parse: +;define id_p r12 +;define id_p_finish r14 +;define lat_p r10 +;define lon_p rdx +;define keys_vals_p rsi ; can be 0 +; local +define node_p rbx ; reuse dense_p +define prev_id r13 ; reuse dense_p_finish +define node_start_p rdi +define granularity_xmm xmm2 +define prev_lat_prev_lon xmm1 +define one_one xmm0 +; PERF +tsc_nodes TSC.STORAGE 'TSC:DENSE:%lu' +align_nops +unserialize: + mov node_p, [nodes.finish] +if ~SYSTEM_LEGACY + prefetchw [node_p] ; 2 cls should be very common + prefetchw [node_p + 64] +end if + pxor one_one, one_one ; construct the constant + pcmpeqd one_one, one_one ; xmm = 0xfffff... (128bits) + psrlq one_one, 63 + + xor prev_id, prev_id ; = 0 + pxor prev_lat_prev_lon, prev_lat_prev_lon ; = 0 + + movq granularity_xmm, granularity + punpcklqdq granularity_xmm, granularity_xmm + ; PERF + TSC.SAMPLE tsc_nodes, start +align_nops +.next_id: + cmp id_p, id_p_finish + je .epilog + inc qword [nodes.n] +;--------------------------------------------------------------------------------------------------- + ; no-branch vector zigzag decoding on sse (no avx2 coze our dev laptops) + ; encoded mod 2 == 1 : < 0 : -(encoded/2) - 1 = decoded + ; encoded mod 2 == 0 ; >= 0 : encoded/2 = decoded +if ~SYSTEM_LEGACY + varint_load r11, lat_p + pinsrq xmm5, r11, 1 ; lat_lon int64/int64 + varint_load r11, lon_p + pinsrq xmm5, r11, 0 ; lat_lon int64/int64 +else + varint_load r11, lat_p + movq xmm7, r11 + varint_load r11, lon_p + movq xmm5, r11 ; lat_lon int64/int64 + punpcklqdq xmm5, xmm7 ; = lat_lon int64/int64 +end if + movdqa xmm7, xmm5 + psrlq xmm5, 1 ; i >> 1, i = lat_lon int64/int64 + pand xmm7, one_one ; i & 1, i = lat_lon int64/int64 + xorps xmm6, xmm6 ; = 0 + psubq xmm6, xmm7 ; 0 - (i & 1) = - (i & 1) + pxor xmm5, xmm6 ; (i >> 1) ^ -(i & 1) = DELTA GEO OF granularity units for lat_lon int64/int64 + paddq prev_lat_prev_lon, xmm5 ; = PREV GEO OF = GEO OF granularity units +;--------------------------------------------------------------------------------------------------- + ; we can switch now from int64/int64 to f64/f64 since we finished decoding GEO coords + + ; lon granularity units int64->f64 -- START + movq rax, prev_lat_prev_lon + cvtsi2sd xmm7, rax + ; lon granularity units int64->f64 -- END + + ; lat granularity units int64->f64 -- START +if ~SYSTEM_LEGACY + pextrq rax, prev_lat_prev_lon, 1 ; = lat granularity units +else + movdqa xmm6, prev_lat_prev_lon + punpckhqdq xmm6, xmm6 ; DESTRUCTIVE:duplicate xmm high dq in xmm low dq, namely lat + movq rax, xmm6 ; = lat granularity units +end if + cvtsi2sd xmm6, rax + ; lat granularity units int64->f64 -- END + + unpcklpd xmm7, xmm6 ; = lat_lon f64/f64 + + mulpd xmm7, granularity_xmm ; lat_lon granularity units -> lat_lon nanodegrees f64/f64 + addpd xmm7, lat_of_lon_of ; lat_lon full nanodegrees f64/f64 + + ; cannot use non-temporal store here due to mis-alignment + movupd xword [node_p + nodes.node_t.geo], xmm7 ; little endian storage +;--------------------------------------------------------------------------------------------------- + mov node_start_p, node_p ; save the start of the node since are going to modify node_p + + test keys_vals_p, keys_vals_p + jnz keys_vals_cpy ; unlikely because (key/val)s are more likely on ways/relations + ; insert an empty key/terminator while updating node_p + add node_p, nodes.node_t.keys_vals + 1 ; room for the 0 terminator (= empty key) + mov byte [node_p - 1], 0 ; insert the 0 terminator +align_nops +.node_next_compute: + ; here, node_p points of the finish byte, aka the next node + mov qword [node_start_p + nodes.node_t.next], node_p + mov [nodes.finish], node_p ; save the finish/next node +;--------------------------------------------------------------------------------------------------- + ; decode the id + varint_load r11, id_p ; load/skip the raw zigzag delta_id + mov rax, r11 + shr r11, 1 ; i >> 1 + and rax, 1 ; i & 1 + neg rax ; -(i & 1) + xor r11, rax ; (i >> 1) ^ -(i & 1) ; = delta_id + add prev_id, r11 ; = prev_id = id +;{{{ idx_insert_node ------------------------------------------------------------------------------- +; scratch ~ rax xmm7 xmm6 +; from primgrp +;define flags r15 +;define granularity rbp ; f64 +;define strtbl_idx_p r8 +;define lat_of_lon_of xmm3 ; f64/f64 +; local from parse: +;define id_p r12 +;define id_p_finish r14 +;define lat_p r10 +;define lon_p rdx +;define keys_vals_p rsi ; can be 0 +; local from unserialize +;define node_p rbx +;define prev_id r13 ; = id +;define node_start_p rdi +;define granularity_xmm xmm2 +;define prev_lat_prev_lon xmm1 +;define one_one xmm0 +;local +define id prev_id +define idx_finish_p r9 +define id_bit_idx keys_vals_p ; spill +define bit_val_select rcx +define bit_val_select_b cl +define id_bit_idx_msb lon_p ; spill +define idx_slot_p r11 +idx_insert_node: + mov idx_slot_p, [nodes.idx] + mov idx_finish_p, [nodes.idx.finish] + movq xmm7, id_bit_idx_msb ; spill + movq xmm6, id_bit_idx ; spill + TRACE_NODE_IDX 'id=%#016lx nodes.idx=%p nodes.finish=%p', id, idx_slot_p, idx_finish_p + xor id_bit_idx, id_bit_idx + xor bit_val_select, bit_val_select + mov id_bit_idx_msb, -1 ; this is to make it work with id = 0 + bsr id_bit_idx_msb, id ; if id = 0, id_bit_idx_msb is untouched namely -1 + TRACE_NODE_IDX 'id_bit_idx_msb=%lu', id_bit_idx_msb +align_nops +.next_id_bit: + TRACE_NODE_IDX 'id_bit_idx=%lu', id_bit_idx + cmp id_bit_idx_msb, id_bit_idx + jb .insert_here + bt id, id_bit_idx + setc bit_val_select_b ; bit_val_select = 1 if id[id_bit_idx] = 1, else 0 + TRACE_NODE_IDX 'bit_val_select=%lu', bit_val_select + mov rax, [idx_slot_p + 8 * bit_val_select] + TRACE_NODE_IDX 'slot=%p bit slot=%p', idx_slot_p, rax + test rax, rax + jnz .idx_existing_slot + TRACE_NODE_IDX 'non existing slot using finish=%p', idx_finish_p + mov rax, idx_finish_p + add idx_finish_p, nodes.idx.slot_t.bytes_n ; we could zero the mem here, but mmap does it for us +if ~SYSTEM_LEGACY + prefetchw [idx_finish_p + 64 * 2] ; try to get ready + prefetchw [idx_finish_p + 64 * 3] +end if + mov [idx_slot_p + 8 * bit_val_select], rax +align_nops +.idx_existing_slot: + mov idx_slot_p, rax + TRACE_NODE_IDX 'next_slot=%p', idx_slot_p + inc id_bit_idx + + jmp .next_id_bit +align_nops +.insert_here: +if TRACE_NODE_IDX_ENABLED + mov rax, [idx_slot_p + nodes.idx.slot_t.node] + test rax, rax + jz .node_slot_available + TRACE_NODE_IDX 'WARNING: overwritting an existing node=%p', rax +.node_slot_available: + TRACE_NODE_IDX 'inserting node=%p in slot=%p', node_start_p, idx_slot_p +end if + mov [idx_slot_p + nodes.idx.slot_t.node], node_start_p + mov [nodes.idx.finish], idx_finish_p + movq id_bit_idx_msb, xmm7 ; unspill + movq id_bit_idx, xmm6 ; unspill +purge idx_slot_p +purge id_bit_idx_msb +purge bit_val_select_b +purge bit_val_select +purge id_bit_idx +purge idx_finish_p +purge id +;--------------------------------------------------------------------------------------------------- + ; XXX: if one day we need to be backed by a file we will need to DONT_NEED "madvise" the + ; the current node memory + jmp unserialize.next_id +;}}} idx_insert_node ------------------------------------------------------------------------------- +align_nops +unserialize.epilog: + ; PERF -- START + TSC.SAMPLE tsc_nodes, stop + TSC.PRINT tsc_nodes + ; PERF -- STOP + jmp primgrp.parse.return_from_dense +;}}} unserialize ----------------------------------------------------------------------------------- +;{{{ keys_vals_cpy(unlikely) ---------------------------------------------------------------------- +; scratch ~ rax rcx r11 r9 xmm7 xmm6 xmm5 xmm4 +; from primgrp +;define flags r15 +;define granularity rbp ; f64 +;define strtbl_idx_p r8 +;define lat_of_lon_of xmm3 ; f64/f64 +; local from parse: +;define id_p r12 +;define id_p_finish r14 +;define lat_p r10 +;define lon_p rdx +;define keys_vals_p rsi ; can be 0 +; local from unserialize +;define node_p rbx +;define prev_id r13 +;define node_start_p rdi +;define granularity_xmm xmm2 +;define prev_lat_prev_lon xmm1 +;define one_one xmm0 +align_nops +keys_vals_cpy: + add node_p, nodes.node_t.keys_vals ; = key_dst_addr +align_nops +.next_key_val: + varint_load r11, keys_vals_p ; load/skip the int32 key_id/end marker + test r11, r11 ; int32 key_id == 0 ? + jz .epilog ; no more key=val for this node + mov r11, [strtbl_idx_p + 8 * r11] ; = key_src_addr + varint_load r9, r11 ; r9 = key_bytes_n, r11 = key_src_addr + mov [node_p], r9b ; store the byte of key_bytes_n at the start of the str + inc node_p + ; memcpy_dst = node_p(rbx), memcpy_src = key_src_addr(r11), memcpy_bytes_n = key_bytes_n(r9) + lea memcpy_link, [.key_cpy_done] ; where memcpy will jmp back + jmp memcpy ; node_p = node_p + key_bytes_n +align_nops +.key_cpy_done: + ; node_p = val_dst_addr + varint_load r11, keys_vals_p ; load/skip the int32 val_id + mov r11, [strtbl_idx_p + 8 * r11] ; = val_src_addr + varint_load r9, r11; r9 = val_bytes_n, r11 = val_src_addr + mov [node_p], r9w ; store the short of val_bytes_n at the start of the str + add node_p, 2 + ; memcpy_dst = node_p(rbx), memcpy_src = val_src_addr(r11), memcpy_bytes_n = val_bytes_n(r9) + lea memcpy_link, [.next_key_val] ; where memcpy will jmp back + jmp memcpy ; node_p = node_p + val_bytes_n +align_nops +.epilog: + mov byte [node_p], 0 ; insert the 0-sized key terminator + inc node_p ; node_p now points on the next node, aka current node finish byte + jmp unserialize.node_next_compute +purge one_one +purge prev_lat_prev_lon +purge granularity_xmm +purge node_start_p +purge prev_id +purge node_p +purge keys_vals_p +purge lon_p +purge lat_p +purge id_p_finish +purge id_p +purge lat_of_lon_of +purge strtbl_idx_p +purge strtbl_idx_p +purge granularity +purge flags +;}}} keys_vals_cpy --------------------------------------------------------------------------------- +;{{{ local memcpy(sse) ----------------------------------------------------------------------------- +; XXX: lddqu performs aligned reads, carefull where you use this, but should be fine with classic +; paginated memory. +; scratch ~ rax xmm7 xmm6 xmm5 xmm4 +align_nops +memcpy: + cmp memcpy_bytes_n, 16 * 4 + jb .below64 + ; since we can be cache line mis-aligned, speculatively do prefetch 2 cls ahead + prefetchnta [memcpy_src + 64 * 2] ; speculative non-temporal "3rd" cache line ahead + prefetchnta [memcpy_src + 64 * 3] ; speculative non-temporal "4th" cache line ahead +if ~SYSTEM_LEGACY + prefetchw [memcpy_dst + 64 * 2] + prefetchw [memcpy_dst + 64 * 3] +end if + lddqu xmm7, [memcpy_src + 16 * 0] + lddqu xmm6, [memcpy_src + 16 * 1] + lddqu xmm5, [memcpy_src + 16 * 2] + lddqu xmm4, [memcpy_src + 16 * 3] + + movdqu [memcpy_dst + 16 * 0], xmm7 + movdqu [memcpy_dst + 16 * 1], xmm6 + movdqu [memcpy_dst + 16 * 2], xmm5 + movdqu [memcpy_dst + 16 * 3], xmm4 + + add memcpy_dst, 16 * 4 + sub memcpy_bytes_n, 16 * 4 + jz .done + add memcpy_src, 16 * 4 + jmp memcpy +align_nops +.below64: + cmp memcpy_bytes_n, 16 * 3 + jb .below48 + + lddqu xmm7, [memcpy_src + 16 * 0] + lddqu xmm6, [memcpy_src + 16 * 1] + lddqu xmm5, [memcpy_src + 16 * 2] + + movdqu [memcpy_dst + 16 * 0], xmm7 + movdqu [memcpy_dst + 16 * 1], xmm6 + movdqu [memcpy_dst + 16 * 2], xmm5 + + add memcpy_dst, 16 * 3 + sub memcpy_bytes_n, 16 * 3 + jz .done + add memcpy_src, 16 * 3 + ; fall-thru +align_nops +.below48: + cmp memcpy_bytes_n, 16 * 2 + jb .below32 + + lddqu xmm7, [memcpy_src + 16 * 0] + lddqu xmm6, [memcpy_src + 16 * 1] + + movdqu [memcpy_dst + 16 * 0], xmm7 + movdqu [memcpy_dst + 16 * 1], xmm6 + + add memcpy_dst, 16 * 2 + sub memcpy_bytes_n, 16 * 2 + jz .done + add memcpy_src, 16 * 2 + ; fall-thru +align_nops +.below32: + cmp memcpy_bytes_n, 16 * 1 + jb .below16 + + lddqu xmm7, [memcpy_src + 16 * 0] + movdqu [memcpy_dst + 16 * 0], xmm7 + + add memcpy_dst, 16 * 1 + sub memcpy_bytes_n, 16 * 1 + jz .done + add memcpy_src, 16 * 1 + ; fall-thru +align_nops +.below16: + cmp memcpy_bytes_n, 8 * 1 + jb .below8 + mov rax, [memcpy_src + 8 * 0] + mov [memcpy_dst + 8 * 0], rax + + add memcpy_dst, 8 * 1 + sub memcpy_bytes_n, 8 * 1 + jz .done + add memcpy_src, 8 * 1 + ; fall-thru +align_nops +.below8: + cmp memcpy_bytes_n, 7 + je .cpy7 + cmp memcpy_bytes_n, 6 + je .cpy6 + cmp memcpy_bytes_n, 5 + je .cpy5 + cmp memcpy_bytes_n, 4 + je .cpy4 + cmp memcpy_bytes_n, 3 + je .cpy3 + cmp memcpy_bytes_n, 2 + je .cpy2 + cmp memcpy_bytes_n, 1 + je .cpy1 + jmp memcpy_link +align_nops +.cpy7: + mov al, [memcpy_src] + mov [memcpy_dst], al + inc memcpy_src + inc memcpy_dst +align_nops +.cpy6: + mov al, [memcpy_src] + mov [memcpy_dst], al + inc memcpy_src + inc memcpy_dst +align_nops +.cpy5: + mov al, [memcpy_src] + mov [memcpy_dst], al + inc memcpy_src + inc memcpy_dst +align_nops +.cpy4: + mov al, [memcpy_src] + mov [memcpy_dst], al + inc memcpy_src + inc memcpy_dst +align_nops +.cpy3: + mov al, [memcpy_src] + mov [memcpy_dst], al + inc memcpy_src + inc memcpy_dst +align_nops +.cpy2: + mov al, [memcpy_src] + mov [memcpy_dst], al + inc memcpy_src + inc memcpy_dst +align_nops +.cpy1: + mov al, [memcpy_src] + mov [memcpy_dst], al + inc memcpy_src + inc memcpy_dst +align_nops +.done: + jmp memcpy_link +;}}} local memcpy(sse) ----------------------------------------------------------------------------- +;=================================================================================================== +purge memcpy_dst +purge memcpy_src +purge memcpy_bytes_n +purge memcpy_link +;{{{ macros ---------------------------------------------------------------------------------------- +if TRACE_NODE_IDX_ENABLED + macro TRACE_NODE_IDX fmt, regs& + TRACE_PREFIX 'NODE_IDX', fmt, regs + end macro +else + macro TRACE_NODE_IDX fmt, regs& + end macro +end if +;}}} macros ---------------------------------------------------------------------------------------- +end namespace ; dense diff --git a/x86_64_sse2_x87/pbf/blk/primblk/primgrp/relations/bss.s b/x86_64_sse2_x87/pbf/blk/primblk/primgrp/relations/bss.s new file mode 100644 index 0000000..7092ace --- /dev/null +++ b/x86_64_sse2_x87/pbf/blk/primblk/primgrp/relations/bss.s @@ -0,0 +1,40 @@ +; vim: set filetype=fasm foldmethod=marker commentstring=;%s colorcolumn=101 : +namespace blk.primblk.primgrp.relations +align 64 +array rq 1 +array.finish rq 1 ; finish/next relation, inited to above array +n rq 1 ; how many relations in this pbf +array.idx rq 1 ; binary tree, bits from the relation id +array.idx.finish rq 1 ; next available idx slot +namespace array ;{{{ ------------------------------------------------------------------------------- + bytes_n = 8 * 1024 * 1024 * 1024 ; 8GiB + relation_t: namespace relation_t ;{{{ ------------------------------------------------------ + next := 0 ; ptr on the next relation (lookup for relation-only) + members := 8 ; qw ptr on the members + ; (key/val)s: + ; keys start with a byte containing their sz in bytes + ; vals start with a short containing their sz in bytes + ; the array is terminated with a 0-szed key + keys_vals := 16 + ; after the (key,val)s come the members + member_t: namespace member_t + type := 0 ; byte, 0xff for the terminator + ; XXX: the following field is actually filled with the decoded id first + ; then will be resolved to a ptr in a second pass using the various + ; idxs + p := 1 ; qw + role := 9 ; role starts with a byte containing its sz in bytes + end namespace + end namespace ;}}} relation_t -------------------------------------------------------------- +end namespace ;}}} array --------------------------------------------------------------------------- +namespace array.idx ;{{{ --------------------------------------------------------------------------- + slot_t: namespace slot_t + bit0 := 0 ; qw, ptr on idx slot for this bit + bit1 := 8 ; qw, idem + relation := 16 ; qw, ptr in the array for this id + bytes_n := 24 + end namespace + n = 1000 * 1000 * 1000 ; 1 billion relations + bytes_n = n * slot_t.bytes_n ; 24GB +end namespace ;}}} array.idx ----------------------------------------------------------------------- +end namespace diff --git a/x86_64_sse2_x87/pbf/blk/primblk/primgrp/relations/data.s b/x86_64_sse2_x87/pbf/blk/primblk/primgrp/relations/data.s new file mode 100644 index 0000000..17a2a6b --- /dev/null +++ b/x86_64_sse2_x87/pbf/blk/primblk/primgrp/relations/data.s @@ -0,0 +1,8 @@ +namespace blk.primblk.primgrp.relations + msg: namespace msg + mmap_failed db 'ERROR(%ld):PBF:PRIMBLK:PRIMGRP:RELATIONS:NODES:unable to mmap relations',10,0 + idx_mmap_failed db 'ERROR(%ld):PBF:PRIMBLK:PRIMGRP:RELATIONS:NODES:IDX:unable to mmap the index',10,0 + idx_relations_n db 'PBF:BLK:PRIMBLK:WAYS:INIT_ONCE:reserving a memory mapping for an index able to handle %lu relations',10,0 + end namespace +end namespace + diff --git a/x86_64_sse2_x87/pbf/blk/primblk/primgrp/relations/text.s b/x86_64_sse2_x87/pbf/blk/primblk/primgrp/relations/text.s new file mode 100644 index 0000000..a603d0d --- /dev/null +++ b/x86_64_sse2_x87/pbf/blk/primblk/primgrp/relations/text.s @@ -0,0 +1,660 @@ +; vim: set filetype=fasm foldmethod=marker commentstring=;%s colorcolumn=101 : +; XXX: We actually used this code unit as a training ground. Expect it to be very overkill. +relations: namespace relations +;{{{ init_once ------------------------------------------------------------------------------------- +align_nops +init_once: + TRACE 'PBF:BLK:PRIMBLK:PRIMGRP:RELATIONS:INIT_ONCE' + mov rbp, rsp + and rsp, not 0xf + + xor edi, edi + mov rsi, array.bytes_n + mov rdx, 1b or 10b ; PROT_READ | PROT_WRITE + mov r10, 0x22 ; MAP_PRIVATE | MAP_ANONYMOUS + xor r8d, r8d + xor r9d, r9d + mov rax, linux.mmap + syscall + cmp rax, linux.errno.last + jae .err_relations_mmap_failed + mov [array], rax + mov [array.finish], rax + + lea rdi, [msg.idx_relations_n] + mov rsi, array.idx.n + call qword [libc.printf] + + xor edi, edi + mov rsi, array.idx.bytes_n + mov rdx, 1b or 10b ; PROT_READ | PROT_WRITE + mov r10, 0x22 ; MAP_PRIVATE | MAP_ANONYMOUS + xor r8d, r8d + xor r9d, r9d + mov rax, linux.mmap + syscall + cmp rax, linux.errno.last + jae .err_relations_idx_mmap_failed + mov [array.idx], rax + add rax, 8 * 3 + mov [array.idx.finish], rax ; next availabe idx slot + + stc ; return true + mov rsp, rbp + ret +.err_relations_mmap_failed: + mov rdi, 2 ; stderr + mov rsi, rax ; errno + lea rsi, [msg.mmap_failed] + call qword [libc.dprintf] + clc ; return false + mov rsp, rbp + ret +.err_relations_idx_mmap_failed: + mov rdi, 2 ; stderr + mov rsi, rax ; errno + lea rsi, [msg.idx_mmap_failed] + call qword [libc.dprintf] + clc ; return false + mov rsp, rbp + ret +;}}} init_once ------------------------------------------------------------------------------------- +; local memcpy ABI +define memcpy_dst rbx ; memcpy_dst(out) = memcpy_dst(in) + memcpy_bytes_n +define memcpy_src r11 ; clobbered, don't care +define memcpy_bytes_n r9 ; clobbered, don't care +define memcpy_link rcx ; the return addr +;=================================================================================================== +;{{{ parse ----------------------------------------------------------------------------------------- +; scratch ~ rax rcx r11 r9 +; from primgrp +define relations_p rbx ; in +define relations_finish_p r13 ; in +define flags r15 ; in/out +define strtbl_idx_p r8 +define lat_of_lon_of xmm3 ; f64/f64 +; local +define id r12 +define keys_p r14 ; can be 0 +define keys_p_d r14d ; can be 0 +define keys_finish_p rbp +define vals_p r10 ; num of vals is num of keys +define memids_p rdx ; memids = MEMber IDs +define memids_p_d edx +define memids_finish_p rsi +define types_p rdi ; num of types is num of memids +define roles_p_xmm xmm2 ; the role of a member is a string +align_nops +parse: + xor keys_p_d, keys_p_d ; if no (key/val) + xor memids_p_d, memids_p_d ; presume we can have no memids (should not happen though) +align_nops +.next_msg_key: + ; "relationS" is plural because of the primgrp field name, but there is + ; actually only one "relation" which will be repeated + cmp relations_p, relations_finish_p + je unserialize + varint_load r11, relations_p ; msg key + mov r9, r11 ; = msg key copy + shr r11, 3 ; = field num + cmp r11, 1 + je .id_found + cmp r11, 2 + je .keys_found + cmp r11, 3 + je .vals_found + cmp r11, 8 + je .roles_found + cmp r11, 9 + je .memids_found + cmp r11, 10 + je .types_found + and r9, 111b ; field type + TRACE 'PBF:BLK:PRIMBLK:PRIMGRP:RELATIONS:MSG:FIELD %lu OF TYPE %lu', r11, r9 + val_skip relations_p, r9, r11 + jmp .next_msg_key +align_nops +.id_found: + varint_load id, relations_p + jmp .next_msg_key +align_nops +.keys_found: + varint_load r11, relations_p ; keys_sz + mov keys_p, relations_p + TRACE 'PBF:BLK:PRIMBLK:PRIMGRP:RELATIONS:KEYS:start = %p:size = %lu(0x%lx) bytes', relations_p, r11, r11 + add relations_p, r11 + mov keys_finish_p, relations_p + jmp .next_msg_key +align_nops +.vals_found: + varint_load r11, relations_p ; vals_sz + mov vals_p, relations_p + TRACE 'PBF:BLK:PRIMBLK:PRIMGRP:RELATIONS:VALS:start = %p:size = %lu(0x%lx) bytes', vals_p, r11, r11 + add relations_p, r11 + jmp .next_msg_key +align_nops +.memids_found: + varint_load r11, relations_p ; memids_sz + mov memids_p, relations_p + TRACE 'PBF:BLK:PRIMBLK:PRIMGRP:RELATIONS:MEMIDS:start = %p:size = %lu(0x%lx) bytes', memids_p, r11, r11 + add relations_p, r11 + mov memids_finish_p, relations_p + jmp .next_msg_key +align_nops +.types_found: + varint_load r11, relations_p ; types_sz + mov types_p, relations_p + TRACE 'PBF:BLK:PRIMBLK:PRIMGRP:RELATIONS:TYPES:start = %p:size = %lu(0x%lx) bytes', types_p, r11, r11 + add relations_p, r11 + jmp .next_msg_key +align_nops +.roles_found: + varint_load r11, relations_p ; roles_sz + TRACE 'PBF:BLK:PRIMBLK:PRIMGRP:RELATIONS:ROLES:start = %p:size = %lu(0x%lx) bytes', relations_p, r11, r11 + movq roles_p_xmm, relations_p + add relations_p, r11 + jmp .next_msg_key +purge relations_finish_p +purge relations_p +;}}} parse ----------------------------------------------------------------------------------------- +;{{{ unserialize ----------------------------------------------------------------------------------- +; scratch ~ rax rcx r11 r9 +; from primgrp +;define flags r15 ; in/out +;define strtbl_idx_p r8 +;define lat_of_lon_of xmm3 ; f64/f64 +; local from parse +;define id r12 +;define keys_p r14 ; can be 0 +;define keys_p_d r14d ; can be 0 +;define keys_finish_p rbp +;define vals_p r10 ; num of vals is num of keys +;define memids_p rdx ; memids = MEMber IDs +;define memids_p_d edx +;define memids_finish_p rsi +;define types_p rdi ; num of types is num of memids +;define roles_p_xmm xmm2 ; the role of a member is a string +; local +define relation_p rbx +define relation_start_p r13 +align_nops +unserialize: + mov relations_p, [array.finish] + mov relation_start_p, relations_p +;{{{ keys_val_cpy ---------------------------------------------------------------------------------- +keys_vals_cpy: + add relation_p, array.relation_t.keys_vals ; relation_p points on the start of the keys_vals section + test keys_p, keys_p + jz unserialize.keys_vals_done + jmp .entry +align_nops +.next_key_val: + cmp keys_p, keys_finish_p + je unserialize.keys_vals_done +.entry: + varint_load r11, keys_p ; = key_id, keys_p points on next key_id + mov r11, [strtbl_idx_p + 8 * r11] ; = key_src_addr + varint_load r9, r11 ; r9 = key_bytes_n, r11 = key_src_addr + mov [relation_p], r9b ; store the byte containing the sz of the key str in bytes + inc relation_p + ; memcpy_dst = relation_p(rbx), memcpy_src = key_src_addr(r11), memcpy_bytes_n = key_bytes_n(r9) + lea memcpy_link, [.key_cpy_done] + jmp memcpy ; relation_p = relation_p + key_bytes_n +align_nops +.key_cpy_done: + varint_load r11, vals_p ; = val_id, vals_p points on next val_id + mov r11, [strtbl_idx_p + 8 * r11] ; = val_src_addr + varint_load r9, r11; r9 = val_bytes_n, r11 = val_src_addr + mov [relation_p], r9w ; store the short containing the sz of the val str in bytes + add relation_p, 2 + ; memcpy_dst = relation_p(rbx), memcpy_src = val_src_addr(r11), memcpy_bytes_n = val_bytes_n(r9) + lea memcpy_link, [.next_key_val] + jmp memcpy ; relation_p = relation_p + key_bytes_n +;}}} keys_val_cpy ---------------------------------------------------------------------------------- +align_nops +unserialize.keys_vals_done: + mov byte [relation_p], 0 ; insert the 0-szed key/terminator + inc relation_p + ; here relation_p points on the start of the section of members + mov [relation_start_p + array.relation_t.members], relation_p + test memids_p, memids_p + jz unserialize.members_done ; unlikely +purge vals_p +purge keys_finish_p +purge keys_p_d +purge keys_p +;{{{ members --------------------------------------------------------------------------------------- +; scratch ~ rax rcx r11 r9 +; from primgrp +;define flags r15 ; in/out +;define strtbl_idx_p r8 +;define lat_of_lon_of xmm3 ; f64/f64 +; local from parse +;define id r12 +;define memids_p rdx ; memids = MEMber IDs +;define memids_p_d edx +;define memids_finish_p rsi +;define types_p rdi ; num of types is num of memids +;define roles_p_xmm xmm2 ; the role of a member is a string +; local from unserialize +;define relation_p rbx +;define relation_start_p r13 +; local +define roles_p r14 ; get it from xmm roles_p_xmm +define prev_memid rbp +define prev_memid_d ebp +define member_p relation_p ; alias +define member_p.p member_p + array.relation_t.member_t.p +define member_p.type member_p + array.relation_t.member_t.type +members: + movq roles_p, roles_p_xmm + xor prev_memid_d, prev_memid_d ; prev_id = 0 + jmp .entry +align_nops +.next_member: + cmp memids_p, memids_finish_p + je unserialize.members_done +.entry: + ; decode the relation id ------------------------------------------------------------------- + varint_load r11, memids_p ; load/skip the raw zigzag delta_refs + mov rax, r11 + shr r11, 1 ; i >> 1 + and rax, 1 ; i & 1 + neg rax ; -(i & 1) + xor r11, rax ; (i >> 1) ^ -(i & 1) ; = delta_id + add prev_memid, r11 ; = prev_memid = memid because DELTA encoded + mov [member_p.p], prev_memid ; store memid + ; decode the type -------------------------------------------------------------------------- + varint_load r11, types_p + mov byte [member_p.type], r11b ; a byte in enough + ; cpy the role str ------------------------------------------------------------------------- + add member_p, array.relation_t.member_t.role ; member_p points on the first byte of the role str + varint_load r11, roles_p ; = role_str_id + mov r11, [strtbl_idx_p + 8 * r11] ; role_src_addr + varint_load r9, r11 ; r9 = role_bytes_n, r11 = role_src_addr + mov [member_p], r9b ; insert the byte containing the sz of the role str in bytes + inc member_p + ; memcpy_dst = member_p(rbx), memcpy_src = role_src_addr(r11), memcpy_bytes_n = role_bytes_n(r9) + lea memcpy_link, [.next_member] + jmp memcpy ; member_p = member_p + role_bytes_n +purge member_p.type +purge member_p.p +purge member_p +purge prev_memid_d +purge prev_memid +purge roles_p +;}}} members --------------------------------------------------------------------------------------- +purge roles_p_xmm +purge memids_finish_p +purge memids_p_d +purge memids_p +purge types_p +unserialize.members_done: + ; here relation_p points on the terminating member (only a 0xff type and nothing else) + mov byte [relation_p], 0xff ; the terminator type is at the start of relation_p + inc relation_p ; = next relation + mov [relation_start_p + array.relation_t.next], relation_p + mov [array.finish], relation_p +purge relation_p +;{{{ idx_insert_node ------------------------------------------------------------------------------- +; scratch ~ rax +; from primgrp +;define flags r15 ; in/out +;define strtbl_idx_p r8 +;define lat_of_lon_of xmm3 ; f64/f64 +; local from parse +;define id r12 +; local from unserialize +;define relation_start_p r13 +; local +define idx_finish_p rbx +define id_bit_idx r14 +define id_bit_idx_d r14d +define bit_val_select rcx ; we use rcx here because we may use rcx compressed instructions +define bit_val_select_d ecx +define bit_val_select_b cl +define id_bit_idx_msb rbp +define idx_slot_p r9 +; TODO:REMOVE +;define idx_slot_p.relation idx_slot_p + array.idx.slot_t.relation +idx_insert_node: + mov idx_slot_p, [array.idx] + mov idx_finish_p, [array.idx.finish] + TRACE_RELATIONS_IDX 'id=%#016lx nodes.idx=%p nodes.finish=%p', id, idx_slot_p, idx_finish_p + xor id_bit_idx_d, id_bit_idx_d + xor bit_val_select_d, bit_val_select_d + mov id_bit_idx_msb, -1 ; this is to make it work with id = 0 + bsr id_bit_idx_msb, id ; if id = 0, id_bit_idx_msb is untouched namely -1 + TRACE_RELATIONS_IDX 'id_bit_idx_msb=%lu', id_bit_idx_msb +align_nops +.next_id_bit: + TRACE_RELATIONS_IDX 'id_bit_idx=%lu', id_bit_idx + cmp id_bit_idx_msb, id_bit_idx + jb .insert_here + bt id, id_bit_idx + setc bit_val_select_b ; bit_val_select = 1 if id[id_bit_idx] = 1, else 0 + TRACE_RELATIONS_IDX 'bit_val_select=%lu', bit_val_select + mov rax, [idx_slot_p + 8 * bit_val_select] + TRACE_RELATIONS_IDX 'slot=%p bit slot=%p', idx_slot_p, rax + test rax, rax + jnz .idx_existing_slot + TRACE_RELATIONS_IDX 'non existing slot using finish=%p', idx_finish_p + mov rax, idx_finish_p + add idx_finish_p, array.idx.slot_t.bytes_n ; we could zero the mem here, but mmap does it for us +if ~SYSTEM_LEGACY + prefetchw [idx_finish_p + 64 * 2] ; try to get ready + prefetchw [idx_finish_p + 64 * 3] +end if + mov [idx_slot_p + 8 * bit_val_select], rax +align_nops +.idx_existing_slot: + mov idx_slot_p, rax + TRACE_RELATIONS_IDX 'next_slot=%p', idx_slot_p + inc id_bit_idx + + jmp .next_id_bit +align_nops +.insert_here: +if TRACE_RELATIONS_IDX_ENABLED + mov rax, [idx_slot_p + array.idx.slot_t.relation] + test rax, rax + jz .relation_slot_available + TRACE_RELATIONS_IDX 'WARNING: overwritting an existing relation=%p', rax +.relation_slot_available: + TRACE_RELATIONS_IDX 'inserting relation=%p in slot=%p', relation_start_p, idx_slot_p +end if + mov [idx_slot_p + array.idx.slot_t.relation], relation_start_p + ; XXX: if one day we need to be backed by a file we will need to DONT_NEED "madvise" the + mov [array.idx.finish], idx_finish_p + ; the current relation memory +purge idx_slot_p +purge id_bit_idx_msb +purge bit_val_select_b +purge bit_val_select_d +purge bit_val_select +purge id_bit_idx_d +purge id_bit_idx +purge idx_finish_p +;}}} idx_insert_node ------------------------------------------------------------------------------- +purge relation_start_p +purge id +purge lat_of_lon_of +purge strtbl_idx_p +purge flags + jmp primgrp.parse.return_from_relations +;}}} unserialize ----------------------------------------------------------------------------------- +;{{{ local memcpy(sse) ----------------------------------------------------------------------------- +; XXX: lddqu performs aligned reads, carefull where you use this, but should be fine with classic +; paginated memory. +; scratch ~ rax xmm7 xmm6 xmm5 xmm4 +align_nops +memcpy: + cmp memcpy_bytes_n, 16 * 4 + jb .below64 + ; since we can be cache line mis-aligned, speculatively do prefetch 2 cls ahead + prefetchnta [memcpy_src + 64 * 2] ; speculative non-temporal "3rd" cache line ahead + prefetchnta [memcpy_src + 64 * 3] ; speculative non-temporal "4th" cache line ahead +if ~SYSTEM_LEGACY + prefetchw [memcpy_dst + 64 * 2] + prefetchw [memcpy_dst + 64 * 3] +end if + lddqu xmm7, [memcpy_src + 16 * 0] + lddqu xmm6, [memcpy_src + 16 * 1] + lddqu xmm5, [memcpy_src + 16 * 2] + lddqu xmm4, [memcpy_src + 16 * 3] + + movdqu [memcpy_dst + 16 * 0], xmm7 + movdqu [memcpy_dst + 16 * 1], xmm6 + movdqu [memcpy_dst + 16 * 2], xmm5 + movdqu [memcpy_dst + 16 * 3], xmm4 + + add memcpy_dst, 16 * 4 + sub memcpy_bytes_n, 16 * 4 + jz .done + add memcpy_src, 16 * 4 + jmp memcpy +align_nops +.below64: + cmp memcpy_bytes_n, 16 * 3 + jb .below48 + + lddqu xmm7, [memcpy_src + 16 * 0] + lddqu xmm6, [memcpy_src + 16 * 1] + lddqu xmm5, [memcpy_src + 16 * 2] + + movdqu [memcpy_dst + 16 * 0], xmm7 + movdqu [memcpy_dst + 16 * 1], xmm6 + movdqu [memcpy_dst + 16 * 2], xmm5 + + add memcpy_dst, 16 * 3 + sub memcpy_bytes_n, 16 * 3 + jz .done + add memcpy_src, 16 * 3 + ; fall-thru +align_nops +.below48: + cmp memcpy_bytes_n, 16 * 2 + jb .below32 + + lddqu xmm7, [memcpy_src + 16 * 0] + lddqu xmm6, [memcpy_src + 16 * 1] + + movdqu [memcpy_dst + 16 * 0], xmm7 + movdqu [memcpy_dst + 16 * 1], xmm6 + + add memcpy_dst, 16 * 2 + sub memcpy_bytes_n, 16 * 2 + jz .done + add memcpy_src, 16 * 2 + ; fall-thru +align_nops +.below32: + cmp memcpy_bytes_n, 16 * 1 + jb .below16 + + lddqu xmm7, [memcpy_src + 16 * 0] + movdqu [memcpy_dst + 16 * 0], xmm7 + + add memcpy_dst, 16 * 1 + sub memcpy_bytes_n, 16 * 1 + jz .done + add memcpy_src, 16 * 1 + ; fall-thru +align_nops +.below16: + cmp memcpy_bytes_n, 8 * 1 + jb .below8 + mov rax, [memcpy_src + 8 * 0] + mov [memcpy_dst + 8 * 0], rax + + add memcpy_dst, 8 * 1 + sub memcpy_bytes_n, 8 * 1 + jz .done + add memcpy_src, 8 * 1 + ; fall-thru +align_nops +.below8: + cmp memcpy_bytes_n, 7 + je .cpy7 + cmp memcpy_bytes_n, 6 + je .cpy6 + cmp memcpy_bytes_n, 5 + je .cpy5 + cmp memcpy_bytes_n, 4 + je .cpy4 + cmp memcpy_bytes_n, 3 + je .cpy3 + cmp memcpy_bytes_n, 2 + je .cpy2 + cmp memcpy_bytes_n, 1 + je .cpy1 + jmp memcpy_link +align_nops +.cpy7: + mov al, [memcpy_src] + mov [memcpy_dst], al + inc memcpy_src + inc memcpy_dst +align_nops +.cpy6: + mov al, [memcpy_src] + mov [memcpy_dst], al + inc memcpy_src + inc memcpy_dst +align_nops +.cpy5: + mov al, [memcpy_src] + mov [memcpy_dst], al + inc memcpy_src + inc memcpy_dst +align_nops +.cpy4: + mov al, [memcpy_src] + mov [memcpy_dst], al + inc memcpy_src + inc memcpy_dst +align_nops +.cpy3: + mov al, [memcpy_src] + mov [memcpy_dst], al + inc memcpy_src + inc memcpy_dst +align_nops +.cpy2: + mov al, [memcpy_src] + mov [memcpy_dst], al + inc memcpy_src + inc memcpy_dst +align_nops +.cpy1: + mov al, [memcpy_src] + mov [memcpy_dst], al + inc memcpy_src + inc memcpy_dst +align_nops +.done: + jmp memcpy_link +;}}} local memcpy(sse) ----------------------------------------------------------------------------- +; {{{ resolve_ids ---------------------------------------------------------------------------------- +; from pbf.tile_db_build +define flags r15 +; local +define relation_p rbx +define relations_finish_p r12 +define member_p r13 +define nodes_idx r14 +define ways_idx rbp +define relations_idx r11 +define id r10 +define bit_val_select r9 +define bit_val_select_b r9b +define id_bit_idx_msb rcx ; we use rcx for compressed opcodes +define id_bit_idx_msb_d ecx +define idx_slot_p r8 +align_nops +resolve_ids: + mov relation_p, [array] + mov relations_finish_p, [array.finish] + mov nodes_idx, [primgrp.dense.nodes.idx] + mov ways_idx, [primgrp.ways.array.idx] + mov relations_idx, [primgrp.relations.array.idx] +align_nops +.next_relation: + TRACE_2ND_PASS 'relation=%p relation_finish_p=%p', relation_p, relations_finish_p + cmp relation_p, relations_finish_p + je pbf.tile_db_build.features_select_pass ; prep 3rd pass + mov member_p, [relation_p + array.relation_t.members] +align_nops +.next_member: + mov al, [member_p + array.relation_t.member_t.type] + ; this field actually contains the id after the first major pass + mov id, [member_p + array.relation_t.member_t.p] + TRACE_2ND_PASS 'member=%p type=%#2lx(lowest byte only) id=%#lx(invalid if type=0xff)', member_p, rax, id + cmp al, 0xff + jne .have_more_members + mov relation_p, [relation_p + array.relation_t.next] + TRACE_2ND_PASS 'no more members next relation=%p', relation_p + jmp .next_relation +align_nops +.have_more_members: + cmp al, 0x00 + cmove idx_slot_p, nodes_idx + je .idx_selected + cmp al, 0x01 + cmove idx_slot_p, ways_idx + je .idx_selected + cmp al, 0x02 + cmove idx_slot_p, relations_idx +.idx_selected: + xor bit_val_select, bit_val_select ; bit_val_select = 0 + mov id_bit_idx_msb, -1 ; this is to make it work with ref = 0 + bsr id_bit_idx_msb, id ; if id = 0, ref_bit_idx_msb is untouched namely -1 + inc id_bit_idx_msb_d ; store the idx + 1, 32bits because of the following jecxz +align_nops +.next_id_bit: + jecxz .idx_slot_lookup_done ; bit_idx + 1 is stored in cx then 0 means we are finished + bt id, 0 + setc bit_val_select_b + mov idx_slot_p, [idx_slot_p + 8 * bit_val_select] + test idx_slot_p, idx_slot_p ; if the idx slot in 0, we have a missing node + jz .missing_node ; unlikely +if ~SYSTEM_LEGACY + prefetcht0 [idx_slot_p] +end if + shr id, 1 + dec id_bit_idx_msb_d + jmp .next_id_bit +align_nops +.idx_slot_lookup_done: + mov rax, [idx_slot_p + 2 * 8] ; XXX: all 3 idxs (nodes/ways/relations) have the same struct + mov [member_p + array.relation_t.member_t.p], rax + TRACE_2ND_PASS 'id found in idx, replacing with pointer %p', rax +.skip_member_role: ; from unlikely code code below + add member_p, array.relation_t.member_t.role + 1 ; points on the byte right after the byte containing the sz of the role str in bytes + movzx rax, byte [member_p - 1] + add member_p, rax + jmp .next_member + +.missing_node: ; unlikely + mov qword [member_p + array.relation_t.member_t.p], 0 + TRACE_2ND_PASS 'missing id, pointer to 0' + jmp .skip_member_role +purge idx_slot_p +purge id_bit_idx_msb_d +purge id_bit_idx_msb +purge bit_val_select_b +purge bit_val_select +purge id +purge relations_idx +purge ways_idx +purge nodes_idx +purge member_p +purge relations_finish_p +purge relation_p +purge flags +; }}} resolve_ids ---------------------------------------------------------------------------------- +;=================================================================================================== +purge memcpy_link +purge memcpy_bytes_n +purge memcpy_src +purge memcpy_dst +;{{{ macros ---------------------------------------------------------------------------------------- +if TRACE_RELATIONS_IDX_ENABLED + macro TRACE_RELATIONS_IDX fmt, regs& + TRACE_PREFIX 'RELATIONS_IDX', fmt, regs + end macro +else + macro TRACE_RELATIONS_IDX fmt, regs& + end macro +end if +;--------------------------------------------------------------------------------------------------- +if TRACE_RELATIONS_2ND_PASS_ENABLED + macro TRACE_2ND_PASS fmt, regs& + TRACE_PREFIX 'RELATIONS_2ND_PASS', fmt, regs + end macro +else + macro TRACE_2ND_PASS fmt, regs& + end macro +end if +;}}} macros ---------------------------------------------------------------------------------------- +end namespace ; relations diff --git a/x86_64_sse2_x87/pbf/blk/primblk/primgrp/text.s b/x86_64_sse2_x87/pbf/blk/primblk/primgrp/text.s new file mode 100644 index 0000000..1ec85a1 --- /dev/null +++ b/x86_64_sse2_x87/pbf/blk/primblk/primgrp/text.s @@ -0,0 +1,156 @@ +; vim: set filetype=fasm foldmethod=marker commentstring=;%s colorcolumn=101 : +primgrp: namespace primgrp +align_nops +init_once: + TRACE 'PBF:BLK:PRIMBLK:PRIMGRP:INIT_ONCE' + call dense.init_once + jnc .nok + call ways.init_once + jnc .nok + call relations.init_once + jnc .nok + stc ; return true + ret +.nok: + clc ; return false + ret +;=================================================================================================== +; scratch: rax rcx r11 r9 xmm7 xmm6 xmm5 xmm4 +; from primblk +define primblk_p rbx + define dense_p rbx + define ways_p rbx + define relations_p rbx +define primblk_p_finish r12 +define flags r15 +define p r13 + define dense_p_finish r13 + define ways_p_finish r13 + define relations_finish_p r13 +define p_finish r14 +define granularity rbp ; f64 +define strtbl_idx_p r8 +define lat_of_lon_of xmm3 ; f64/f64 +align_nops +parse: +.next_key: + cmp p, p_finish + je primblk.parse.next_key + varint_load r11, p ; key + mov r9, r11 ; = key copy + shr r11, 3 ; = field num + + cmp r11, 1 + je .nodes_found + cmp r11, 2 + je .dense_found + cmp r11, 3 + je .ways_found + cmp r11, 4 + je .relations_found + and r9, 111b ; = field type + + TRACE 'PBF:BLK:PRIMBLK:PRIMGRP:UNHANDLE FIELD type=%lu num=%lu', r9, r11 + val_skip p, r9, r11 + jmp .next_key +align_nops +.nodes_found: + varint_load r11, p ; nodes_sz + TRACE 'PBF:BLK:PRIMBLK:PRIMGRP:NODES:start=%p:size=%lu(0x%lx) bytes', p, r11, r11 + add p, r11 ; skip + ; XXX: NODES ARE PROBABLY DEPRECATED IN FAVOR OF DENSE + jmp .next_key +align_nops +.dense_found: + varint_load r11, p ; dense_sz + mov r9, p ; = dense_p + TRACE 'PBF:BLK:PRIMBLK:PRIMGRP:DENSE:start=%p:size=%lu(0x%lx) bytes', r9, r11, r11 + add p, r11 ; = dense_p_finish + ; make some reg room for dense parsing -- START + sub rsp, 8 * 4 + mov [rsp + 8 * 0], primblk_p + mov [rsp + 8 * 1], primblk_p_finish + mov [rsp + 8 * 2], p + mov [rsp + 8 * 3], p_finish + ; make some reg room for dense parsing -- END + mov dense_p, r9 + jmp dense.parse ; return will happen in .restore_from_dense +align_nops +.return_from_dense: + mov primblk_p, [rsp + 8 * 0] + mov primblk_p_finish, [rsp + 8 * 1] + mov p, [rsp + 8 * 2] + mov p_finish, [rsp + 8 * 3] + add rsp, 8 * 4 + jmp .next_key +align_nops +.ways_found: + inc qword [ways.n] + varint_load r11, p ; ways_sz + mov r9, p ; ways_p + TRACE 'PBF:BLK:PRIMBLK:PRIMGRP:WAYS:start=%p:size=%lu(0x%lx) bytes', r9, r11, r11 + add p, r11 ; = ways_p_finish + ; make some reg room for ways parsing -- START + sub rsp, 8 * 5 + mov [rsp + 8 * 0], primblk_p + mov [rsp + 8 * 1], primblk_p_finish + mov [rsp + 8 * 2], p + mov [rsp + 8 * 3], p_finish + mov [rsp + 8 * 4], granularity + ; make some reg room for ways parsing -- END + mov ways_p, r9 + jmp ways.parse ; return will happen below in .return_from_ways +align_nops +.return_from_ways: + mov primblk_p, [rsp + 8 * 0] + mov primblk_p_finish, [rsp + 8 * 1] + mov p, [rsp + 8 * 2] + mov p_finish, [rsp + 8 * 3] + mov granularity, [rsp + 8 * 4] + add rsp, 8 * 5 + jmp .next_key +align_nops +.relations_found: + inc qword [relations.n] + varint_load r11, p ; relations_sz + mov r9, p ; relations_p + TRACE 'PBF:BLK:PRIMBLK:PRIMGRP:RELATIONS:start=%p:size=%lu(0x%lx) bytes', p, r11, r11 + add p, r11 ; = relations_finish_p + ; make some reg room for relations parsing -- START + sub rsp, 8 * 5 + mov [rsp + 8 * 0], primblk_p + mov [rsp + 8 * 1], primblk_p_finish + mov [rsp + 8 * 2], p + mov [rsp + 8 * 3], p_finish + mov [rsp + 8 * 4], granularity + ; make some reg room for relations parsing -- END + mov relations_p, r9 + jmp relations.parse +align_nops +.return_from_relations: + mov primblk_p, [rsp + 8 * 0] + mov primblk_p_finish, [rsp + 8 * 1] + mov p, [rsp + 8 * 2] + mov p_finish, [rsp + 8 * 3] + mov granularity, [rsp + 8 * 4] + add rsp, 8 * 5 + jmp .next_key +;--------------------------------------------------------------------------------------------------- +purge primblk_p +purge dense_p +purge ways_p +purge primblk_p_finish +purge flags +purge p +purge dense_p_finish +purge ways_p_finish +purge p_finish +purge granularity +purge strtbl_idx_p +purge lat_of_lon_of +;=================================================================================================== +include 'dense/text.s' +include 'ways/text.s' +include 'relations/text.s' +;=================================================================================================== +end namespace ; primgrp diff --git a/x86_64_sse2_x87/pbf/blk/primblk/primgrp/ways/bss.s b/x86_64_sse2_x87/pbf/blk/primblk/primgrp/ways/bss.s new file mode 100644 index 0000000..b44f1ed --- /dev/null +++ b/x86_64_sse2_x87/pbf/blk/primblk/primgrp/ways/bss.s @@ -0,0 +1,33 @@ +; vim: set filetype=fasm foldmethod=marker commentstring=;%s colorcolumn=101 : +namespace blk.primblk.primgrp.ways +align 64 +array rq 1 +array.finish rq 1 ; finish way, inited to above array +n rq 1 ; how many ways in this pbf +array.idx rq 1 ; binary tree, bits from the way id +array.idx.finish rq 1 ; next available idx slot +namespace array ;{{{ ------------------------------------------------------------------------------- + bytes_n = 16 * 1024 * 1024 * 1024 ; 16GiB + way_t: namespace way_t ;{{{ ---------------------------------------------------------------- + next := 0 ; qw, ptr on next way (lookup for way-only features) + nodes := 8 ; qw ptr on the array of ptrs of nodes after the (key/val)s + ; (key/val)s: + ; keys start with a byte containing their sz in bytes + ; vals start with a short containing their sz in bytes + ; the array is terminated with a 0-szed key + keys_vals := 16 + ; nodes: following are refs as ptrs of nodes, pointed by the above nodes and + ; finished by the way next ptr + end namespace ; }}} way_t ------------------------------------------------------------------ +end namespace ;}}} array --------------------------------------------------------------------------- +namespace array.idx ;{{{ --------------------------------------------------------------------------- + slot_t: namespace slot_t + bit0 := 0 ; qw, ptr on idx slot for this bit + bit1 := 8 ; qw, idem + way := 16 ; qw, ptr in the array for this id + bytes_n := 24 + end namespace + n = 1000 * 1000 * 1000 ; 1 billion ways + bytes_n = n * slot_t.bytes_n ; 24GB +end namespace ;}}} array.idx ----------------------------------------------------------------------- +end namespace diff --git a/x86_64_sse2_x87/pbf/blk/primblk/primgrp/ways/data.s b/x86_64_sse2_x87/pbf/blk/primblk/primgrp/ways/data.s new file mode 100644 index 0000000..18f0503 --- /dev/null +++ b/x86_64_sse2_x87/pbf/blk/primblk/primgrp/ways/data.s @@ -0,0 +1,7 @@ +namespace blk.primblk.primgrp.ways + msg: namespace msg + mmap_failed db 'ERROR(%ld):PBF:PRIMBLK:PRIMGRP:WAYS:NODES:unable to mmap ways',10,0 + idx_mmap_failed db 'ERROR(%ld):PBF:PRIMBLK:PRIMGRP:WAYS:NODES:IDX:unable to mmap the index',10,0 + idx_ways_n db 'PBF:BLK:PRIMBLK:WAYS:INIT_ONCE:reserving a memory mapping for an index able to handle %lu ways',10,0 + end namespace +end namespace diff --git a/x86_64_sse2_x87/pbf/blk/primblk/primgrp/ways/text.s b/x86_64_sse2_x87/pbf/blk/primblk/primgrp/ways/text.s new file mode 100644 index 0000000..3be820f --- /dev/null +++ b/x86_64_sse2_x87/pbf/blk/primblk/primgrp/ways/text.s @@ -0,0 +1,544 @@ +; vim: set filetype=fasm foldmethod=marker commentstring=;%s colorcolumn=101 : +; XXX: We actually used this code unit as a training ground. Expect it to be very overkill. +ways: namespace ways +;{{{ init_once ------------------------------------------------------------------------------------- +align_nops +init_once: + TRACE 'PBF:BLK:PRIMBLK:PRIMGRP:WAYS:INIT_ONCE' + mov rbp, rsp + and rsp, not 0xf + + xor edi, edi + mov rsi, array.bytes_n + mov rdx, 1b or 10b ; PROT_READ | PROT_WRITE + mov r10, 0x22 ; MAP_PRIVATE | MAP_ANONYMOUS + xor r8d, r8d + xor r9d, r9d + mov rax, linux.mmap + syscall + cmp rax, linux.errno.last + jae .err_ways_mmap_failed + mov [array], rax + mov [array.finish], rax + + lea rdi, [msg.idx_ways_n] + mov rsi, array.idx.n + call qword [libc.printf] + + xor edi, edi + mov rsi, array.idx.bytes_n + mov rdx, 1b or 10b ; PROT_READ | PROT_WRITE + mov r10, 0x22 ; MAP_PRIVATE | MAP_ANONYMOUS + xor r8d, r8d + xor r9d, r9d + mov rax, linux.mmap + syscall + cmp rax, linux.errno.last + jae .err_ways_idx_mmap_failed + mov [array.idx], rax + add rax, 8 * 3 + mov [array.idx.finish], rax ; next availabe idx slot + + stc ; return true + mov rsp, rbp + ret +.err_ways_mmap_failed: + mov rdi, 2 ; stderr + mov rsi, rax ; errno + lea rsi, [msg.mmap_failed] + call qword [libc.dprintf] + clc ; return false + mov rsp, rbp + ret +.err_ways_idx_mmap_failed: + mov rdi, 2 ; stderr + mov rsi, rax ; errno + lea rsi, [msg.idx_mmap_failed] + call qword [libc.dprintf] + clc ; return false + mov rsp, rbp + ret +;}}} init_once ------------------------------------------------------------------------------------- +; local memcpy ABI +define memcpy_dst rbx ; memcpy_dst(out) = memcpy_dst(in) + memcpy_bytes_n +define memcpy_src r11 ; clobbered, don't care +define memcpy_bytes_n r9 ; clobbered, don't care +define memcpy_link rcx ; the return addr +;=================================================================================================== +;{{{ parse ----------------------------------------------------------------------------------------- +; scratch ~ rax rcx r11 r9 +; from primgrp +define ways_p rbx ; in +define ways_p_finish r13 ; in +define flags r15 ; in/out +define strtbl_idx_p r8 +define lat_of_lon_of xmm3 ; f64/f64 +; local +define id r12 +define keys_p r14 ; can be 0 +define keys_p_d r14d ; can be 0 +define keys_p_finish rbp +define vals_p r10 ; num of vals is num of keys +define refs_p rdx ; presume it can be 0 +define refs_p_d edx ; presume it can be 0 +define refs_finish_p rsi +align_nops +parse: + xor keys_p_d, keys_p_d ; if no (key/val) + xor refs_p_d, refs_p_d ; presume we can have no refs (should not happen though) +align_nops +.next_msg_key: + ; "wayS" is plural because of the primgrp field name, but there is actually only one "way" + ; which will be repeated + cmp ways_p, ways_p_finish + je unserialize + varint_load r11, ways_p ; msg key + mov r9, r11 ; = msg key copy + shr r11, 3 ; = field num + cmp r11, 1 + je .id_found + cmp r11, 2 + je .keys_found + cmp r11, 3 + je .vals_found + cmp r11, 8 + je .refs_found + and r9, 111b ; field type + TRACE 'PBF:BLK:PRIMBLK:PRIMGRP:WAYS:MSG:FIELD %lu OF TYPE %lu', r11, r9 + val_skip ways_p, r9, r11 + jmp .next_msg_key +align_nops +.id_found: + varint_load id, ways_p + jmp .next_msg_key +align_nops +.keys_found: + varint_load r11, ways_p ; keys_sz + mov keys_p, ways_p + TRACE 'PBF:BLK:PRIMBLK:PRIMGRP:WAYS:KEYS:start = %p:size = %lu(0x%lx) bytes', keys_p, r11, r11 + add ways_p, r11 + mov keys_p_finish, ways_p + jmp .next_msg_key +align_nops +.vals_found: + varint_load r11, ways_p ; vals_sz + mov vals_p, ways_p + TRACE 'PBF:BLK:PRIMBLK:PRIMGRP:WAYS:VALS:start = %p:size = %lu(0x%lx) bytes', vals_p, r11, r11 + add ways_p, r11 + jmp .next_msg_key +align_nops +.refs_found: + varint_load r11, ways_p ; refs_sz + mov refs_p, ways_p + TRACE 'PBF:BLK:PRIMBLK:PRIMGRP:WAYS:REFS:start = %p:size = %lu(0x%lx) bytes', refs_p, r11, r11 + add ways_p, r11 + mov refs_finish_p, ways_p + jmp .next_msg_key +purge ways_p_finish +purge ways_p +;}}} parse ----------------------------------------------------------------------------------------- +;{{{ unserialize ----------------------------------------------------------------------------------- +; scratch ~ rax rcx r11 r9 +; from primgrp +;define flags r15 ; in/out +;define strtbl_idx_p r8 +;define lat_of_lon_of xmm3 ; f64/f64 +; local from parse +;define id r12 +;define keys_p r14 ; can be 0 +;define keys_p_d r14d ; can be 0 +;define keys_p_finish rbp +;define vals_p r10 ; num of vals is num of keys +;define refs_p rdx ; presume it can be 0 +;define refs_p_d edx ; presume it can be 0 +;define refs_finish_p rsi +; local +define way_p rbx +define way_start_p r13 +align_nops +unserialize: + mov way_p, [array.finish] + mov way_start_p, way_p ; save the start of the way since are going to modify way_p + +;{{{ keys_vals_cpy ---------------------------------------------------------------------------------- +keys_vals_cpy: + add way_p, array.way_t.keys_vals ; way_p points on the start of the keys_vals section + test keys_p, keys_p + jz unserialize.keys_vals_done + jmp .entry +align_nops +.next_key_val: + cmp keys_p, keys_p_finish + je unserialize.keys_vals_done +.entry: + varint_load r11, keys_p ; = key_id, keys_p points on next key_id + mov r11, [strtbl_idx_p + 8 * r11] ; = key_src_addr + varint_load r9, r11 ; r9 = key_bytes_n, r11 = key_src_addr + mov [way_p], r9b ; insert the byte containing the sz of the key str in bytes + inc way_p + ; memcpy_dst = way_p(rbx), memcpy_src = key_src_addr(r11), memcpy_bytes_n = key_bytes_n(r9) + lea memcpy_link, [.key_cpy_done] ; where memcpy will jmp back + jmp memcpy ; way_p = way_p + key_bytes_n +align_nops +.key_cpy_done: + varint_load r11, vals_p ; = val_id, vals_p points on next val_id + mov r11, [strtbl_idx_p + 8 * r11] ; = val_src_addr + varint_load r9, r11; r9 = val_bytes_n, r11 = val_src_addr + mov [way_p], r9w ; insert the short containing the sz of the val str in bytes + add way_p, 2 + ; memcpy_dst = way_p(rbx), memcpy_src = val_src_addr(r11), memcpy_bytes_n = val_bytes_n(r9) + lea memcpy_link, [.next_key_val] ; where memcpy will jmp back + jmp memcpy ; way_p = way_p + val_bytes_n +;}}} keys_val_cpy ---------------------------------------------------------------------------------- +align_nops +unserialize.keys_vals_done: + mov byte [way_p], 0 ; insert the 0-szed key/terminator + inc way_p ; here way_p points on the start of the section of ptrs on nodes + mov [way_start_p + array.way_t.nodes], way_p + test refs_p, refs_p + jz unserialize.refs_solved ; unlikely +purge vals_p +purge keys_p_finish +purge keys_p_d +purge keys_p +;{{{ refs_solve ------------------------------------------------------------------------------------ +; XXX: it is expected to have missing nodes in ways-->do 0 their ptrs +; scratch ~ rax rcx r11 +; from primgrp +;define flags r15 ; in/out +;define strtbl_idx_p r8 +;define lat_of_lon_of xmm3 ; f64/f64 +; local from parse +;define id r12 +;define refs_p rdx ; presume it can be 0 +;define refs_p_d edx ; presume it can be 0 +;define refs_finish_p rsi +; local from unserialize +;define way_p rbx +;define way_start_p r13 +; local +define prev_ref r14 +define prev_ref_d r14d +define ref rbp +define bit_val_select r10 +define bit_val_select_b r10b +define ref_bit_idx_msb rcx +define ref_bit_idx_msb_d ecx +define idx_slot_p r9 +refs_solve: + xor prev_ref_d, prev_ref_d ; prev_ref = 0 +align_nops +.next_ref: + ; decode the id ---------------------------------------------------------------------------- + varint_load r11, refs_p ; load/skip the raw zigzag delta_refs + mov rax, r11 + shr r11, 1 ; i >> 1 + and rax, 1 ; i & 1 + neg rax ; -(i & 1) + xor r11, rax ; (i >> 1) ^ -(i & 1) ; = delta_id + add prev_ref, r11 ; = prev_ref = ref + mov ref, prev_ref + ; lookup the node ptr in the nodes idx ----------------------------------------------------- + xor bit_val_select, bit_val_select ; bit_val_select = 0 + mov idx_slot_p, [primgrp.dense.nodes.idx] + mov ref_bit_idx_msb, -1 ; this is to make it work with ref = 0 + bsr ref_bit_idx_msb, ref ; if ref = 0, ref_bit_idx_msb is untouched namely -1 + inc ref_bit_idx_msb_d ; store the idx + 1, 32bits because of the following jecxz +align_nops +.next_ref_bit: + jecxz .idx_slot_lookup_done ; bit_idx + 1 is stored in cx + bt ref, 0 + setc bit_val_select_b + mov idx_slot_p, [idx_slot_p + 8 * bit_val_select] + test idx_slot_p, idx_slot_p ; if the idx slot in 0, we have a missing node + jz .missing_slot_node ; unlikely +if ~SYSTEM_LEGACY + prefetcht0 [idx_slot_p] +end if + shr ref, 1 + dec ref_bit_idx_msb_d + jmp .next_ref_bit +.missing_slot_node: ; unlikely + mov qword [way_p], 0 + jmp .next_way_node_ptr +align_nops +.idx_slot_lookup_done: + mov rax, [idx_slot_p + dense.nodes.idx.slot_t.node] ; XXX: can be 0 + mov [way_p], rax +.next_way_node_ptr: ; unlikely + add way_p, 8 ; points on way storage for next node ptr + cmp refs_p, refs_finish_p + jne .next_ref +purge idx_slot_p +purge ref_bit_idx_msb_d +purge ref_bit_idx_msb +purge bit_val_select_b +purge bit_val_select +purge ref +purge prev_ref_d +purge prev_ref +;}}} refs_solve ------------------------------------------------------------------------------------ +unserialize.refs_solved: ; unlikely don't align_nops + ; here way_p points on the start of the next/finish way + mov [way_start_p + array.way_t.next], way_p + mov [array.finish], way_p +purge way_p +;{{{ idx_insert_node ------------------------------------------------------------------------------- +; scratch ~ rax +; from primgrp +;define flags r15 ; in/out +;define strtbl_idx_p r8 +;define lat_of_lon_of xmm3 ; f64/f64 +; local from parse +;define id r12 +;define refs_p rdx ; presume it can be 0 +;define refs_p_d edx ; presume it can be 0 +;define refs_finish_p rsi +; local from unserialize +;define way_start_p r13 +; local +define idx_finish_p rbx +define id_bit_idx r14 +define id_bit_idx_d r14d +define bit_val_select rcx ; we use rcx here because we may use rcx compressed instructions +define bit_val_select_d ecx +define bit_val_select_b cl +define id_bit_idx_msb rbp +define idx_slot_p r9 +idx_insert_node: + mov idx_slot_p, [array.idx] + mov idx_finish_p, [array.idx.finish] + TRACE_WAYS_IDX 'id=%#016lx nodes.idx=%p nodes.finish=%p', id, idx_slot_p, idx_finish_p + xor id_bit_idx_d, id_bit_idx_d + xor bit_val_select_d, bit_val_select_d + mov id_bit_idx_msb, -1 ; this is to make it work with id = 0 + bsr id_bit_idx_msb, id ; if id = 0, id_bit_idx_msb is untouched namely -1 + TRACE_WAYS_IDX 'id_bit_idx_msb=%lu', id_bit_idx_msb +align_nops +.next_id_bit: + TRACE_WAYS_IDX 'id_bit_idx=%lu', id_bit_idx + cmp id_bit_idx_msb, id_bit_idx + jb .insert_here + bt id, id_bit_idx + setc bit_val_select_b ; bit_val_select = 1 if id[id_bit_idx] = 1, else 0 + TRACE_WAYS_IDX 'bit_val_select=%lu', bit_val_select + mov rax, [idx_slot_p + 8 * bit_val_select] + TRACE_WAYS_IDX 'slot=%p bit slot=%p', idx_slot_p, rax + test rax, rax + jnz .idx_existing_slot + TRACE_WAYS_IDX 'non existing slot using finish=%p', idx_finish_p + mov rax, idx_finish_p + add idx_finish_p, array.idx.slot_t.bytes_n ; we could zero the mem here, but mmap does it for us +if ~SYSTEM_LEGACY + prefetchw [idx_finish_p + 64 * 2] ; try to get ready + prefetchw [idx_finish_p + 64 * 3] +end if + mov [idx_slot_p + 8 * bit_val_select], rax +align_nops +.idx_existing_slot: + mov idx_slot_p, rax + TRACE_WAYS_IDX 'next_slot=%p', idx_slot_p + inc id_bit_idx + + jmp .next_id_bit +align_nops +.insert_here: +if TRACE_WAYS_IDX_ENABLED + mov rax, [idx_slot_p + array.idx.slot_t.way] + test rax, rax + jz .way_slot_available + TRACE_WAYS_IDX 'WARNING: overwritting an existing way=%p', rax +.way_slot_available: + TRACE_WAYS_IDX 'inserting way=%p in slot=%p', way_start_p, idx_slot_p +end if + mov [idx_slot_p + array.idx.slot_t.way], way_start_p + mov [array.idx.finish], idx_finish_p + ; XXX: if one day we need to be backed by a file we will need to DONT_NEED "madvise" the + ; the current way memory +purge idx_slot_p +purge id_bit_idx_msb +purge bit_val_select_b +purge bit_val_select_d +purge bit_val_select +purge id_bit_idx_d +purge id_bit_idx +purge idx_finish_p +;}}} idx_insert_node ------------------------------------------------------------------------------- + jmp primgrp.parse.return_from_ways +purge way_start_p +purge refs_finish_p +purge refs_p_d +purge refs_p +purge lat_of_lon_of +purge strtbl_idx_p +purge id +purge flags +;}}} unserialize ----------------------------------------------------------------------------------- +;{{{ local memcpy(sse) ----------------------------------------------------------------------------- +; XXX: lddqu performs aligned reads, carefull where you use this, but should be fine with classic +; paginated memory. +; scratch ~ rax xmm7 xmm6 xmm5 xmm4 +align_nops +memcpy: + cmp memcpy_bytes_n, 16 * 4 + jb .below64 + ; since we can be cache line mis-aligned, speculatively do prefetch 2 cls ahead + prefetchnta [memcpy_src + 64 * 2] ; speculative non-temporal "3rd" cache line ahead + prefetchnta [memcpy_src + 64 * 3] ; speculative non-temporal "4th" cache line ahead +if ~SYSTEM_LEGACY + prefetchw [memcpy_dst + 64 * 2] + prefetchw [memcpy_dst + 64 * 3] +end if + lddqu xmm7, [memcpy_src + 16 * 0] + lddqu xmm6, [memcpy_src + 16 * 1] + lddqu xmm5, [memcpy_src + 16 * 2] + lddqu xmm4, [memcpy_src + 16 * 3] + + movdqu [memcpy_dst + 16 * 0], xmm7 + movdqu [memcpy_dst + 16 * 1], xmm6 + movdqu [memcpy_dst + 16 * 2], xmm5 + movdqu [memcpy_dst + 16 * 3], xmm4 + + add memcpy_dst, 16 * 4 + sub memcpy_bytes_n, 16 * 4 + jz .done + add memcpy_src, 16 * 4 + jmp memcpy +align_nops +.below64: + cmp memcpy_bytes_n, 16 * 3 + jb .below48 + + lddqu xmm7, [memcpy_src + 16 * 0] + lddqu xmm6, [memcpy_src + 16 * 1] + lddqu xmm5, [memcpy_src + 16 * 2] + + movdqu [memcpy_dst + 16 * 0], xmm7 + movdqu [memcpy_dst + 16 * 1], xmm6 + movdqu [memcpy_dst + 16 * 2], xmm5 + + add memcpy_dst, 16 * 3 + sub memcpy_bytes_n, 16 * 3 + jz .done + add memcpy_src, 16 * 3 + ; fall-thru +align_nops +.below48: + cmp memcpy_bytes_n, 16 * 2 + jb .below32 + + lddqu xmm7, [memcpy_src + 16 * 0] + lddqu xmm6, [memcpy_src + 16 * 1] + + movdqu [memcpy_dst + 16 * 0], xmm7 + movdqu [memcpy_dst + 16 * 1], xmm6 + + add memcpy_dst, 16 * 2 + sub memcpy_bytes_n, 16 * 2 + jz .done + add memcpy_src, 16 * 2 + ; fall-thru +align_nops +.below32: + cmp memcpy_bytes_n, 16 * 1 + jb .below16 + + lddqu xmm7, [memcpy_src + 16 * 0] + movdqu [memcpy_dst + 16 * 0], xmm7 + + add memcpy_dst, 16 * 1 + sub memcpy_bytes_n, 16 * 1 + jz .done + add memcpy_src, 16 * 1 + ; fall-thru +align_nops +.below16: + cmp memcpy_bytes_n, 8 * 1 + jb .below8 + mov rax, [memcpy_src + 8 * 0] + mov [memcpy_dst + 8 * 0], rax + + add memcpy_dst, 8 * 1 + sub memcpy_bytes_n, 8 * 1 + jz .done + add memcpy_src, 8 * 1 + ; fall-thru +align_nops +.below8: + cmp memcpy_bytes_n, 7 + je .cpy7 + cmp memcpy_bytes_n, 6 + je .cpy6 + cmp memcpy_bytes_n, 5 + je .cpy5 + cmp memcpy_bytes_n, 4 + je .cpy4 + cmp memcpy_bytes_n, 3 + je .cpy3 + cmp memcpy_bytes_n, 2 + je .cpy2 + cmp memcpy_bytes_n, 1 + je .cpy1 + jmp memcpy_link +align_nops +.cpy7: + mov al, [memcpy_src] + mov [memcpy_dst], al + inc memcpy_src + inc memcpy_dst +align_nops +.cpy6: + mov al, [memcpy_src] + mov [memcpy_dst], al + inc memcpy_src + inc memcpy_dst +align_nops +.cpy5: + mov al, [memcpy_src] + mov [memcpy_dst], al + inc memcpy_src + inc memcpy_dst +align_nops +.cpy4: + mov al, [memcpy_src] + mov [memcpy_dst], al + inc memcpy_src + inc memcpy_dst +align_nops +.cpy3: + mov al, [memcpy_src] + mov [memcpy_dst], al + inc memcpy_src + inc memcpy_dst +align_nops +.cpy2: + mov al, [memcpy_src] + mov [memcpy_dst], al + inc memcpy_src + inc memcpy_dst +align_nops +.cpy1: + mov al, [memcpy_src] + mov [memcpy_dst], al + inc memcpy_src + inc memcpy_dst +align_nops +.done: + jmp memcpy_link +;}}} local memcpy(sse) ----------------------------------------------------------------------------- +;=================================================================================================== +purge memcpy_link +purge memcpy_bytes_n +purge memcpy_src +purge memcpy_dst +;{{{ macros ---------------------------------------------------------------------------------------- +if TRACE_WAYS_IDX_ENABLED + macro TRACE_WAYS_IDX fmt, regs& + TRACE_PREFIX 'WAYS_IDX', fmt, regs + end macro +else + macro TRACE_WAYS_IDX fmt, regs& + end macro +end if +;}}} macros ---------------------------------------------------------------------------------------- +end namespace ; ways diff --git a/x86_64_sse2_x87/pbf/blk/primblk/strtbl/bss.s b/x86_64_sse2_x87/pbf/blk/primblk/strtbl/bss.s new file mode 100644 index 0000000..a8aec8f --- /dev/null +++ b/x86_64_sse2_x87/pbf/blk/primblk/strtbl/bss.s @@ -0,0 +1,6 @@ +; vim: set filetype=fasm foldmethod=marker commentstring=;%s colorcolumn=101 : +namespace blk.primblk.strtbl +align 64 +idx rq 1 ; strtbl look-up idx filled with str addrs +idx.bytes_n = 32 * 1024 * 1024 ; 32MiB, 4 millions str addrs, overkill on purpose +end namespace diff --git a/x86_64_sse2_x87/pbf/blk/primblk/strtbl/data.s b/x86_64_sse2_x87/pbf/blk/primblk/strtbl/data.s new file mode 100644 index 0000000..221eca4 --- /dev/null +++ b/x86_64_sse2_x87/pbf/blk/primblk/strtbl/data.s @@ -0,0 +1,4 @@ +; vim: set filetype=fasm foldmethod=marker commentstring=;%s colorcolumn=101 : +blk.primblk.strtbl.idx.msg: namespace blk.primblk.strtbl.idx.msg + mmap_failed db 'ERROR(%ld):PBF:BLK:PRIMBLK:STRTBL:IDX:unable to initially mmap the string table index of offsets',10,0 +end namespace diff --git a/x86_64_sse2_x87/pbf/blk/primblk/strtbl/text.s b/x86_64_sse2_x87/pbf/blk/primblk/strtbl/text.s new file mode 100644 index 0000000..c5b948f --- /dev/null +++ b/x86_64_sse2_x87/pbf/blk/primblk/strtbl/text.s @@ -0,0 +1,70 @@ +; vim: set filetype=fasm foldmethod=marker commentstring=;%s colorcolumn=101 : +strtbl: namespace strtbl +align_nops +init_once: + TRACE 'PBF:BLK:PRIMBLK:STRTBL:INIT_ONCE' + + mov rbp, rsp + and rsp, not 0xf + + xor edi, edi + mov rsi, idx.bytes_n + mov rdx, 0x1 or 0x2 ; PROT_READ | PROT_WRITE + mov r10, 0x20 or 0x02 ; MAP_ANONYMOUS | MAP_PRIVATE + xor r8d, r8d + xor r9d, r9d + mov rax, linux.mmap + syscall + cmp rax, linux.errno.last + jae .err_strtbl_idx_mmap_failed + mov [idx], rax + TRACE 'PBF:BLK:PRIMBLK:STRTBL:IDX:INIT_ONCE:string table index mmaped at %p', rax + stc ; return true + mov rsp, rbp + ret +.err_strtbl_idx_mmap_failed: ; in: rax = error code + mov rdi, 2 ; stderr + lea rsi, [idx.msg.mmap_failed] + mov rdx, rax + call qword [libc.dprintf] + clc ; return false + mov rsp, rbp + ret +;--------------------------------------------------------------------------------------------------- +; we create an idx for fast look-up +; scratch: rax rcx rdx +; leaf code +; from primblk +;define primblk_p rbx +;define primblk_p_finish r12 +;define primgrp_p r13 +;define primgrp_p_finish r14 +;define flags r15 +define idx_start_p r8 +define p r10 +define p_finish rsi +; local +define idx_p rdi +align_nops +parse: + mov idx_p, idx_start_p +.str_next: + cmp p, p_finish + je primblk.parse.next_key + inc p ; skip the msg field num/type since it is always num = 1/type = 2 namely 0x0a + mov [idx_p], p ; idx_p = ptr on current byte + varint_load rdx, p ; = str_sz + add p, rdx ; skip str + add idx_p, 8 ; points on next str ptr + jmp .str_next +;--------------------------------------------------------------------------------------------------- +;purge primblk_p +;purge primblk_p_finish +;purge primgrp_p +;purge primgrp_p_finish +;purge flags +purge idx_start_p +purge p +purge p_finish +purge idx_p +end namespace ; strtbl diff --git a/x86_64_sse2_x87/pbf/blk/primblk/text.s b/x86_64_sse2_x87/pbf/blk/primblk/text.s new file mode 100644 index 0000000..da99925 --- /dev/null +++ b/x86_64_sse2_x87/pbf/blk/primblk/text.s @@ -0,0 +1,109 @@ +; vim: set filetype=fasm foldmethod=marker commentstring=;%s colorcolumn=101 : +primblk: namespace primblk +align_nops +init_once: + TRACE 'PBF:BLK:PRIMBLK:INIT_ONCE' + call strtbl.init_once + jnc .nok + call primgrp.init_once + jnc .nok + stc ; return true + ret +.nok: + clc ; return false + ret +;--------------------------------------------------------------------------------------------------- +; scratch: rax rcx r11 r9 xmm7 xmm6 xmm5 xmm4 +; from blk +define p rbx ; in +define p_finish r12 ; in +define flags r15 ; in/out +; local +define primgrp_p r13 +define primgrp_p_finish r14 +define granularity rbp ; f64 +define strtbl_idx_p r8 +define strtbl_p r10 ; tmp +define strtbl_p_finish rsi ; tmp +define lat_of_lon_of xmm3 ; f64/f64 +align_nops +parse: + mov strtbl_idx_p, [strtbl.idx] + mov granularity, 100.0 ; f64 nanodegrees default, see PBF specs + ; defaults is [0.0,0.0] nanodegrees, _NOT_ granularity units, see PBF specs + mov rax, 0.0 + movq lat_of_lon_of, rax + punpcklqdq lat_of_lon_of, lat_of_lon_of +align_nops +.next_key: + cmp p, p_finish + je blob.hdr.parse.return_from_blk + varint_load r11, p ; = key + mov r9, r11 ; = copy key + shr r11, 3 ; = field num + and r9, 111b ; = field type + cmp r11, 1 + je .strtbl_found + cmp r11, 2 + je .primgrp_found + cmp r11, 17 + je .granularity_found + cmp r11, 19 + je .lat_of_found + cmp r11, 20 + je .lon_of_found + TRACE 'PBF:BLK:PRIMBLK:MSG:FIELD %lu OF TYPE %lu', r11, r9 + val_skip p, r9, r11 + jmp .next_key +align_nops +.strtbl_found: + varint_load r11, p ; = strtbl_sz + TRACE 'PBF:BLK:PRIMBLK:STRTBL:start = %p:size = %lu(0x%lx) bytes', p, r11, r11 + mov strtbl_p, p + add p, r11 + mov strtbl_p_finish, p + jmp strtbl.parse ; leaf code +align_nops +.primgrp_found: + varint_load r11, p ; = primgrp_sz + TRACE 'PBF:BLK:PRIMBLK:PRIMGRP:start = %p:size = %lu(0x%lx) bytes', p, r11, r11 + mov primgrp_p, p + add p, r11 + mov primgrp_p_finish, p + jmp primgrp.parse +align_nops +.granularity_found: + varint_load granularity, p ; int64 granularity + TRACE 'PBF:BLK:PRIMBLK:granularity=%lu nanodegrees', r11 + cvtsi2sd xmm7, granularity + movq granularity, xmm7 ; f64 granularity + jmp .next_key +align_nops +.lat_of_found: + varint_load r11, p + TRACE 'PBF:BLK:PRIMBLK:latitude offset=%lu nanodegrees', r11 + cvtsi2sd xmm7, r11 + punpcklqdq lat_of_lon_of, xmm7 ; = lat_lon f64/f64 + jmp .next_key +align_nops +.lon_of_found: + varint_load r11, p + TRACE 'PBF:BLK:PRIMBLK:longitude offset=%lu nanodegrees', r11 + cvtsi2sd lat_of_lon_of, r11 + jmp .next_key +;--------------------------------------------------------------------------------------------------- +purge p +purge p_finish +purge flags +purge primgrp_p +purge primgrp_p_finish +purge granularity +purge strtbl_idx_p +purge strtbl_p +purge strtbl_p_finish +purge lat_of_lon_of +;=================================================================================================== +include 'strtbl/text.s' +include 'primgrp/text.s' +;=================================================================================================== +end namespace ; primblk diff --git a/x86_64_sse2_x87/pbf/blk/text.s b/x86_64_sse2_x87/pbf/blk/text.s new file mode 100644 index 0000000..4c5fba2 --- /dev/null +++ b/x86_64_sse2_x87/pbf/blk/text.s @@ -0,0 +1,29 @@ +; vim: set filetype=fasm foldmethod=marker commentstring=;%s colorcolumn=101 : +blk: namespace blk +align_nops +init_once: + TRACE 'PBF:BLK:INIT_ONCE' + call primblk.init_once + jnc .nok + stc ; return true + ret +.nok: + clc ; return false + ret +;--------------------------------------------------------------------------------------------------- +; from blob +;define p rbx ; in +;define p_finish r12 ; in +define flags r15 ; in/out +align_nops +parse: + bt flags, 2 ; 0 = OSMHeader, 1 = OSMData (aka primblk...) + jc primblk.parse + ; fallthru to blk.hdr.parse +;--------------------------------------------------------------------------------------------------- +purge flags +;=================================================================================================== +include 'hdr/text.s' +include 'primblk/text.s' +;=================================================================================================== +end namespace ; blk diff --git a/x86_64_sse2_x87/pbf/blob/bss.s b/x86_64_sse2_x87/pbf/blob/bss.s new file mode 100644 index 0000000..4b04b99 --- /dev/null +++ b/x86_64_sse2_x87/pbf/blob/bss.s @@ -0,0 +1,6 @@ +; vim: set filetype=fasm foldmethod=marker commentstring=;%s colorcolumn=101 : +namespace blob +align 64 +inflated rq 1 +inflated.bytes_n_max = 32 * 1024 * 1024 ; 32 MiB (see specs) +end namespace diff --git a/x86_64_sse2_x87/pbf/blob/data.s b/x86_64_sse2_x87/pbf/blob/data.s new file mode 100644 index 0000000..90f4fdf --- /dev/null +++ b/x86_64_sse2_x87/pbf/blob/data.s @@ -0,0 +1,5 @@ +; vim: set filetype=fasm foldmethod=marker commentstring=;%s colorcolumn=101 : +blob.msg: namespace blob.msg + inflate_mmap_failed db 'ERROR(%ld):PBF:BLOB:INIT_ONCE:unable to mmap memory for inflated data',10,0 + inflate_blob_failed db 'ERROR(%ld):PBF:BLOB:unable to inflate/decompress data',10,0 +end namespace diff --git a/x86_64_sse2_x87/pbf/blob/hdr/text.s b/x86_64_sse2_x87/pbf/blob/hdr/text.s new file mode 100644 index 0000000..e862908 --- /dev/null +++ b/x86_64_sse2_x87/pbf/blob/hdr/text.s @@ -0,0 +1,120 @@ +; vim: set filetype=fasm foldmethod=marker commentstring=;%s colorcolumn=101 : +hdr: namespace hdr +; scratch: rax rcx r11 r9 +define pbf_finish rbx +define p r12 +define p_finish r13 + define blob_p r13 +define blob_p_finish r14 +define flags r15 +align_nops +parse: +.next: + ; first, can we read blob hdr sz ? + mov rax, p + add rax, 4 ; blob hdr sz is be32 + cmp rax, pbf_finish + jae pbf.tile_db_build.relations_2nd_pass + xor eax, eax +if ~SYSTEM_LEGACY + movbe eax, [p] ; blob hdr sz is big endian, see specs +else + mov eax, [p] + bswap eax +end if + add p, 4 ; skip "sz" bytes + TRACE 'PBF:BLOB:HDR:start = %p:size = %lu(0x%lx) bytes', p, rax, rax + mov p_finish, p + add p_finish, rax ; hdr.start + sizeof(hdr.sz) = blop_p + mov blob_p_finish, p_finish ; will add the sz later, init now + and flags, not 1111b ; clear blob_hdr parsing related flags + ; fall-thru +align_nops +.key_next: + cmp p, p_finish + je pbf.tile_db_build.relations_2nd_pass + varint_load r11, p ; = key + mov r9, r11 ; = key cpy + shr r11, 3 ; = field num + and r9, 111b ; = field type + cmp r11, 3 ; field num == BlobHeader.datasize (see specs) ? + je datasize_found + cmp r11, 1 ; field num == BlobHeader.type (see specs) ? + je type_found + TRACE 'PBF:BLOB:HDR:MSG:UNHANDLED FIELD %lu OF TYPE %lu', r11, r9 + val_skip p, r9, r11 + jmp .key_next +align_nops +.key_epilog: + mov rax, flags + and rax, 1011b; got the type and the datasize ? yes, then we can parse the blob. + cmp rax, 1011b + je blob.parse ; we can jump since blob hdr finish is blob start + jmp .key_next +align_nops +.return_from_blk: ; return from blk parsing code + mov pbf_finish, [rsp + 8 * 0] + mov p, [rsp + 8 * 1] + add rsp, 8 * 2 + jmp .next +;--------------------------------------------------------------------------------------------------- +align_nops +type_found: + or flags, 1b ; flags[0] = 1 -> found the blob_hdr_type field + varint_load r11, p ; = type_str_sz + cmp r11, lengthof 'OSMData' ; the most common + jne .type_str_sz_next +.OSMData_str_cmp: ; hardcoded short str cmp + cmp [p], dword 'OSMD' + jne .unknown + cmp [p + 4], word 'at' + jne .unknown + cmp [p + 6], byte 'a' + jne .unknown + TRACE 'PBF:BLOB:HDR:type = "OSMData"' + ; flags[1] = 1 -> blob_hdr_type is known + ; flags[2] = 1 -> following blob contains an osm data blk + or flags, 110b + + add p, lengthof 'OSMData' + jmp parse.key_epilog +align_nops +.type_str_sz_next: + cmp r11, lengthof 'OSMHeader' + jne .unknown +.OSMHeader_str_cmp: ; hardcoded short str cmp + mov rax, 'OSMHeade' + cmp [p], rax ; max immediate is 32bits + jne .unknown + cmp [p + 8], byte 'r' + jne .unknown + TRACE 'PBF:BLOB:HDR:type = "OSMHeader"' + ; flags[1] = 1 -> blob_hdr_type is known + ; flags[2] = 0 -> following blob contains an osm hdr blk + and flags, not 110b + or flags, 10b + + add p, lengthof 'OSMHeader' + jmp parse.key_epilog +align_nops +.unknown: + TRACE 'PBF:BLOB:HDR:type = "%.*s"(UNKNOWN STRING OF %lu bytes)', rbx, rsi, rbx + and flags, not 10b ; flags[1] = 0 -> blob_hdr_type is unknown + add p, r11 ; skip the unkwown str + jmp parse.key_epilog +;--------------------------------------------------------------------------------------------------- +align_nops +datasize_found: + or flags, 1000b ; flags[3] = 1 -> blob_hdr_datasize was found + varint_load r11, p + TRACE 'PBF:BLOB:HDR:datasize = %lu(0x%lx) bytes', r11, r11 + add blob_p_finish, r11 + jmp parse.key_epilog +;--------------------------------------------------------------------------------------------------- +purge pbf_finish +purge p +purge p_finish +purge blob_p +purge blob_p_finish +purge flags +end namespace ; hdr diff --git a/x86_64_sse2_x87/pbf/blob/text.s b/x86_64_sse2_x87/pbf/blob/text.s new file mode 100644 index 0000000..04ac733 --- /dev/null +++ b/x86_64_sse2_x87/pbf/blob/text.s @@ -0,0 +1,132 @@ +; vim: set filetype=fasm foldmethod=marker commentstring=;%s colorcolumn=101 : +blob: namespace blob +;=================================================================================================== +include 'hdr/text.s' +;=================================================================================================== +align_nops +init_once: + TRACE 'PBF:BLOB:INIT_ONCE' + mov rbp, rsp ; align the stack now for the external calls + and rsp, not 0xf + + xor edi, edi + mov rsi, inflated.bytes_n_max + mov rdx, 1b or 10b ; PROT_READ | PROT_WRITE + mov r10, 0x22 ; MAP_PRIVATE | MAP_ANONYMOUS + xor r8d, r8d + xor r9d, r9d + mov rax, linux.mmap + syscall + cmp rax, linux.errno.last + jae .err_inflate_mmap_failed + mov [inflated], rax + TRACE 'PBF:BLOB:INIT_ONCE:inflated=%p', rax + stc ; return true + mov rsp, rbp + ret +.err_inflate_mmap_failed: + mov rdi, 2 ; stderr + lea rsi, [msg.inflate_mmap_failed] + mov rdx, rax ; errno + call qword [libc.dprintf] + clc ; return false + mov rsp, rbp + ret +;--------------------------------------------------------------------------------------------------- +; scratch: rax rcx r11 r9 +define pbf_finish rbx + define blk_p rbx +define hdr_p r12 + define blk_p_finish r12 +define p r13 +define p_finish r14 +define flags r15 +define tmp_blk_p rbp +define tmp_blk_p_d ebp +define tmp_blk_p_finish r8 +; XXX it is reused, if you change it, check the available storage is enough where it is reused +stack_storage = 2 ; qword +align_nops +parse: + sub rsp, stack_storage * 8 ; allocate + xor tmp_blk_p_d, tmp_blk_p_d + xor tmp_blk_p_finish, tmp_blk_p_finish +align_nops +.key_next: + cmp p, p_finish + je .epilog + varint_load r11, p ; key + mov r9, r11 ; = key + shr r11, 3 ; = field num + and r9, 111b ; = field type + cmp r11, 1 ; field num = Blob.data.raw (see specs) ? + je .data_raw_found + cmp r11, 2 ; field num = Blob.raw_size (see specs) ? + je .raw_size_found + cmp r11, 3 ; field num = Blob.data.zlib_data (see specs) ? + je .data_zlib_data_found + TRACE 'PBF:BLOB:MSG:UNHANDLED FIELD %lu OF TYPE %lu', r11, r9 + val_skip p, r11, r9 + jmp .key_next +align_nops +.epilog: + ; get ready to parse the next blob hdr at the finish of this blob + mov hdr_p, p_finish + ; return will be happening in blob hdr to pop the vals, reuse the 2 qwords of stack storage + mov [rsp + 8 * 0], pbf_finish + mov [rsp + 8 * 1], hdr_p + mov blk_p, tmp_blk_p + mov blk_p_finish, tmp_blk_p_finish + TRACE 'PBF:BLK:start=%p:finish=%p bytes', blk_p, blk_p_finish + jmp blk.parse; the blob is parsed, now parsed the blk +align_nops +.raw_size_found: + varint_load r11, p ; = Blob.raw_size(int32) + TRACE 'PBF:BLOB:raw_size=%lu(0x%lx) bytes', r11, r11 + add tmp_blk_p_finish, r11 ; may already have loaded blk_p, or 0 + jmp .key_next +align_nops +.data_raw_found: + varint_load r11, p ; load the data sz + TRACE 'PBF:BLOB:data.raw found, size=%lu(0x%lu) bytes', r11, r11 + mov tmp_blk_p, p + add p, r11 ; skip blk + mov tmp_blk_p_finish, p + jmp .key_next +align_nops +.data_zlib_data_found: + mov tmp_blk_p, [inflated] ; callee-saved reg + mov rdi, tmp_blk_p ; arg0 + mov rsi, inflated.bytes_n_max ; arg1 + varint_load r11, p ; deflate_bytes_n + TRACE 'PBF:BLOB:data.zlib_data found, %lu(0x%lx) bytes', r11, r11 + mov rdx, p ; arg2 = deflate_bytes_start + add p, r11 ; deflate_bytes_start + deflate_bytes_n = deflate_bytes_finish + mov rcx, r11 ; arg3 = deflate_bytes_n + mov [rsp], tmp_blk_p_finish ; save r8 + call inflate.do ; will do some C ABI external calls, expect usual clobbers + jnc .err_inflate_blob_failed + mov tmp_blk_p_finish, [rsp] ; restore r8 + add tmp_blk_p_finish, tmp_blk_p ; may already contain the raw size, or 0 + jmp .key_next + +.err_inflate_blob_failed: + mov rdi, 2 ; stderr + mov rsi, rax ; errno + lea rsi, [msg.inflate_blob_failed] + call qword [libc.dprintf] + mov rax, 1 ; return 1 + add rsp, stack_storage * 8 ; free + jmp pbf.tile_db_build.exit_nok +;--------------------------------------------------------------------------------------------------- +purge pbf_finish +purge blk_p +purge hdr_p +purge blk_p_finish +purge p +purge p_finish +purge flags +purge tmp_blk_p +purge tmp_blk_p_d +purge tmp_blk_p_finish +end namespace ; blob diff --git a/x86_64_sse2_x87/pbf/bss.s b/x86_64_sse2_x87/pbf/bss.s new file mode 100644 index 0000000..4ec247c --- /dev/null +++ b/x86_64_sse2_x87/pbf/bss.s @@ -0,0 +1,47 @@ +; vim: set filetype=fasm foldmethod=marker commentstring=;%s colorcolumn=101 : +namespace pbf +align 64 +ctx rq 1 +start rq 1 +fd rq 1 ; s32 +stat rb linux.stat_t.bytes_n +;--------------------------------------------------------------------------------------------------- +; this ctx is actually more of a user client code parameter passing than anything else +ctx_t: namespace ctx_t + ; INPUT -- START --------------------------------------------------------------------------- + ; TODO: move to defined bit idx + ; [0] bbox.sw.lat user provided + ; [1] bbox.sw.lon user provided + ; [2] bbox.ne.lat user provided + ; [3] bbox.ne.lon user provided + flags := 0 ; qw + file_path := 8 ; qw + tile_db_dir.path := 16 ; qw + zoom := 24 ; byte + ; pad 3 bytes + ; little endian lat/lon + bbox.sw := 32 ; xword, vec2f64 + bbox.sw.lon := 32 ; f64 nanodegrees + bbox.sw.lat := 40 ; f64 nanodegrees + bbox.ne := 48 ; xword, vec2f64 + bbox.ne.lon := 48 ; f64 nanodegrees + bbox.ne.lat := 56 ; f64 nanodegrees + ; INPUT -- END ----------------------------------------------------------------------------- + ; PRIVATE -- START ------------------------------------------------------------------------- + zoom.scaled.f64 := 64 ; for wm computation (1<next node + ; from here, node is in lat bbox + bt rcx, 0 ; (ne_lon < sw_lon) + jc .lon_warp_around ; unlikely + and rcx, 1010b ; we can be destructive here + cmp rcx, 1010b ; (sw_lon <= node_lon) && (node_lon <= ne_lon) + je tile_intersector ; node in bbox, compute way intersection with tiles + jmp .next_node ; node not in lon bbox-->next node +align_nops +.lon_warp_around: ; unlikely + test rcx, 1010b; (sw_lon <= node_lon) || (node_lon <= ne_lon) + jnz tile_intersector ; node in bbox, compute way intersection with tiles + jmp .next_node ; node not in lon bbox-->next node + ; way nodes loop -- end -------------------------------------------------------------------- +purge way_node_p +purge way_nodes_finish_p +purge way_nodes_p + ; bbox -- end ------------------------------------------------------------------------------ + ; tile_intersector -- start ---------------------------------------------------------------- +align_nops +tile_intersector: +; from select +;define flags r15 +; from ways +;define ways_finish_p rbx +;define way_start_p r12 +; from ways used only if a bbox is used +;define sw xmm3 +;define ne xmm2 +; local +define cache_tile xmm1 ; this contain the last x/y vector of the last tile this way was added +define way_nodes_p r13 ; ptr in the array of node ptrs from the current way +define way_nodes_finish_p r14 +define way_node_p rbp +define scaled_zoom_p rdi ; 1st param of geo_to_wm call + TRACE_FEATURES 'intersector for way=%p', way_start_p + mov way_nodes_p, qword [way_start_p + blk.primblk.primgrp.ways.array.way_t.nodes] + ; where the array of node ptrs does finish + mov way_nodes_finish_p, qword [way_start_p + blk.primblk.primgrp.ways.array.way_t.next] + ; prepare the call to geo_to_wm.f64 + mov rax, [ctx] + lea scaled_zoom_p, [rax + ctx_t.zoom.scaled.f64] + ; load the cache tile reg with an invalid value (tile_x_finish/tile_y_finish) for this zoom lvl + lddqu cache_tile, xword [rax + ctx_t.zoom.finish.vec2s64] + jmp .loop_entry + ; way nodes loop -- start ------------------------------------------------------------------ +define geo_p rsi ; 2nd param of geo_to_wm call +define wm_tile_p rdx ; 3rd param of geo_to_wm call +align_nops +.next_node: + add way_nodes_p, 8 +.loop_entry: + cmp way_nodes_p, way_nodes_finish_p + je ways.next ; intersection done, jmp back to the selecting the way-only features + mov way_node_p, [way_nodes_p] ; load the node ptr +if ~SYSTEM_LEGACY + test way_nodes_p, 0x3f ; if way_nodes_p is aligned on a cl, prefetch the next 16 ptrs of nodes + jnz .no_prefetch + prefetcht0 [way_nodes_p + 2 * 64] ; greed: prefetch 2 cls (= 16 node ptrs) further away + prefetcht0 [way_nodes_p + 3 * 64] +.no_prefetch: +end if + test way_node_p, way_node_p ; a node ptr can be 0 + jz .next_node + bt qword [way_node_p + blk.primblk.primgrp.dense.nodes.node_t.flags], \ + blk.primblk.primgrp.dense.nodes.node_t.flags.wm_computed + jc .cache_hit_test + lea geo_p, [way_node_p + blk.primblk.primgrp.dense.nodes.node_t.geo] + lea wm_tile_p, [way_node_p + blk.primblk.primgrp.dense.nodes.node_t.wm.tile] + call geo_to_wm.f64 + or qword [way_node_p + blk.primblk.primgrp.dense.nodes.node_t.flags], \ + 1 shl blk.primblk.primgrp.dense.nodes.node_t.flags.wm_computed +purge geo_p +purge wm_tile_p +.cache_hit_test: + lddqu xmm7, xword [way_node_p + blk.primblk.primgrp.dense.nodes.node_t.wm.tile.xy] + pcmpeqb xmm7, cache_tile + pmovmskb rax, xmm7 + cmp rax, 0xffff + je .next_node ; cache hit + ; cache miss, update cache reg + lddqu cache_tile, xword [way_node_p + blk.primblk.primgrp.dense.nodes.node_t.wm.tile.xy] + ; tile lookup -- start --------------------------------------------------------------------- + ; add this way feature to this very tile db +define xs_p r8 +define x r9 +define ys_p r10 +.tile_lookup: + mov xs_p, [xs] + mov x, [way_node_p + blk.primblk.primgrp.dense.nodes.node_t.wm.tile.xy.x] + mov ys_p, [xs_p + 8 * x] + test ys_p, ys_p + jz tile_column_new ; once, we'll jmp back to tile_column_done below +purge xs_p +purge x +define tile_p r9 +.tile_column_lookup_done: + ; we expect ys_p only from here + mov tile_p, [way_node_p + blk.primblk.primgrp.dense.nodes.node_t.wm.tile.xy.y] + shl tile_p, tile_t.bytes_n_log2 ; = y * tile_bytes_n + add tile_p, ys_p ; = ys_p + y * tile_bytes_n = tile_p +purge ys_p + ; tile lookup -- end ----------------------------------------------------------------------- + ; adding feature way to tile -- start ------------------------------------------------------ +; from select +;define flags r15 +; from ways +;define ways_finish_p rbx +;define way_start_p r12 +; from ways used only if a bbox is used +;define sw xmm3 +;define ne xmm2 +; from tile_intersector +;define cache_tile xmm1 +;define way_nodes_p r13 +;define way_nodes_finish_p r14 +;define way_node_p rbp +;define scaled_zoom_p rdi +; from tile_lookup +;define tile_p r9 + TRACE_FEATURES 'adding feature way %p to tile %p because of node %p', way_start_p, tile_p, way_node_p + bt qword [tile_p + tile_t.flags], tile_t.flags.ways_inited + jnc .tile_ways_not_inited ; cold and slow, put far away + ; idx get slot -- start -------------------------------------------------------------------- +.tile_ways_inited: +define idx_slot_p r8 ; out + +define way_pbf_of r11 +define bit_val_select r10 +define bit_val_select_b r10b +define way_pbf_of_bit_idx_msb rcx ; using rcx specific instructions +define way_pbf_of_bit_idx_msb_b cl +define next_idx_slot rsi + mov idx_slot_p, qword [tile_p + tile_t.ways.idx] + mov way_pbf_of, way_start_p + sub way_pbf_of, qword [blk.primblk.primgrp.ways.array] ; ptr->pbf_of + mov way_pbf_of_bit_idx_msb, -1 ; if the pbf_of is 0, make it work + bsr way_pbf_of_bit_idx_msb, way_start_p + inc way_pbf_of_bit_idx_msb_b ; the n of bits to go thru + xor bit_val_select, bit_val_select +align_nops +.next_way_pbf_of_bit: + jecxz .ways_idx_slot_found + bt way_pbf_of, 0 + setc bit_val_select_b + mov next_idx_slot, [idx_slot_p + 8 * bit_val_select] + test next_idx_slot, next_idx_slot ; do we have an idx slot + jz .idx_insert_slot ; missing slot, switch to slot insertion parsing mode +if ~SYSTEM_LEGACY + prefetcht0 [next_idx_slot] +end if + mov idx_slot_p, next_idx_slot + shr way_pbf_of, 1 + dec way_pbf_of_bit_idx_msb_b + jmp .next_way_pbf_of_bit +align_nops +.idx_insert_slot: + ; we are in the middle of the idx: we keep going adding new slots until the right one + mov rax, qword [tile_p + tile_t.ways.idx.next] + mov [idx_slot_p + 8 * bit_val_select], rax + add qword [tile_p + tile_t.ways.idx.next], tile_t.ways.idx.slot_t.bytes_n + mov idx_slot_p, rax + mov qword [idx_slot_p + tile_t.ways.idx.slot_t.of], -1 ; = 'empty' slot + dec way_pbf_of_bit_idx_msb_b + jz .ways_idx_slot_inserted + shr way_pbf_of, 1 + bt way_pbf_of, 0 + setc bit_val_select_b + jmp .idx_insert_slot +align_nops +.ways_idx_slot_found: +.ways_idx_slot_inserted: +purge way_pbf_of +purge bit_val_select +purge bit_val_select_b +purge way_pbf_of_bit_idx_msb +purge way_pbf_of_bit_idx_msb_b +purge next_idx_slot + ; idx get slot -- end ---------------------------------------------------------------------- +define way_of_in_tile_db rax + mov way_of_in_tile_db, qword [idx_slot_p + tile_t.ways.idx.slot_t.of] + cmp way_of_in_tile_db, -1 + jne .tile_add_way_to_feature ; already there, just need to add this way to the feature +.tile_pbf_way_import: +; from select +;define flags r15 +; from ways +;define ways_finish_p rbx +;define way_start_p r12 +; from ways used only if a bbox is used +;define sw xmm3 +;define ne xmm2 +; from tile intersector +;define cache_tile xmm1 +;define way_nodes_p r13 +;define way_nodes_finish_p r14 +;define way_node_p rbp +;define scaled_zoom_p rdi +; from tile lookup +;define tile_p r9 +; from idx slot get +;define idx_slot_p r8 +;define way_of_in_tile_db rax + movq xmm7, scaled_zoom_p ; spill + movq xmm6, tile_p + movq xmm5, idx_slot_p + mov rdi, tile_p ; dst tile + mov rsi, way_start_p ; src way + ; factorized code, without tile idx handling since we do it above here + call tile.from_pbf.way_import_no_idx + jnc tile_db_build.exit_nok + movq scaled_zoom_p, xmm7 ; unspill + movq tile_p, xmm6 + movq idx_slot_p, xmm5 + mov qword [idx_slot_p + tile_t.ways.idx.slot_t.of], way_of_in_tile_db +purge idx_slot_p +.tile_add_way_to_feature: + movq xmm7, scaled_zoom_p ; spill + movq xmm6, tile_p + mov rdi, tile_p ; dst tile + mov rsi, way_of_in_tile_db ; the offset in tile db + mov rdx, way_node_p ; the node of the tile + call [select.ways.import.fn] + jnc tile_db_build.exit_nok + movq scaled_zoom_p, xmm7 ; unspill + movq tile_p, xmm6 + jmp .next_node +purge way_of_in_tile_db + ; adding feature way to tile -- end -------------------------------------------------------- +.tile_ways_not_inited: +; from select +;define flags r15 +; from ways +;define ways_finish_p rbx +;define way_start_p r12 +; from ways used only if a bbox is used +;define sw xmm3 +;define ne xmm2 +; from tile intersector +;define cache_tile xmm1 +;define way_nodes_p r13 +;define way_nodes_finish_p r14 +;define way_node_p rbp +;define scaled_zoom_p rdi +; from tile lookup +;define tile_p r9 + movq xmm7, scaled_zoom_p ; spill + movq xmm6, tile_p ; spill + mov rdi, tile_p + mov rsi, way_node_p + call tile.init.ways ; factorized code + jnc tile_db_build.exit_nok ; fatal + movq scaled_zoom_p, xmm7 + movq tile_p, xmm6 + jmp .tile_ways_inited +purge tile_p + ; way nodes loop -- end -------------------------------------------------------------------- +purge cache_tile +purge way_nodes_p +purge way_nodes_finish_p +purge way_node_p +purge scaled_zoom_p + ; tile intersector -- end ------------------------------------------------------------------ + ; ways loop -- end ------------------------------------------------------------------------- +purge ways_finish_p +purge way_start_p +purge sw +purge ne +; ways -- end -------------------------------------------------------------------------------------- +;=================================================================================================== +; Since we start with way-only features, then tile column creation is more likely to happen here, +; put it near way-only features main code path. +include 'tile_column_new.s' +;=================================================================================================== +; relations -- start ------------------------------------------------------------------------------- +relations: + TRACE_FEATURES 'RELATIONS:START' + ; EMPTY +.zoom_selection_done: + ; EMTPY +; relations -- end --------------------------------------------------------------------------------- +; nodes -- start ----------------------------------------------------------------------------------- +nodes: + TRACE_FEATURES 'NODES:START' + ; EMPTY +.zoom_selection_done: + ; EMPTY +; nodes -- end ------------------------------------------------------------------------------------- +select.epilog: + TRACE_FEATURES 'END' + jmp pbf.tile_db_build.exit_ok +purge flags +;=================================================================================================== +include 'tile.s' ; put the factorized tile code here +include 'motorway.s' ; put some motorway code here, will reuse some code from tile.s +;=================================================================================================== +;{{{ macros +if TRACE_FEATURES_ENABLED + macro TRACE_FEATURES fmt, regs& + TRACE_PREFIX 'FEATURES', fmt, regs + end macro +else + macro TRACE_FEATURES fmt, regs& + end macro +end if +;}}} macros +end namespace ; features diff --git a/x86_64_sse2_x87/pbf/features/tile.s b/x86_64_sse2_x87/pbf/features/tile.s new file mode 100644 index 0000000..ca0ec20 --- /dev/null +++ b/x86_64_sse2_x87/pbf/features/tile.s @@ -0,0 +1,665 @@ +; vim: set filetype=fasm foldmethod=marker commentstring=;%s colorcolumn=101 : +tile: namespace tile +init: namespace init +; Init of nodes section, ways section, relations section could be factorized even more, but we chose +; not in order to keep it open for ez customization per section. +; Shared definitions: +define tile_p rbx +define node_p r12 ; the reference pbf node used for this very tile +define path_finish r13 +define flags r14 + flags.path_tile_dir_rdy := 0 ; init.path = 'x/y' with its path_finish +;=================================================================================================== +; C-ABI call (notstackalign), input: +; rdi = tile_p +; rsi = node_p +ways: namespace ways + enter 8 * 4, 0 + and rsp, not 0xf ; align stack for external calls + + mov [rsp + 8 * 0], rbx + mov [rsp + 8 * 1], r12 + mov [rsp + 8 * 2], r13 + mov [rsp + 8 * 3], r14 + + mov tile_p, rdi + mov node_p, rsi + xor flags, flags + + call init.xy_to_str + bt qword [tile_p + tile_t.flags], tile_t.flags.dirs_created + jc dirs_inited + call init.dirs_create ; will build path_tile_dir + jnc epilog +dirs_inited: + bt flags, flags.path_tile_dir_rdy + jc path_tile_dir_is_rdy + call path_tile_dir_do +path_tile_dir_is_rdy: + mov rax, '/ways' ; terminator zero added + mov qword [path_finish], rax + add path_finish, lengthof '/ways' + 1 ; add a zero terminator + ; get a fd --------------------------------------------------------------------------------- + mov rax, [ctx] + mov rdi, [rax + ctx_t.tile_db_dir.fd] + lea rsi, [path] + mov rdx, linux.O_CREAT or linux.O_TRUNC or linux.O_WRONLY + mov r10, 110110110b ; RW for all but umask + mov rax, linux.openat + syscall + cmp rax, linux.errno.last + jae err_ways_openat_failed + mov [tile_p + tile_t.ways.fd], rax + mov qword [tile_p + tile_t.ways.of], 0 + ; idx init --------------------------------------------------------------------------------- + mov rax, qword [blk.primblk.primgrp.ways.n] ; worst case, all ways + mov rcx, tile_t.ways.idx.slot_t.bytes_n + mul rcx ; = rdx:rax = bytes n for the idx + + xor edi, edi + mov rsi, rax ; = bytes n for the idx + mov rdx, 1b or 10b ; PROT_READ | PROT_WRITE + mov r10, 0x22 ; MAP_PRIVATE | MAP_ANONYMOUS + xor r8d, r8d + xor r9d, r9d + mov rax, linux.mmap + syscall + cmp rax, linux.errno.last + jae err_ways_idx_mmap_failed + mov [tile_p + tile_t.ways.idx], rax + mov qword [rax + tile_t.ways.idx.slot_t.of], -1 ; set up invalid offset in the first slot + add rax, tile_t.ways.idx.slot_t.bytes_n + mov [tile_p + tile_t.ways.idx.next], rax + ; we need the inited nodes ----------------------------------------------------------------- + bt qword [tile_p + tile_t.flags], 1 shl tile_t.flags.nodes_inited + jnc nodes_init_do +nodes_inited: + ; ------------------------------------------------------------------------------------------ + or qword [tile_p + tile_t.flags], 1 shl tile_t.flags.ways_inited + stc +epilog: + mov rbx, [rsp + 8 * 0] + mov r12, [rsp + 8 * 1] + mov r13, [rsp + 8 * 2] + mov r14, [rsp + 8 * 3] + leave + ret +nodes_init_do: + mov rdi, tile_p + mov rsi, node_p + call nodes + jnc err_ways_nodes_init_failed + jmp nodes_inited +err_ways_openat_failed: ; errno in rax + mov rdi, 2 + lea rsi, [msg.err_ways_openat_failed] + mov rdx, rax + lea rcx, [init.path] + call qword [libc.dprintf] + clc + jmp epilog +err_ways_idx_mmap_failed: ; errno in rax + mov rdi, 2 + lea rsi, [msg.err_ways_idx_mmap_failed] + mov rdx, rax + call qword [libc.dprintf] + clc + jmp epilog +err_ways_nodes_init_failed: ; errno in rax + mov rdi, 2 + lea rsi, [msg.err_ways_nodes_init_failed] + call qword [libc.dprintf] + clc + jmp epilog +end namespace ; ways_init +;=================================================================================================== +; C-ABI call (nostackalign), input: +; rdi = tile_p +; rsi = node_p +nodes: namespace nodes + enter 8 * 4, 0 + and rsp, not 0xf ; align stack for external calls + + mov [rsp + 8 * 0], rbx + mov [rsp + 8 * 1], r12 + mov [rsp + 8 * 2], r13 + mov [rsp + 8 * 3], r14 + + mov tile_p, rdi + mov node_p, rsi + xor flags, flags + + call init.xy_to_str + bt qword [tile_p + tile_t.flags], tile_t.flags.dirs_created + jc dirs_inited + call init.dirs_create ; will build path_tile_dir + jnc epilog +dirs_inited: + bt flags, flags.path_tile_dir_rdy + jc path_tile_dir_is_rdy + call path_tile_dir_do +path_tile_dir_is_rdy: + mov rax, '/nodes' ; terminator zero added + mov qword [path_finish], rax + add path_finish, lengthof '/nodes' + 1 ; add a zero terminator + ; get a fd --------------------------------------------------------------------------------- + mov rax, [ctx] + mov rdi, [rax + ctx_t.tile_db_dir.fd] + lea rsi, [path] + mov rdx, linux.O_CREAT or linux.O_TRUNC or linux.O_WRONLY + mov r10, 110110110b ; RW for all but umask + mov rax, linux.openat + syscall + cmp rax, linux.errno.last + jae err_nodes_openat_failed + mov [tile_p + tile_t.nodes.fd], rax + mov qword [tile_p + tile_t.nodes.of], 0 + ; idx init --------------------------------------------------------------------------------- + mov rax, qword [blk.primblk.primgrp.dense.nodes.n] ; worst case, all nodes + mov rcx, tile_t.nodes.idx.slot_t.bytes_n + mul rcx ; = rdx:rax = bytes n for the idx + + xor edi, edi + mov rsi, rax ; = bytes n for the idx + mov rdx, 1b or 10b ; PROT_READ | PROT_WRITE + mov r10, 0x22 ; MAP_PRIVATE | MAP_ANONYMOUS + xor r8d, r8d + xor r9d, r9d + mov rax, linux.mmap + syscall + cmp rax, linux.errno.last + jae err_nodes_idx_mmap_failed + mov [tile_p + tile_t.nodes.idx], rax + mov qword [rax + tile_t.ways.idx.slot_t.of], -1 ; set up invalid offset in the first slot + add rax, tile_t.nodes.idx.slot_t.bytes_n + mov [tile_p + tile_t.nodes.idx.next], rax ; the kernel will zero it + ; ------------------------------------------------------------------------------------------ + or qword [tile_p + tile_t.flags], 1 shl tile_t.flags.nodes_inited + stc +epilog: + mov rbx, [rsp + 8 * 0] + mov r12, [rsp + 8 * 1] + mov r13, [rsp + 8 * 2] + mov r14, [rsp + 8 * 3] + leave + ret +err_nodes_openat_failed: ; errno in rax + mov rdi, 2 + lea rsi, [msg.err_nodes_openat_failed] + mov rdx, rax + lea rcx, [init.path] + call qword [libc.dprintf] + clc + jmp epilog +err_nodes_idx_mmap_failed: ; errno in rax + mov rdi, 2 + lea rsi, [msg.err_nodes_idx_mmap_failed] + mov rdx, rax + call qword [libc.dprintf] + clc + jmp epilog +end namespace ; nodes_init +;=================================================================================================== +dirs_create: namespace dirs_create + lea rdi, [init.path] +path_x_add: ; rdi points on the terminating zero char of path + ; x_str is "inverted" + movzx rcx, byte [init.x_str] ; = x_str_sz + lea rsi, [init.x_str] ; points on the end of x_str ("past" the last char) + add rsi, rcx ; rsi points on the first char of y_str +.next_char: + mov al, [rsi] ; cpy the char + mov [rdi], al + inc rdi ; path next char + dec cl + jz .insert_terminator + dec rsi ; "inverted" x next char + jmp .next_char +.insert_terminator: + mov byte [rdi], 0 + mov path_finish, rdi ; to add the rest of the path later + ;------------------------------------------------------------------------------------------- + ; stat the x directory (tile columun) + mov rax, [ctx] + mov rdi, [rax + ctx_t.tile_db_dir.fd] + lea rsi, [init.path] + lea rdx, [stat] ; global storage + xor r10, r10 ; flags = 0 + + mov rax, linux.fstatat + syscall + cmp rax, linux.errno.last + jae x_dir_create +x_dir_done: + ;------------------------------------------------------------------------------------------- +path_y_add: + mov rdi, path_finish ; points on the x dir terminator + mov byte [rdi], '/' + inc rdi + ; y_str is "inverted" + movzx rcx, byte [init.y_str] ; = y_str_sz + lea rsi, [init.y_str] ; points on the end of y_str ("past" the last char) + add rsi, rcx ; rsi points on the first char of y_str +.next_char: + mov al, [rsi] ; cpy the char + mov [rdi], al + inc rdi ; path next char + dec cl + jz .insert_terminator + dec rsi ; "inverted" y next char + jmp .next_char +.insert_terminator: + mov byte [rdi], 0 + mov path_finish, rdi + or flags, 1 shl flags.path_tile_dir_rdy + ;------------------------------------------------------------------------------------------- + ; stat the y directory (tile) + mov rax, [ctx] + mov rdi, [rax + ctx_t.tile_db_dir.fd] + lea rsi, [path] + lea rdx, [stat] + xor r10, r10 ; flags = 0 + + mov rax, linux.fstatat + syscall + cmp rax, linux.errno.last + jae y_dir_create +y_dir_done: + ;------------------------------------------------------------------------------------------- + or qword [tile_p + tile_t.flags], 1 shl tile_t.flags.dirs_created + stc + ret +;=================================================================================================== +x_dir_create: + mov rax, [ctx] + mov rdi, [rax + ctx_t.tile_db_dir.fd] + lea rsi, [path] + mov rdx, 111111111b ; RWX for all + + mov rax, linux.mkdirat + syscall + cmp rax, linux.errno.last + jae err_x_dir_mkdirat_failed + jmp x_dir_done + ;------------------------------------------------------------------------------------------- +y_dir_create: + mov rax, [ctx] + mov rdi, [rax + ctx_t.tile_db_dir.fd] + lea rsi, [path] + mov rdx, 111111111b ; RWX for all + + mov rax, linux.mkdirat + syscall + cmp rax, linux.errno.last + jae err_y_dir_mkdirat_failed + jmp y_dir_done + ;------------------------------------------------------------------------------------------- +err_y_dir_mkdirat_failed: ; errno in rax + lea rsi, [msg.err_y_dir_mkdirat_failed] + jmp err_exit +err_x_dir_mkdirat_failed: ; errno in rax + lea rsi, [msg.err_x_dir_mkdirat_failed] + jmp err_exit +err_exit: ; errno in rax + mov rdi, 2 + mov rdx, rax + lea rcx, [path] + call qword [libc.dprintf] + clc + ret +end namespace ; dirs_create +;=================================================================================================== +; expect x and y to be converted already +; REUSED FROM EXTERNAL CODE (i.e. MOTORWAYS) +path_tile_dir_do: + lea rdi, [init.path] +.path_x_add: ; rdi points on the terminating zero char of path + ; x_str is "inverted" + movzx rcx, byte [init.x_str] ; = x_str_sz + lea rsi, [init.x_str] ; points on the end of x_str ("past" the last char) + add rsi, rcx ; rsi points on the first char of y_str +.x_next_char: + mov al, [rsi] ; cpy the char + mov [rdi], al + inc rdi ; path next char + dec cl + jz .insert_separator + dec rsi ; "inverted" x next char + jmp .x_next_char +.insert_separator: + mov byte [rdi], '/' + inc rdi + ; y_str is "inverted" + movzx rcx, byte [init.y_str] ; = y_str_sz + lea rsi, [init.y_str] ; points on the end of y_str ("past" the last char) + add rsi, rcx ; rsi points on the first char of y_str +.y_next_char: + mov al, [rsi] ; cpy the char + mov [rdi], al + inc rdi ; path next char + dec cl + jz .save_finish + dec rsi ; "inverted" y next char + jmp .y_next_char +.save_finish: + mov byte [rdi], 0 ; zero terminate the path + mov path_finish, rdi + ret +;=================================================================================================== +; REUSED FROM EXTERNAL CODE (i.e. MOTORWAYS) +xy_to_str: namespace xy_to_str +define str_p rdi ; tmp +x_conv: + xor edx, edx + mov rax, qword [node_p + blk.primblk.primgrp.dense.nodes.node_t.wm.tile.xy.x] + xor ecx, ecx + mov r11, 10 + lea str_p, [init.x_str + 1] +.digit: + div r11 + add dl, '0' ; to ascii + mov byte [str_p], dl + inc cl + test rax, rax + jz .finished + inc str_p + xor edx, edx + jmp .digit +.finished: + mov [init.x_str], cl +y_conv: + xor edx, edx + mov rax, qword [node_p + blk.primblk.primgrp.dense.nodes.node_t.wm.tile.xy.y] + xor ecx, ecx + ; mov r11, 10 + lea str_p, [init.y_str + 1] +.digit: + div r11 + add dl, '0' ; to ascii + mov byte [str_p], dl + inc cl + test rax, rax + jz .finished + inc str_p + xor edx, edx + jmp .digit +.finished: + mov [init.y_str], cl + ret +purge str_p +end namespace ; xy_to_str +;=================================================================================================== +purge tile_p +purge node_p +purge path_finish +purge flags +end namespace ; init +;=================================================================================================== +from_pbf: namespace from_pbf +align_nops +node_import: namespace node_import +; in rdi = tile_p +; in rsi = pbf_node_p +; out rax = node offset in tile array +; expect the tile to be properly inited, but the node may require wm computation +define tile_p rbx +define tile_node_bytes_n r12 +define idx_slot_p r13 +define pbf_node_p r14 +define rbx_save rsp + 8 * 0 +define r12_save rsp + 8 * 1 +define r13_save rsp + 8 * 2 +define r14_save rsp + 8 * 3 + enter 8 * 4, 0 + and rsp, not 0xf ; align the stack for the external calls + mov [rbx_save], rbx + mov [r12_save], r12 + mov [r13_save], r13 + mov [r14_save], r14 + + mov tile_p, rdi + mov pbf_node_p, rsi + + ; idx get slot -- start -------------------------------------------------------------------- +define pbf_node_of r11 +define bit_val_select r9 +define bit_val_select_b r9b +define pbf_node_of_bit_idx_msb rcx ; using rcx specific instructions +define pbf_node_of_bit_idx_msb_b cl +define next_idx_slot r8 + mov idx_slot_p, qword [tile_p + tile_t.nodes.idx] + mov pbf_node_of, pbf_node_p + sub pbf_node_of, qword [blk.primblk.primgrp.dense.nodes] ; ptr->pbf offset + mov pbf_node_of_bit_idx_msb, -1 ; if the pbf offset is 0, make it work + bsr pbf_node_of_bit_idx_msb, pbf_node_p + inc pbf_node_of_bit_idx_msb_b ; the n of bits to go thru + xor bit_val_select, bit_val_select +align_nops +.next_pbf_node_of_bit: + jecxz .idx_slot_found + bt pbf_node_of, 0 + setc bit_val_select_b + mov next_idx_slot, [idx_slot_p + 8 * bit_val_select] + test next_idx_slot, next_idx_slot ; do we have an idx slot + jz .idx_insert_slot ; missing slot, switch to slot insertion parsing mode +if ~SYSTEM_LEGACY + prefetcht0 [next_idx_slot] +end if + mov idx_slot_p, next_idx_slot + shr pbf_node_of, 1 + dec pbf_node_of_bit_idx_msb_b + jmp .next_pbf_node_of_bit +align_nops +.idx_insert_slot: + ; we are in the middle of the idx: we keep going adding new slots until the right one + mov rax, qword [tile_p + tile_t.nodes.idx.next] + mov [idx_slot_p + 8 * bit_val_select], rax + add qword [tile_p + tile_t.nodes.idx.next], tile_t.nodes.idx.slot_t.bytes_n + mov idx_slot_p, rax + mov qword [idx_slot_p + tile_t.nodes.idx.slot_t.of], -1 ; = 'empty' slot + dec pbf_node_of_bit_idx_msb_b + jz .idx_slot_inserted + shr pbf_node_of, 1 + bt pbf_node_of, 0 + setc bit_val_select_b + jmp .idx_insert_slot +align_nops +.idx_slot_found: +.idx_slot_inserted: +purge pbf_node_of_node +purge bit_val_select +purge bit_val_select_b +purge pbf_node_of_bit_idx_msb +purge pbf_node_of_bit_idx_msb_b +purge next_idx_slot + ; idx get slot -- end ---------------------------------------------------------------------- + mov rax, qword [idx_slot_p + tile_t.nodes.idx.slot_t.of] + cmp rax, -1 + jne exit_ok ; we have already the offset in rax to return to the caller +add_node_to_tile: + ; compute the tile stuff if needed before storage -- start --------------------------------- + bt qword [pbf_node_p + blk.primblk.primgrp.dense.nodes.node_t.flags], \ + blk.primblk.primgrp.dense.nodes.node_t.flags.wm_computed + jc .pbf_node_wm_computed + mov rax, [ctx] + lea rdi, [rax + ctx_t.zoom.scaled.f64] ; 1st arg + lea rsi, [pbf_node_p + blk.primblk.primgrp.dense.nodes.node_t.geo] ; 2nd arg + lea rdx, [pbf_node_p + blk.primblk.primgrp.dense.nodes.node_t.wm.tile] ; 3rd arg + call geo_to_wm.f64 + or qword [pbf_node_p + blk.primblk.primgrp.dense.nodes.node_t.flags], \ + 1 shl blk.primblk.primgrp.dense.nodes.node_t.flags.wm_computed +.pbf_node_wm_computed: + ; compute the tile stuff if needed before storage -- end ----------------------------------- + ; write into the tile db nodes file -- start ----------------------------------------------- + mov rdi, qword [tile_p + tile_t.nodes.fd] + lea rsi, [pbf_node_p + blk.primblk.primgrp.dense.nodes.node_t.wm.tile.uv] ; 2nd arg of the write syscall + mov rdx, qword [pbf_node_p + blk.primblk.primgrp.dense.nodes.node_t.next] ; ptr on next node = ptr on the end of current node +purge pbf_node_p + sub rdx, rsi ; = tile_node_bytes_n and 3rd arg of the write syscall + mov tile_node_bytes_n, rdx + ; write the pbf node in the tile node + mov rax, linux.write + syscall + cmp rax, linux.errno.last + jae err_write_node_failed + ; write into the tile db nodes file -- end ------------------------------------------------- + mov rax, qword [tile_p + tile_t.nodes.of] ; return the tile_node offset to the caller + mov [idx_slot_p + tile_t.nodes.idx.slot_t.of], rax ; insert the now known offset into the idx + add [tile_p + tile_t.nodes.of], tile_node_bytes_n ; update the offset tracker +exit_ok: + stc +epilog: + mov rbx, qword [rbx_save] + mov r12, qword [r12_save] + mov r13, qword [r13_save] + mov r14, qword [r14_save] + leave + ret +err_write_node_failed: ; errno in rax + mov rdi, 2 ; stderr + lea rsi, [msg.err_write_node_failed] + mov rdx, rax ; errno + call qword [libc.dprintf] + clc + jmp epilog +purge tile_p +purge tile_node_bytes_n +purge idx_slot_p +purge rbx_save +purge r12_save +purge r13_save +purge r14_save +end namespace ; node_import +;=================================================================================================== +align_nops +way_import_no_idx: namespace way_import_no_idx +; rdi = tile_p +; rsi = pbf_way_p +; expect the tile properly inited +; TODO: one day, move as many stack variables as possible to rbx, +; r12,r13,r14,15 (yes, sacrifice the r15 flags) +define rbx_save rsp + 8 * 0 +define r12_save rsp + 8 * 1 +define tile_p rsp + 8 * 2 +define pbf_way_p rsp + 8 * 3 +define tile_way_bytes_n rsp + 8 * 4 +define way_nodes_finish_p rsp + 8 * 5 +define tile_node_of rsp + 8 * 6 + enter 8 * 7, 0 + and rsp, not 0xf ; align the stack for the external calls + mov [rbx_save], rbx + mov [r12_save], r12 + mov [tile_p], rdi + mov [pbf_way_p], rsi + +define pbf_way_keys_vals_p rbx +define keys_vals_bytes_n r12 + ; compute the (key/val)s bytes n = offset of tile way nodes + mov rcx, qword [rsi + blk.primblk.primgrp.ways.array.way_t.nodes] ; the nodes come after the (key/val)s in the pbf way + lea pbf_way_keys_vals_p, qword [rsi + blk.primblk.primgrp.ways.array.way_t.keys_vals] + sub rcx, pbf_way_keys_vals_p ; = (key/val)s bytes n + ; we don't test here we actually have (key/val)s to write since way-only features have some + mov keys_vals_bytes_n, rcx + add rcx, 8 ; add the field containing the offset of the array of nodes of the tile way, which will come afte the (key\val)s + mov [tile_way_bytes_n], rcx + + ; write the offset of the array of the nodes in the tile way, right after the (key/val)s + mov rdi, [rdi + tile_t.ways.fd] + lea rsi, [tile_way_bytes_n] + mov rdx, 8 + mov rax, linux.write + syscall + cmp rax, linux.errno.last + jae err_write_nodes_of_failed + + ; write the pbf way (key/val)s in the tile way (key/val)s + mov rdi, qword [tile_p] + mov rdi, qword [rdi + tile_t.ways.fd] + mov rsi, pbf_way_keys_vals_p + mov rdx, keys_vals_bytes_n + mov rax, linux.write + syscall + cmp rax, linux.errno.last + jae err_write_keys_vals_failed +purge pbf_way_keys_vals_p +purge keys_vals_bytes_n + ; way nodes loop -- start ------------------------------------------------------------------ +define way_nodes_p rbx +define way_node_p r12 + mov rax, qword [pbf_way_p] + mov way_nodes_p, qword [rax + blk.primblk.primgrp.ways.array.way_t.nodes] + mov rax, qword [rax + blk.primblk.primgrp.ways.array.way_t.next] + mov [way_nodes_finish_p], rax + jmp loop_entry +align_nops +next_node: + add way_nodes_p, 8 +loop_entry: + cmp way_nodes_p, qword [way_nodes_finish_p] + je nodes_imported + mov way_node_p, [way_nodes_p] ; load the node ptr + test way_node_p, way_node_p ; a node ptr can be 0 + jz next_node + mov rdi, qword [tile_p] + mov rsi, way_node_p + call node_import + jnc epilog + mov [tile_node_of], rax + ; write the tile node offset in the tile array into the tile way array of tile nodes + mov rdi, qword [tile_p] + mov rdi, qword [rdi + tile_t.ways.fd] + lea rsi, [tile_node_of] + mov rdx, 8 + mov rax, linux.write + syscall + cmp rax, linux.errno.last + jae err_write_tile_node_of_failed + add qword [tile_way_bytes_n], 8 ; 1 tile node offset is 8 bytes more to the tile way + jmp next_node +purge way_nodes_p +purge way_node_p + ; way nodes loop -- end -------------------------------------------------------------------- +align_nops +nodes_imported: + mov rdi, [tile_p] + mov rcx, [tile_way_bytes_n] + mov rax, [rdi + tile_t.ways.of] ; return the offset of this tile_way to the caller + add [rdi + tile_t.ways.of], rcx ; now, update the offset tracker + stc +epilog: + mov rbx, qword [rbx_save] + mov r12, qword [r12_save] + leave + ret +err_write_keys_vals_failed: ; errno in rax + mov rdi, 2 ; stderr + lea rsi, [msg.err_write_keys_vals_failed] + mov rdx, rax ; errno + call qword [libc.dprintf] + clc + jmp epilog +err_write_nodes_of_failed: ; errno in rax + mov rdi, 2 ; stderr + lea rsi, [msg.err_write_nodes_of_failed] + mov rdx, rax ; errno + call qword [libc.dprintf] + clc + jmp epilog +err_write_tile_node_of_failed: ; errno in rax + mov rdi, 2 ; stderr + lea rsi, [msg.err_write_tile_node_of_failed] + mov rdx, rax ; errno + call qword [libc.dprintf] + clc + jmp epilog +purge rbx_save +purge r12_save +purge tile_p +purge pbf_way_p +purge tile_way_bytes_n +purge way_nodes_finish_p +purge tile_node_of +end namespace ; way_import_no_idx +end namespace ; from_pbf +;=================================================================================================== +end namespace ; tile diff --git a/x86_64_sse2_x87/pbf/features/tile_column_new.s b/x86_64_sse2_x87/pbf/features/tile_column_new.s new file mode 100644 index 0000000..fcc6e8b --- /dev/null +++ b/x86_64_sse2_x87/pbf/features/tile_column_new.s @@ -0,0 +1,81 @@ +; vim: set filetype=fasm foldmethod=marker commentstring=;%s colorcolumn=101 : +align_nops +tile_column_new: namespace tile_column_new +; from select +define flags r15 +; from ways +define ways_finish_p rbx +define way_start_p r12 +; from ways used only if a bbox is used +define sw xmm3 +define ne xmm2 +; from tile_intersector +define cache_tile xmm1 +define way_nodes_p r13 +define way_nodes_finish_p r14 +define way_node_p rbp +define scaled_zoom_p rdi +; from tile lookup +define xs_p r8 +define x r9 +define ys_p r10 ; out + ; save and align the stack ptr + movq xmm7, rsp + and rsp, not 0xf + ; save callee clobbered regs + movq xmm6, scaled_zoom_p + movq xmm5, way_node_p + movq xmm4, xs_p + ; xmm3 and xmm2 could be booked if we have a bbox + movq xmm0, x + ; compute the array sz in bytes + mov rax, [ctx] + movzx rcx, byte [rax + ctx_t.zoom] + add cl, tile_t.bytes_n_log2 + mov rsi, 1 + ; 1 << (zoom+tile_t.bytes_n_log2) = (1 << zoom) * 2^tile_t.bytes_n_log2 = column_tiles_n * tile_bytes_n + shl rsi, cl ; = tile_column_bytes_n + + xor edi, edi + ; rsi = sz of xy array in bytes + mov rdx, 1b or 10b ; PROT_READ | PROT_WRITE + mov r10, 0x22 ; MAP_PRIVATE | MAP_ANONYMOUS + xor r8d, r8d + xor r9d, r9d + mov rax, linux.mmap + syscall + cmp rax, linux.errno.last + jae err_features_ys_mmap_failed ; no return + ; The kernel will zero the new mem pages. Restore callee clobbered regs. + movq x, xmm0 + movq xs_p, xmm4 + movq way_node_p, xmm5 + movq scaled_zoom_p, xmm6 + movq rsp, xmm7 + ; stitch regs for the jmp back and save the ptr of the array of column tiles + mov [xs_p + 8 * x], rax + mov ys_p, rax + jmp tile_intersector.tile_column_lookup_done ; this is where we jmp back to tile_lookup + +err_features_ys_mmap_failed: ; ouch, don't bother + mov rdi, 2 ; stderr + mov rsi, rax ; errno + lea rsi, [msg.ys_mmap_failed] + movq rdx, xmm0 ; = x + call qword [libc.dprintf] + movq rsp, xmm7 ; restore only rsp + jmp tile_db_build.exit_nok ; no return +purge flags +purge ways_finish_p +purge way_start_p +purge sw +purge ne +purge cache_tile +purge way_nodes_p +purge way_nodes_finish_p +purge way_node_p +purge scaled_zoom_p +purge xs_p +purge x +purge ys_p +end namespace diff --git a/x86_64_sse2_x87/pbf/features/zoom/lvl12/nodes/text.s b/x86_64_sse2_x87/pbf/features/zoom/lvl12/nodes/text.s new file mode 100644 index 0000000..cd78b69 --- /dev/null +++ b/x86_64_sse2_x87/pbf/features/zoom/lvl12/nodes/text.s @@ -0,0 +1,7 @@ +; vim: set filetype=fasm foldmethod=marker commentstring=;%s colorcolumn=101 : +nodes: namespace nodes +select: + clc ; not selected + jmp features.nodes.zoom_selection_done +end namespace ; zoom.lvl12.nodes + diff --git a/x86_64_sse2_x87/pbf/features/zoom/lvl12/relations/text.s b/x86_64_sse2_x87/pbf/features/zoom/lvl12/relations/text.s new file mode 100644 index 0000000..19139a5 --- /dev/null +++ b/x86_64_sse2_x87/pbf/features/zoom/lvl12/relations/text.s @@ -0,0 +1,6 @@ +; vim: set filetype=fasm foldmethod=marker commentstring=;%s colorcolumn=101 : +relations: namespace relations +select: + clc ; not selected + jmp features.relations.zoom_selection_done +end namespace ; zoom.lvl12.relations diff --git a/x86_64_sse2_x87/pbf/features/zoom/lvl12/text.s b/x86_64_sse2_x87/pbf/features/zoom/lvl12/text.s new file mode 100644 index 0000000..7d945a6 --- /dev/null +++ b/x86_64_sse2_x87/pbf/features/zoom/lvl12/text.s @@ -0,0 +1,6 @@ +; vim: set filetype=fasm foldmethod=marker commentstring=;%s colorcolumn=101 : +lvl12: namespace lvl12 +include 'ways/text.s' +include 'relations/text.s' +include 'nodes/text.s' +end namespace diff --git a/x86_64_sse2_x87/pbf/features/zoom/lvl12/ways/text.s b/x86_64_sse2_x87/pbf/features/zoom/lvl12/ways/text.s new file mode 100644 index 0000000..1dfb5fc --- /dev/null +++ b/x86_64_sse2_x87/pbf/features/zoom/lvl12/ways/text.s @@ -0,0 +1,77 @@ +; vim: set filetype=fasm foldmethod=marker commentstring=;%s colorcolumn=101 : +ways: namespace ways +; from select +define flags r15 +; from features way +define ways_finish_p rbx +define way_start_p r12 +; local +define keys_vals_p r13 +define mask7b r14 +align_nops +select: + lea keys_vals_p, [way_start_p + blk.primblk.primgrp.ways.array.way_t.keys_vals] + mov mask7b, 0x00ffffffffffffff ; 7 bytes str +align_nops +.next_key_val: + movzx rcx, byte [keys_vals_p] + test cl, cl + jz .not_selected ; 0-szed key terminator + ;{{{ key="highway" ------------------------------------------------------------------------- + cmp cl, lengthof 'highway' + jne .skip_from_key_sz_byte + inc keys_vals_p ; reach the key first char + mov rax, [keys_vals_p] + and rax, mask7b + mov rdx, 'highway' + cmp rax, rdx + jne .skip_from_key_first_char + add keys_vals_p, rcx ; reach the val sz word + ;{{{ "highway" vals ------------------------------------------------------------------------ + movzx rcx, word [keys_vals_p] + test cx, cx + jz .skip_empty_val; 0-szed val + ;{{{ val="motorway" + cmp cx, lengthof 'motorway' + jne .skip_from_val_sz_word + add keys_vals_p, 2 ; reach the val first char + mov rax, 'motorway' + cmp [keys_vals_p], rax + jne .skip_from_val_first_char + ; (highway,motorway) + lea rax, [motorway.import]; install the way-feature import func for a motorway + mov [features.select.ways.import.fn], rax + jmp .selected + ;}}} val="motorway" + ;}}} "highway" vals ------------------------------------------------------------------------ + ;}}} key="highway" ------------------------------------------------------------------------- +align_nops +.skip_from_key_sz_byte: + inc rcx +.skip_from_key_first_char: + add keys_vals_p, rcx ; skip key + movzx rcx, word [keys_vals_p] +.skip_from_val_sz_word: + add rcx, 2 +.skip_from_val_first_char: + add keys_vals_p, rcx ; skip val + jmp .next_key_val +.skip_empty_val: + add keys_vals_p, 2 ; skip sz word + jmp .next_key_val +align_nops +.not_selected: + clc + jmp features.ways.zoom_selection_done +.selected: + stc + jmp features.ways.zoom_selection_done +purge flags +purge ways_finish_p +purge way_start_p +purge keys_vals_p +purge mask7b +;=================================================================================================== +align_nops +motorway_import: +end namespace ; zoom.lvl12.ways diff --git a/x86_64_sse2_x87/pbf/features/zoom/text.s b/x86_64_sse2_x87/pbf/features/zoom/text.s new file mode 100644 index 0000000..f714c1a --- /dev/null +++ b/x86_64_sse2_x87/pbf/features/zoom/text.s @@ -0,0 +1,4 @@ +; vim: set filetype=fasm foldmethod=marker commentstring=;%s colorcolumn=101 : +zoom: namespace zoom +include 'lvl12/text.s' +end namespace diff --git a/x86_64_sse2_x87/pbf/text.s b/x86_64_sse2_x87/pbf/text.s new file mode 100644 index 0000000..bafab18 --- /dev/null +++ b/x86_64_sse2_x87/pbf/text.s @@ -0,0 +1,410 @@ +; vim: set filetype=fasm foldmethod=marker commentstring=;%s colorcolumn=101 : +pbf: namespace pbf +;--------------------------------------------------------------------------------------------------- +init_once: + call blob.init_once + jnc .nok + call blk.init_once + jnc .nok + stc + ret +.nok: + clc + ret +;--------------------------------------------------------------------------------------------------- +define file_path rbx +define file_fd r12 +file_mmap: + mov rbp, rsp ; align the stack now for the external calls + and rsp, not 0xf + + mov rax, [ctx] + mov file_path, [rax + ctx_t.file_path] + + lea rdi, [msg.opening] + mov rsi, file_path + call qword [libc.printf] + + mov rax, linux.open + mov rdi, file_path + mov rsi, 2000000o ; flags O_RDONLY | O_CLOEXEC + xor edx, edx ; mode + syscall + cmp rax, linux.errno.last + jae .err_open_failed + mov [fd], rax + mov file_fd, rax + + mov rax, linux.fstat + mov rdi, file_fd + lea rsi, [stat] + syscall + cmp rax, linux.errno.last + jae .err_stat_failed + + lea rdi, [msg.stat_sz] + mov rsi, file_path + mov rdx, qword [stat + linux.stat_t.sz] + call qword [libc.printf] + + mov rax, linux.mmap + xor edi, edi + mov rsi, qword [stat + linux.stat_t.sz] + mov rdx, 1 ; PROT_READ + mov r10, 2 ; MAP_PRIVATE + mov r8, [fd] + xor r9d, r9d + syscall + cmp rax, linux.errno.last + jae .err_mmap_failed + mov [start], rax + + lea rdi, [msg.mmap_addr] + mov rsi, [start] + call qword [libc.printf] + + stc + mov rsp, rbp + ret +.err_mmap_failed: ; errno in rax + lea rdi, [msg.mmap_failed] + mov rsi, file_path + mov rdx, rax + call qword [libc.printf] + clc + mov rsp, rbp + ret +.err_stat_failed: ; errno in rax + lea rdi, [msg.stat_failed] + mov rsi, file_path + mov rdx, rax + call qword [libc.printf] + clc + mov rsp, rbp + ret +.err_open_failed: ; errno in rax + lea rdi, [msg.open_failed] + mov rsi, file_path + mov rdx, rax + call qword [libc.printf] + clc + mov rsp, rbp + ret +purge file_path +purge file_fd +;--------------------------------------------------------------------------------------------------- +define ctx_p rbx +ctx_print: + mov rbp, rsp ; align the stack now for external calls + and rsp, not 0xf + + mov ctx_p, [ctx] + lea rdi, [msg.ctx.start] + call qword [libc.printf] + + lea rdi, [msg.ctx.general] + mov rsi, [ctx_p + ctx_t.file_path] + mov rdx, [ctx_p + ctx_t.tile_db_dir.path] + movzx rcx, byte [ctx_p + ctx_t.zoom] + call qword [libc.printf] + + mov rax, [ctx_p + ctx_t.flags] + ; only print the bbox if we have at least one user provided geo coord + and qword [ctx_p + ctx_t.flags], 0xf + jz .finish + ;------------------------------------------------------------------------------------------- +.swlat: + lea rdi, [msg.ctx.bbox.swlat] + movsd xmm0, qword [ctx_p + ctx_t.bbox.sw.lat] + mov rax, 1 + bt qword [ctx_p + ctx_t.flags], 0 + jc .swlat_user_provided + lea rsi, [msg.ctx.bbox.adjust] + jmp .swlat_print +.swlat_user_provided: + lea rsi, [msg.ctx.bbox.user] +.swlat_print: + call qword [libc.printf] + ;------------------------------------------------------------------------------------------- +.swlon: + lea rdi, [msg.ctx.bbox.swlon] + movsd xmm0, qword [ctx_p + ctx_t.bbox.sw.lon] + mov rax, 1 + bt qword [ctx_p + ctx_t.flags], 1 + jc .swlon_user_provided + lea rsi, [msg.ctx.bbox.adjust] + jmp .swlon_print +.swlon_user_provided: + lea rsi, [msg.ctx.bbox.user] +.swlon_print: + call qword [libc.printf] + ;------------------------------------------------------------------------------------------- +.nelat: + lea rdi, [msg.ctx.bbox.nelat] + movsd xmm0, qword [ctx_p + ctx_t.bbox.ne.lat] + mov rax, 1 + bt qword [ctx_p + ctx_t.flags], 2 + jc .nelat_user_provided + lea rsi, [msg.ctx.bbox.adjust] + jmp .nelat_print +.nelat_user_provided: + lea rsi, [msg.ctx.bbox.user] +.nelat_print: + call qword [libc.printf] + ;------------------------------------------------------------------------------------------- +.nelon: + lea rdi, [msg.ctx.bbox.nelon] + movsd xmm0, [ctx_p + ctx_t.bbox.ne.lon] + mov rax, 1 + bt qword [ctx_p + ctx_t.flags], 3 + jc .nelon_user_provided + lea rsi, [msg.ctx.bbox.adjust] + jmp .nelon_print +.nelon_user_provided: + lea rsi, [msg.ctx.bbox.user] +.nelon_print: + call qword [libc.printf] + ;------------------------------------------------------------------------------------------- +.finish: + lea rdi, [msg.ctx.finish] + call qword [libc.printf] + ; we flush stdout + mov rax, [libc.stdout] + mov rdi, [rax] + call [libc.fflush] + mov rsp, rbp + ret +purge ctx_p +;--------------------------------------------------------------------------------------------------- +define ctx_p rbx +ctx_bbox_adjust: + mov ctx_p, [ctx] +.sw_lat: + bt qword [ctx_p + ctx_t.flags], 0 + jc .sw_lon + mov rax, -90.0 * 1000000000; f64 + mov [ctx_p + ctx_t.bbox.sw.lat], rax +.sw_lon: + bt qword [ctx_p + ctx_t.flags], 1 + jc .ne_lat + mov rax, -180.0 * 1000000000 + mov [ctx_p + ctx_t.bbox.sw.lon], rax +.ne_lat: + bt qword [ctx_p + ctx_t.flags], 2 + jc .ne_lon + mov rax, 90.0 * 1000000000 + mov [ctx_p + ctx_t.bbox.ne.lat], rax +.ne_lon: + bt qword [ctx_p + ctx_t.flags], 3 + jc .epilog + mov rax, 180.0 * 1000000000 + mov [ctx_p + ctx_t.bbox.ne.lon], rax +.epilog: + ret +purge ctx_p +;--------------------------------------------------------------------------------------------------- +; scratch: rax, rdx, rcx +define ctx_p rbx +wm_scaled_zoom_compute: + mov ctx_p, [ctx] + movzx rcx, byte [ctx_p + ctx_t.zoom] ; zoom is <= 255 + mov rax, 1 + shl rax, cl ; 1< 4 + err 'TRACE handle only 4 registers',10 + end if + dest_reg_num = % + + ; we must go internals because we could use 'register labels' a 64bits register is + ; an element with its metadata being a polynomial of element x86.r64 with its + ; constant being the register idx. + reg_idx = (reg metadata 1) scale 0 + if reg_idx < 0 | 15 < reg_idx + err 'BAD REG IDX',10 + end if + iterate dest_reg, rdx, rcx, r8, r9 + if dest_reg_num = % + mov dest_reg, [greg_vals + 8 * reg_idx] + break + end if + end iterate + end iterate + + mov rbp, rsp + and rsp, not 0xf + mov rdi, 2 ; the unbuffered stderr + lea rsi, [fmt_store] + call qword [libc.dprintf] + mov rsp, rbp + + iterate reg, xmm0, xmm1, xmm2, xmm3, xmm4, xmm6, xmm7 + movdqa reg, [dqreg_vals + 16 * (% - 1)] + end iterate + iterate reg, rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi, r8, r9, r10, r11, r12, r13, r14, r15 + mov reg, [greg_vals + 8 * (% - 1)] + end iterate + jmp exit +if prefix <> '' ; cmp str tokens should be ok I guess +fmt_store db 'TRACE:',prefix,':',fmt,10,0 +else +fmt_store db 'TRACE:',fmt,10,0 +end if +greg_vals dq 16 dup 0 +align 16 ; 128 bits +dqreg_vals ddq 8 dup 0 +exit: +end macro ; TRACE_PREFIX +;--------------------------------------------------------------------------------------------------- +if TRACE_DEBUG_ENABLED + macro T fmt, regs& + TRACE_PREFIX '',fmt, regs + end macro +else + macro T fmt, regs& + end macro +end if +;--------------------------------------------------------------------------------------------------- +if TRACE_GENERIC_ENABLED + macro TRACE fmt, regs& + TRACE_PREFIX '',fmt, regs + end macro +else + macro TRACE fmt, regs& + end macro +end if +;--------------------------------------------------------------------------------------------------- +macro zreg reg ; zero reg + if `reg = 'rax' + xor eax, eax + else if `reg = 'rbx' + xor ebx, ebx + else if `reg = 'rcx' + xor ecx, ecx + else if `reg = 'rdx' + xor edx, edx + else if `reg = 'rbp' + xor ebp, ebp + else if `reg = 'rsi' + xor esi, esi + else if `reg = 'rdi' + xor edi, edi + else + xor reg, reg ; r8-r15, no gain at using eXX + end if +end macro +; heavything rip +; align_macros.inc: multibyte nop alignment macros +; carefull of the new indirect branch declaration "nop" from latest micro-architecture +macro align_nops + local a + virtual + align 16 + a = $ - $$ + end virtual + if a = 15 + db 0x66, 0xf, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 + db 0x66, 0xf, 0x1f, 0x44, 0x00, 0x00 + else if a = 14 + db 0x66, 0xf, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 + db 0xf, 0x1f, 0x44, 0x00, 0x00 + else if a = 13 + db 0x66, 0xf, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 + db 0xf, 0x1f, 0x40, 0x00 + else if a = 12 + db 0x66, 0xf, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 + db 0xf, 0x1f, 0x00 + else if a = 11 + db 0x66, 0xf, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 + db 0x66, 0x90 + else if a = 10 + db 0x66, 0xf, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 + db 0x90 + else if a = 9 + db 0x66, 0xf, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 + else if a = 8 + db 0xf, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 + else if a = 7 + db 0xf, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x00 + else if a = 6 + db 0x66, 0xf, 0x1f, 0x44, 0x00, 0x00 + else if a = 5 + db 0xf, 0x1f, 0x44, 0x00, 0x00 + else if a = 4 + db 0xf, 0x1f, 0x40, 0x00 + else if a = 3 + db 0xf, 0x1f, 0x00 + else if a = 2 + db 0x66, 0x90 + else if a = 1 + db 0x90 + end if +end macro +;=================================================================================================== +TSC='visible' +; TODO: get rid of struct and go of tbl +;if PERF_ENABLED +;;--------------------------------------------------------------------------------------------------- +;struc (name) TSC.STORAGE msg_str +; name.start dq 0 +; name.stop dq 0 +; name.msg db msg_str,10,0 +; align 16 +; name.gregs_save rq 16 +; name.vregs_save rdq 8 +;end struc +;;--------------------------------------------------------------------------------------------------- +;macro TSC.SAMPLE storage, dst +; iterate reg, rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi, r8, r9, r10, r11, r12, r13, r14, r15 +; mov [storage.gregs_save + 8 * (% - 1)], reg +; end iterate +; iterate reg, xmm0, xmm1, xmm2, xmm3, xmm4, xmm6, xmm7 +; movdqa [storage.vregs_save + 16 * (% - 1)], reg +; end iterate +; +; lfence +; rdtsc +; +; mov rcx, 0xffffffff +; and rax, rcx +; and rdx, rcx +; +; shl rdx, 32 +; or edx, eax +; mov [storage.dst], rdx +; +; iterate reg, xmm0, xmm1, xmm2, xmm3, xmm4, xmm6, xmm7 +; movdqa reg, [storage.vregs_save + 16 * (% - 1)] +; end iterate +; iterate reg, rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi, r8, r9, r10, r11, r12, r13, r14, r15 +; mov reg, [storage.gregs_save + 8 * (% - 1)] +; end iterate +;end macro +;;--------------------------------------------------------------------------------------------------- +;macro TSC.PRINT storage +; iterate reg, rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi, r8, r9, r10, r11, r12, r13, r14, r15 +; mov [storage.gregs_save + 8 * (% - 1)], reg +; end iterate +; iterate reg, xmm0, xmm1, xmm2, xmm3, xmm4, xmm6, xmm7 +; movdqa [storage.vregs_save + 16 * (% - 1)], reg +; end iterate +; +; mov rbp, rsp +; and rsp, not 0xf +; mov rdi, 2 ; arg0 = stderr +; lea rsi, [storage.msg] ; arg1 = msg +; mov rdx, [storage.stop] +; sub rdx, [storage.start] ; arg2 = TSC diff +; call qword [libc.dprintf] +; mov rsp, rbp +; +; iterate reg, xmm0, xmm1, xmm2, xmm3, xmm4, xmm6, xmm7 +; movdqa reg, [storage.vregs_save + 16 * (% - 1)] +; end iterate +; iterate reg, rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi, r8, r9, r10, r11, r12, r13, r14, r15 +; mov reg, [storage.gregs_save + 8 * (% - 1)] +; end iterate +;end macro +;else ; PERF_ENABLED +macro TSC.PRINT storage +end macro +macro TSC.SAMPLE storage, dst +end macro +struc (name) TSC.STORAGE msg_str +end struc +;end if +;=================================================================================================== -- 2.11.4.GIT