Justine Tunney 2020-11-06 20:02:53 -08:00
parent 2d80bbc802
commit 9fe95ef12b
22 changed files with 1511 additions and 1028 deletions

307
README.md
View File

@ -5,310 +5,3 @@ Fast portable static native textmode executable containers.
For an introduction to this project, please read the <a
href="https://justine.storage.googleapis.com/ape.html">αcτµαlly pδrταblε
εxεcµταblε</a> blog post.
Cosmopolitan builds run natively on most platforms without dependencies,
because it implements the subset of the C library, compiler runtimes and
system call interfaces that've achieved near universal consensus amongst
all major platforms, e.g. Linux/BSD/XNU/NT. Containerization is achieved
by having binaries also be ZIP files. Modern static stock GNU toolchains
are provided in a hermetic mono repo for the best historical determinism
Here's how you can get started by printing cat videos inside a terminal:
make -j8 o//tool/viz/printvideo.com
wget https://justine.storage.googleapis.com/cats.mpg
o//tool/viz/printvideo.com cats.mpg
unzip -vl o//tool/viz/printvideo.com
Cosmopolitan provides a native development environment similar to those
currently being offered by RedHat, Google, Apple, Microsoft, etc. We're
different in two ways: (1) we're not a platform therefore we don't have
any commercial interest in making our tooling work better on one rather
than another; and (2) we cater primarily towards features having gained
cross-platform agreement. Goal is software that stands the test of time
without the costs and restrictions cross-platform distribution entails.
That makes Cosmopolitan an excellent fit for writing small CLI programs
that do things like matrix multiplication relu stdio as a subprocess or
perhaps a web server having the minimum surface area that you require.
## Getting Started
Just clone the repository and put your own folder in it. Please choose a
name that's based on a .com or .org domain name registration you control
which is scout's honor but could change to verify TXT records in future.
We provide a script that makes it easy to start a new package:
examples/package/new.sh com/github/user/project
emacs com/github/user/project/program.c
make o//com/github/user/project/program.com
o//com/github/user/project/program.com
Please note GNU Make is awesome. Little known fact: make is a functional
programming language. We improved upon it too! In [tool/build/package.c]
which performs strict dependency checking, to correct Google's published
mistakes c. 2006 which was when they switched from using a GNU Make repo
in favor of an inhouse derivative called Blaze which does graph checking
thereby allowing the repository to grow gracefully with any requirements
## Performance
Your C Standard Library is designed to be competitive with glibc, which
has historically been the best. Routines like [libc/nexgen32e/memcpy.S]
are usually accompanied by microbenchmarks to demonstrate their merits.
Where GNU, LLVM and MSVC got lazy is intrinsics. Cosmopolitan does that
better. Your [ansitrinsics library] makes a 10x speedup so much easier,
using Intel's new instructions, in such a way that only allows Intel to
leverage their patents which have knowable expiry; rather than compiler
intrinsics API copyrights, which might never expire like Walt Disney IP
See [dsp/scale/cdecimate2xuint8x8.c] and [tool/viz/lib/ycbcr2rgb3.c] as
an example, of how 4k video convolutions needed to print cat videos can
be done from a single core without threads on a cheap computer, without
sacrificing backwards compatibility due to excellent microarchitectural
dispatch like [libc/nexgen32e/kcpuids.S], [libc/nexgen32e/x86feature.h]
Furthermore Cosmopolitan provides you with Intel's official instruction
length decoder Xed ravaged down to 3kb in size using Tim Patterson code
stunts. So you can absolutely code high-performance lightweight fabless
x86 microprocessors using any hobbyist board without royalties, because
Xed tells us which parts of the encoding space now belong to the people.
Please see [third_party/xed/x86ild.greg.c] to learn more.
## Integrated Development Environment
Your Cosmopolitan IDE is based on whichever editor you love most. Emacs
configs are provided, showing how `make tags` can be used to automate
certain toil, such as adding include lines by typing `C-c C-h` over a
symbol. We recommend trying the following:
sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)"
sudo apt install gdb ragel ctags clang-format-10
git clone git@github.com:jart/cosmopolitan.git && cd cosmopolitan
tool/scripts/install-emacs.sh # Emacs 26.1 has a bug
tool/scripts/configure-emacs.sh # adds load statements
make tags # index all the symbol
emacs # for power and glory!
See [tool/emacs/cosmo-stuff.el] for further details. Further note that
this codebase might seem to have unusual numbers of include statements
quoted and vendored code, but that's actually why the tools work since
it gives them easy perfect knowledge of what exists, thus performance.
## Specimen
Cosmopolitan encodes binaries respecting the intersection of reasonable
platform requirements. RADIX-256 disassembly of [examples/life.c] below
should explain how easy it is to do pe+sh+elf+macho+bootloader overlays
enabling gnu ld.bfd to produce a simple portable 12kb .com program file
Please send feedback and concerns to <jtunney@gmail.com> since we would
love to do an even better job. Please also let us know if some goofball
is distributing without bundling their source code, so we can reach out
and ask if they would like to purchase a commercial license.
make -j10 -O MODE=tiny CPPFLAGS=-DIM_FEELING_NAUGHTY
o/tiny/tool/viz/bing.com <o/tiny/examples/life.com |
o/tiny/tool/viz/fold.com
MZqFpD=‘◙  ► °       ☺ ◘@           JT                      p♂  
▓@δ δ◄ÉÉδ♥R1╥╜  δ♣Θ⌐☼  ⁿ┐ p1╔·Ä╫ë╠√♫XΦ  ^üεh ▒♦╤εâΘ☺u∙╣ ☻┐ ♦Ä▐Ä╟
1÷1λ≤ñΩï@   ■≤ñ1φÄ▌Φ  σ 0╕ ☺Ä└1└1λ≤¬Ç·@t#Φ.  ♦Ä└┐↑ 0÷1╔ë°Φ]
 î╞â╞ Ot•Hu≈Ä╞δ∞ë♫@2ë▬B2Ω3E  ◙SR┤◘═‼r,ê╧Çτ⁇Çß└╨┴╨┴å═▲♠▼1÷Ä╞╛ 2ç≈
ÑÑÑÑÑñ▼ô½æ½Æ½X¬Æ[├ZÇ≥Ç1└═‼r≈δ┴S1█è▲♀29├w☻ë╪QP╗ ►î└)├üπλ☼╣♣ ╤δâΘ☺
u∙XYà█t♠9├w☻ë╪PQå═╨╔╨╔Ç╔☺1█┤☻═‼Y[r‼┤ 9├|♪A;♫♪2~♦1╔■╞[├P1└═‼Xδ╨  
                                                                
                                                              
‘◙#“◙o=“$(command -v “$0“)“◙set -- “$o“ “$@“◙if [ -d /Applicati
ons ]; then◙dd if=“$o“ of=“$o“ bs=8 skip=“ 410“ count=“
87“ conv=notrunc 2>/dev/null◙elif exec 7<> “$o“; then◙printf ‘╲
177ELF╲2╲1╲1╲011╲0╲0╲0╲0╲0╲0╲0╲0╲2╲0╲076╲0╲1╲0╲0╲0╲134╲022╲100╲0
00╲000╲000╲000╲000╲150╲012╲000╲000╲000╲000╲000╲000╲000╲000╲000╲0
00╲000╲000╲000╲000╲0╲0╲0╲0╲100╲0╲070╲0╲004╲000╲0╲0╲000╲000╲000╲0
00 >&7◙exec 7<&-◙fi◙exec “$@“◙R=$⁇◙if [ $R -eq 126 ] && [ “$(un
ame -m)“ != x86_64 ]; then◙if Q=“$(command -v qemu-x86_64)“; the
n◙exec “$Q“ “$@“◙else◙echo error: need qemu-x86_64 >&2◙fi◙fi◙exi
t $R◙αcτμαlly pδrταblε εxεcμταblε♪◙ error:  ♪◙ cpuid oldskool ds
knfo e820 nomem nolong hello◙   Cf☼▼D  8 ╕D      f☼▼D          
λλ   Ü☼ λλ   Æ☼ λλ   Ü╧ λλ   Æ╧ λλ   ¢» λλ   ô» ☻░¡←   ■OQΣ≡♦  
    0►  @► ►♣  =☻░¡+☼à↑♪  j ¥▓@☼ └âα■☼“└fΩW@  ÉÉÉÉUëσΦ§♪Φ↓ ┐• î
♠▬2ú↑2Φ(☺┐EDΦÑ Φ╨♀Φ→☻Uëσ╣♦  0¼ê┬¼ê╞à╥t♀QVë╫╛€DΦ○ ^YâΘ☺uσ]├Uëσë·
à╥t↑RV1╔▒♥☺╩¼^♀ÇεZ¼εâ┬☺âΘ☺y÷]├UëσΦ↕ Φ  σ 0╛♦ ΦÇ ΦÉ☺Uëσï╖☻0à÷u
♦ï6 01└PV╕lDPVWV╕dDP_àλt♠^Φ♦ δ⌡]├UëσWVΦ♂ ^_à÷t♥Φ↨ ]├Uëσë■─>▬2Φ╕ 
î♠▬2ú↑2]├UëσSë√ç▐¼ç▐ä└t↑ë╟VP╕  δ◘XΦ.   δ♦XΦ( ^δ▀[]├ë±ë■1╥¼ê┬¼ê╞à
└t◄â┬♣┤@∞ α≤Ét∙âΘ☺uσ├PQRë≥â┬♣┤ ∞ αu♦≤Éδ≈ë°ë≥εZYX├UëσS┤╕░ Ä└ë°<♥t
☻0φ1λj X═►┤☺░ ╡ ▒ ═►┤►░♥╖  ═►1└[]├Uëσâ∞►ûë∙ëμëτ¬ë╧j☺Zδ♫V¼ä└u√ë≥
^)≥UëσSë╤ë·ÇΓ ÇμÇÇ┬áÇ╓☼╖ │á9·t)w↕¼<◙t↕<♪t↕¬░•¬âΘ☺uΦë°[]├☺▀δ≥ë°R1
╥≈√)╫ZδμPQRVë▐P╕  δ◘XΦ♫   δ♦XΦ◘ ë╟^ZYXδ╞≈╥ë∙Çß ÇσÇç╧)±Q)∙ë■☺╬╕  
▲•≤ñX├Uëσ S1█═§r→ü√MPu¶╕☺S1█═§1█1╔╕•S│☺▒♥═§Φ╢◙UëσΦ↔ Φy☺┐ ►╛  Φz
 r○Φ╟ Φ∞ Φ☻╕àDΦ♀■Uëσ£X% puMf£fXfë┴f╗    f1╪fPf¥f£fXf9┴t:f!╪fPf¥
f┐   Çfë°fG☼óf9°|∟fë°☼óf┐    f!·f9·u○1└]├╕uDδ◙╕ÉDδ♣╕oDδ Φú²Uëσfh
PAMSS┴∩♦Ä╟f1λf1█f╕ Φ  f╣↑   fïVⁿ═§r&f;Fⁿu à╔t♫â∙§r♠÷E¶☺u♥â╟↑fà█t
♦9≈r╦ë°[╔├∙δ·Uëσ·☼☺▬¿D☼ └♀☺☼“└δ ╣  Äß$■☼“└fΩrH    √]├UëσSf╛ @  f
   f╣↑   f┴ß○fâ╩ⁿfâ┬♦f9╤t♫dgfï♦▬dgfë♦‼δΘgfì∟‼┐   @Ä└ï♫@2ï▬B2à
λt+ë°S1█ΦS°[)╟Që┴┴ß○1÷&fï♦dgfë♥fâ├♦â╞♦âΘ♦uδYδ╤[]├·▲1└Ä└HÄ╪┐ ♣╛►♣
&è♣Pè♦P&╞♣ ╞♦λ&Ç=λXê♦X&ê♣▼u@╕☺ Φ, ░¡μdΦ% ░╨μdΦ% Σ`PΦ↑ ░╤μdΦ◄ X♀☻
μ`Φ○ ░«μdΦ☻ δúΣd¿☻u·├Σd¿☺t·├√├Uëσ▲╕ lÄ╪f╟♠ 0♥α f╟♠  ♥╨♠ f╟♠ ►♥└
  ☺f╕♥   1÷fë♦f♣   â╞◘âΘ☺u∩▼f╟♠ 2 └♠ f╕ ≡♠ ☼“╪]├·☼☺▲02Φóλ☼ αf
♪á☻  ☼“αf╣Ç  └☼2f♪☺☺  ☼0☼☺▬¿D☼ └f♪♥  Çfâα√☼“└Ω≥I( ╕0   Ä╪ÄαÄΦ1╥δ
 HâΣ≡1φ1λΦ]•  ┐ ►  Φ{•  ┐ ►  ╛ ≡♠ ║ 2  Φ≡♣  ┐ùD  ï4% 0  Φ╠√λλ┐ 0
  ╛♦   Φτ√λλΘΓ•  Hì§█•  Φλ•  Θ╤•  É☼▼D                  @     
  ►                      ►      ☺   ♠             @       ►     
 ►                     Qσtd♠                                   
                     P♂      P♂@     P♂►                   
       É☼▼Ç             OpenBSD     É☼▼Ç    PE  då☻ k↕d╲    
    ≡ #☻♂☻♫☼            ╞¶        @      ►   ►  ♠       ♠       
 @   ►      ♥  ☺  ►       ♥       ◘      ►          ►           
ä←  (                                                           
                        ►   @                           .text   
 ►   ►   ►   ►              `  p.data            ►              
      └É☼▼Ç    ╧·φ■•  ☺♥         ÿ☻            H   __PAGEZE
RO                                                      ↓   ÿ   
__TEXT            @                             •   ♣   ☺       
__text          __TEXT           ►@      ►       ►  ♀           
                  Φ   __DATA            @                     
                    __data          __DATA            @     
 ►                                     __bss           __DATA  
         0@      ►          ♀           ☺               ←   ↑   
B)→☺&¿◘ºB)→☺&¿◘º♣   ╕   ♦   *             @                     
                                                                
                                S↕@                             
        Éf.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä
     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f☼▼ä     
ZAR╕    Hà└t◙Hà└t♣Θ»♦  Θ@☻  UHë╤HëσAWIë≈AVA╛  @ AUIë²ATA╝   SHë
√Hâ∞↑Hâ{◘ ☼ä╕   ïS►Hë╪Hâ├↑λ╩t○Hâ{◘ uδδ♥Hë├L9#L☼C#Iü─λ☼  IüΣ ≡λλH
ïC◘H♥♥H% ≡λλI9─snIïU◘Hà╥t↓Aâ}►☺u♠Iâ┼↑δΩIïE H☺╨L9αrεIïE L9αw◄H☺╨L
9αv○Lìáλ☼  δ½Lë·Lë≈╛♥   HëM╚Φ6   LëΓHïM╚Iü╞   Hâ╩♥Iü─   Hë►δÇH
â├↑Θ=λλλHâ─↑[A╲A]A^A_]├I╣ ≡λλλλ♥ Ië╚╣   Hë°λ╬H╙Φ%λ☺  â■λu♣Hì♦┬├
Hì¶┬Hâ: u‼Iï H-    Hâ╚♥Hë☻Hï☻âΘ○L!╚Hë┬δ╛U╣ 2   ≡♠ ╛♥   σSH
ë√PΦÅλλλHâ ■☼☺;Z[]├1÷ë≡Hk└↑Hâ|•◘ t♦λ╞δεA╕☺   HìW↑A9≡r☺├UHëσATSLï
◙HïZ◘Hë╤Dë└DïZ►DïR¶Aë─λ╚L;IΦt→☼►AΦLïa°HâΘ↑☼◄A↑Lëa(à└u▌δ♥DëαHk└↑A
λ└Hâ┬↑H☺°Lë◘HëX◘DëX►DëP¶A9≡rí[A╲]├1└├UëσSç█[fÉ╠·⌠δ²Uëσâ∞◘U^ì~°j◘
Y1└≤¬·☼☺^°☼♂δ≈Uëσ]├╞♣ª↔  ◘δ►HàλH☼Eτt•╞♣ö↔   Lc$$Lìl$◘NìtΣ↑╕“↕@ ┐
► @ ╣P @ H)∙┴Θ♥≤H½1└â╔λLë≈≥H»IëλΘ╧   UHëσ 0@ ╛°←@ ╣00@ HìA►Hë☺╟
A◘   SV÷•◘u<÷• u!÷•♦u◄Iâ⁇ t!j j☺╛ⁿ←@ δ*j“j♦╛☻∟@ δ▼j0j ╛∙←@ δ¶j*
j►╛λ←@ δ○jEj◘╛♦∟@ ╣q↓@ XH½XH☺╚H½W┐↑0@ Iâ╔λ╗(0@ H9▀s51╔1╥¼Ië└Aâα
I╙αâ┴•L○┬ä└xδ¿@t○Lë╚H╙αH○┬Hë╨Hâ⁇ H☼E•H½δ╞_^[╔├╕*   ├HâΣ≡1φ╗  @ Φ
!λλλâ♪┴▼  ☺╕◘ @ ╣◘ @ H9┴t♀PQλ►YXHâ└◘δ∩ÉLëτLëεLë≥Φ╣λλλë╟Φ    UHëσ
ATAëⁿ1λPΦr   DëτΦö♠  1÷1╥UHëσAW╣00@ Ië╧Iï•IïO◘Hà╔t∟HâΘ↑Hë¶◘Hët◘◘
Hë|◘►IëO◘1└A_]├╣    Hà╔t≥PWVj!_j►^λ╤^_YHà└t▀I╟└   Ië•Jë♀ Lë┴δ┤U
σAVAUATHï5α  Ië■Ië⌠j YH¡HÆH¡HùH¡Hà└t↑Mà÷δ♣L9≥u♫VQQλ╨YY^1└HëF°
âΘ☺u╘H¡Hà└t◄Mà÷t○PLëτΦfδ┐λ^δ╢A╲Mà÷u←╕► @ ╣► @ HâΦ◘H9╚|◘PQλ►YXδ∩A
]A^]├╠U╣   1└HëσAWAVAUATSHü∞Hα  Hëà└▼λλHëà╚▼λλλ§C♂  Hâ─ à└uGHâ∞
H╟┴⌠λλλ맓♂  Hâ─↑A╕♠   Lìì╨⌂λλj Hë┴║⌐→@ Hâ∞ λ§♪♂  XZ╣☺   λ§╨◙  
Hâ─ Hâ∞ j♦Xë♣»→  λ§╔◙  Ië─λ§╚◙  LëτHâ─ A╕   Hìì╨/λλ║λ⁇  Hì╡╤⁇λλ
Ië┼Φ╙☺  Aë─1└Hïì╨/λλH☺┴è◄ä╥t♪Ç·╲u♥╞☺/Hλ└δπA☼╖E f=λ╫w♪ëà╝▼λλ╕☺   
δ☼Hì╡╝▼λλLë∩Φ0♦  ë└Hìì╨⌂λλE1└1█H☺└Ië╦LìU╬A╛²⌂  IìT♣ Hì╡╝▼λλâ╜╝▼λ
λ ☼äÅ   Iλ└Iü°λ☺  w↕L9╤Hë╪H☼B┴Jëä┼╚▼λλDïì╝▼λλIc┴Aâ∙⌂v◘Dë╧ΦÉ♥  L9
╤s♀Hλ┴êAλH┴Φ◘u∩☼╖☻f=λ╫w♪ëà╝▼λλ╕☺   δ◘Hë╫Φö♥  ë└H☺└H☺┬Eà╔u¼Hë╚L)╪
H=²⌂  I☼G╞╞ä♣╨⌂λλ ΘdλλλL9╤s♠╞☺ Hλ┴L)┘╕■⌂  Lì╡╨▼λλHü∙■⌂  Lì╜└▼λλH
☼G╚Iü°λ☺  ╕λ☺  L☼G└Hâ∞ ╞ä♪╨⌂λλ LëΘLì¡╨/λλJ╟ä┼╨▼λλ    λ§↔○  Θtⁿλλ
Hë·Hï⁇☼╖•f=λ╫w◙ëB↑╕☺   δ○Hìr↑Φ▀☻  ë└H☺└H☺☻├Hë·Hc╞ë≈LïB►â■⌂v♣Φì☻ 
 HïJ◘I9╚v►Hìq☺Hër◘ê☺H┴Φ◘uτ├UHì♦▬Ië╙Më┬HëσAWI┐ &     AVAUIë⌡ATS1
█Hâ∞8Hë}░Hì}░HëM¿Hëu╕HëE└Φbλλλâ}╚ t↔ïE╚à└t▬â° w╲I☼ú╟sVHì}░ΦAλλλδ
π1÷Hì}░Φ_λλλMà█t↨HïE╕Iλ╦L)ΦL9╪I☼G├A╞D♣  Mà╥☼äf☺  Iλ╩HïE¿I9┌L☼G╙J
╟♦╨    ΘK☺  Hλ├L9╙s§HïE╕H;E└r☻1└HïU¿HëD┌°E1÷ïu╚à÷☼ä►☺  Eä÷u¶â■ w
☼I☼ú≈☼âσ   Θ≈   â■“t○â■╲☼à╥   E1Σâ}╚╲u♫Hì}░Iλ─Φì■λλδ∞E1╔â}╚“u♫Hì
}░Φy■λλIλ┴δ∞LëαMà╔u↓Iλ╠IâⁿλtÅ╛╲   Hì}░ΦÇ■λλδτHâ°☺v∟╛╲   Hì}░HëEá
Φf■λλHïEáHâΦ☻δ▐AÇΣ☺t↨╛“   Hì}░ΦH■λλIλ╔☼ä@λλλAÇ■☺A╛♥   Iâ┘ Mìa☺M9
⌠r¶╛“   Hì}░Iâ╞♥Φ▬■λλδτ1╥╣♥   Lë╚H≈±Hà╥A☼ö╞Θⁿ■λλHì}░Φ≥²λλHì}░Φ╛²
λλΘσ■λλ1÷Hì}░Φ┘²λλΘG■λλHâ─8ë╪[A╲A]A^A_]├╠Hì♣╪↓  ├☼┐└à└x←Ië╩☼♣H=☺
≡λλs☺├≈╪ë♣║↓  jλX∙├ï♣⌂▬  δφH┴Φ0δ•H┴Φ ☼╖└f=λ☼sσIë╩☼♣r╙├Aë├┴Φ►%λ☼ 
 A┴δ∟A┴π↑D○╪δ┌QRëλ1└âλ⌂v“☼╜╧║┐→@ ïLJ≥ë·┴∩♠ÇΓ⁇♀Ç◘╨H┴α◘■╔u∞◘ΦH○°ZY
├U1└HëσWVSQRë┬ë├λ└☼╖¶Wë╤füß ⁿfü∙ ▄tΦfü∙ ╪t♦ë▬δ,☼╖♦Gë┴füß ⁿfü∙ ▄t
♂╟♠²λ  â╚λδ☼┴Γ◙ìä☻ $áⁿë♠ìC☻ZY[^_]├f☼▼D  Éâ♪▄↑  ♦÷♣맠 ♦t♪@☼╢╧λ§ì
♣  δ°╠ï♣ö§  ☼♣·☼☺∟%Ü→@ ⌠δ²        f☼▼D  Énodll◙ KernelBase.dll 
└☺└☺└☺└☻ααααα♥≡♥≡♥≡♥≡♥≡♦°♦°♦°♦°♦°♣ⁿ♣ⁿ♣ⁿ♣ⁿ♣ⁿ♣ⁿÉÉ☼▼D  ⁇♣ExitPr
ocess æ☺FreeEnvironmentStringsW ╜☺GetCommandLineW ‼☻GetEnvironme
ntStringsW  ╘☻GetStdHandle  ╫♣SetDefaultDllDirectories  ▼•WriteF
ile ░←          ░→  ►                       ☼▼@ °→      ♠←      
      2←      L←      ╲←      x←              É☼▼Ç    É╬ &τ☺╬
 ☺☺⌂╬ üÇÇ►f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼
ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä   
  f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.
☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä 
    f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     
f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼
ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä   
  f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.
☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä 
    f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     
f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼
ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä   
  f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.
☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä 
    f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     
f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     f.☼▼ä     ☼▼@ 
  @             °→      ♠←       ←      2←      L←      ╲←      
x←                                                              
See also `build/objdump -xd o/tiny/examples/life.com.dbg | less` to view
assembly and the linker glue in [ape/ape.lds] that makes it all possible
## Licensing
Cosmopolitan is Free Software licensed under the GPLv2. The build config
**will embed all linked sources inside your binaries** so the compliance
is easy while facilitating trust and transparency similar to JavaScript.
You can audit your source filesystem using ZIP GUIs e.g. Win10, InfoZip.
That works easiest by changing the filename extension from .com to .zip.
### Commercial Support
If you want to be able to distribute binaries in binary only form, then
please send $1,000 to Justine Tunney <jtunney@gmail.com> on PayPal, for
a license lasting 1 year. This README will be updated in the event that
this needs to change. Please reach out if there's anything you need.
## Contributing
We'd love to accept your patches! Before we can take them, we have to
jump through one legal hurdle. Please write an email to Justine Tunney
<jtunney@gmail.com> saying you agree to give her copyright ownership to
any changes you contribute to Cosmopolitan. We need to do that in order
to dual license Cosmopolitan. Otherwise we can't create incentives that
encourage corporations to share their source code with the community.
## Volunteering
We also need volunteers who can help us further stabilize System Five's
application binary interface. See our ABI scripts [libc/sysv/consts.sh]
and [libc/sysv/syscalls.sh]. Magic numbers are usually stabler than API
interfaces cf. NPM but we should ideally have fewer of them, similar to
how SI has sought to have fewer defining physics constants.
## About
Cosmopolitan mostly stands on the shoulders of giants and has few novel
ideas, aside from taking care of low-level build system toil, so coding
can become more beautifully pleasant.
Cosmopolitan is maintained by Justine Tunney, who previously worked on
projects like [TensorFlow], [Operation Rosehub], [Nomulus], and Google's
tape backups SRE team. She's also helped activists by operating the
[Occupy Wall Street] website. Justine Tunney currently isn't on the
payroll of any company. She's been focusing on Cosmopolitan because she
wants to give back to Free Software which helped her be successful. See
her [LinkedIn] profile for further details on her professional history.
[LinkedIn]: https://www.linkedin.com/in/jtunney
[Nomulus]: https://github.com/google/nomulus
[Occupy Wall Street]: https://www.newyorker.com/magazine/2011/11/28/pre-occupied
[Operation Rosehub]: https://opensource.googleblog.com/2017/03/operation-rosehub.html
[TensorFlow]: https://github.com/tensorflow/tensorflow
[ansitrinsics library]: libc/intrin
[ape/ape.lds]: ape/ape.lds
[dsp/scale/cdecimate2xuint8x8.c]: dsp/scale/cdecimate2xuint8x8.c
[examples/life.c]: examples/life.c
[libc/nexgen32e/kcpuids.S]: libc/nexgen32e/kcpuids.S
[libc/nexgen32e/memcpy.S]: libc/nexgen32e/memcpy.S
[libc/nexgen32e/x86feature.h]: libc/nexgen32e/x86feature.h
[libc/sysv/consts.sh]: libc/sysv/consts.sh
[libc/sysv/syscalls.sh]: libc/sysv/syscalls.sh
[third_party/xed/x86ild.greg.c]: third_party/xed/x86ild.greg.c
[tool/build/package.c]: tool/build/package.c
[tool/emacs/cosmo-stuff.el]: tool/emacs/cosmo-stuff.el
[tool/viz/lib/ycbcr2rgb3.c]: tool/viz/lib/ycbcr2rgb3.c

View File

@ -1631,7 +1631,7 @@ long: push $GDT_LONG_DATA
xor %ebp,%ebp
mov $REAL_STACK_FRAME+FRAMESIZE,%esp
call __map_image
mov $_metal,%eax
ezlea _metal,ax
jmp *%rax
.endfn long
@ -1646,8 +1646,12 @@ _metal:
mov $.Lape.bss.vaddr,%edi
mov $.Lape.bss.memsz,%ecx
rep stosb
movb $METAL,hostos(%rip)
push $0 # auxv
.weak hostos
ezlea hostos,ax
test %rax,%rax
jz 1f
movb $METAL,(%rax)
1: push $0 # auxv
push $0
push $0 # envp
push $0 # auxv

View File

@ -108,6 +108,11 @@ o/$(MODE)/examples/%.elf: \
$(ELF)
@$(ELFLINK)
o/$(MODE)/examples/tiny-raw-linux-tutorial.elf: \
o/$(MODE)/examples/tiny-raw-linux-tutorial.o \
$(ELF)
@$(ELFLINK) -N -z max-page-size=0x10
$(EXAMPLES_OBJS): examples/examples.mk
o/$(MODE)/examples/hellojs.com.dbg: \

View File

@ -43,12 +43,12 @@ long hilbert(long n, long y, long x) {
long d, s, ry, rx;
d = 0;
for (s = n / 2; s > 0; s /= 2) {
rx = (x & s) > 0;
ry = (y & s) > 0;
rx = (x & s) > 0;
d += s * s * ((3 * rx) ^ ry);
m = RotateQuadrant(n, y, x, ry, rx);
x = m.dx;
y = m.ax;
x = m.dx;
}
return d;
}

View File

@ -24,7 +24,8 @@
/
/ @param 𝑥 is a double passed in the lower quadword of %xmm0
/ @return result in lower quadword of %xmm0
log2: push %rbp
tinymath_log2:
push %rbp
mov %rsp,%rbp
.profilable
push %rax
@ -36,4 +37,5 @@ log2: push %rbp
movsd -8(%rbp),%xmm0
leave
ret
.endfn log2,globl
.endfn tinymath_log2,globl
.alias tinymath_log2,log2

View File

@ -20,7 +20,12 @@
#include "libc/macros.h"
.source __FILE__
log2f: push %rbp
/ Calculates log𝑥.
/
/ @param 𝑥 is a float passed in the lower quarter of %xmm0
/ @return result in lower quarter of %xmm0
tinymath_log2f:
push %rbp
mov %rsp,%rbp
.profilable
push %rax
@ -32,4 +37,5 @@ log2f: push %rbp
movss 4(%rsp),%xmm0
leave
ret
.endfn log2f,globl
.endfn tinymath_log2f,globl
.alias tinymath_log2f,log2f

View File

@ -25,7 +25,8 @@
/ @param 𝑥 is an 80-bit long double passed on stack in 16-bytes
/ @return result in %st
/ @see ilogbl()
log2l: push %rbp
tinymath_log2l:
push %rbp
mov %rsp,%rbp
.profilable
fld1
@ -33,4 +34,5 @@ log2l: push %rbp
fyl2x
pop %rbp
ret
.endfn log2l,globl
.endfn tinymath_log2l,globl
.alias tinymath_log2l,log2l

View File

@ -17,6 +17,7 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/scale/cdecimate2xuint8x8.h"
#include "dsp/tty/tty.h"
#include "libc/alg/arraylist2.h"
#include "libc/assert.h"
@ -90,6 +91,7 @@
#include "tool/build/lib/stats.h"
#include "tool/build/lib/syscall.h"
#include "tool/build/lib/throw.h"
#include "tool/build/lib/xmmtype.h"
#define USAGE \
" [-?HhrRstv] [ROM] [ARGS...]\n\
@ -102,6 +104,7 @@ DESCRIPTION\n\
FLAGS\n\
\n\
-h help\n\
-z zoom\n\
-v verbosity\n\
-r real mode\n\
-s statistics\n\
@ -122,8 +125,10 @@ FEATURES\n\
8086, 8087, i386, x86_64, SSE3, SSSE3, POPCNT, MDA, CGA, TTY\n\
\n"
#define DUMPWIDTH 64
#define DISPWIDTH 80
#define MAXZOOM 16
#define DUMPWIDTH 64
#define DISPWIDTH 80
#define WHEELDELTA 1
#define RESTART 0x001
#define REDRAW 0x002
@ -138,28 +143,31 @@ FEATURES\n\
#define EXIT 0x400
#define ALARM 0x800
#define kXmmIntegral 0
#define kXmmDouble 1
#define kXmmFloat 2
#define kXmmDecimal 0
#define kXmmHex 1
#define kXmmChar 2
#define kMouseLeftDown 0
#define kMouseMiddleDown 1
#define kMouseRightDown 2
#define kMouseLeftUp 4
#define kMouseMiddleUp 5
#define kMouseRightUp 6
#define kMouseLeftDrag 32
#define kMouseMiddleDrag 33
#define kMouseRightDrag 34
#define kMouseWheelUp 64
#define kMouseWheelDown 65
#define kMouseLeftDown 0
#define kMouseMiddleDown 1
#define kMouseRightDown 2
#define kMouseLeftUp 4
#define kMouseMiddleUp 5
#define kMouseRightUp 6
#define kMouseLeftDrag 32
#define kMouseMiddleDrag 33
#define kMouseRightDrag 34
#define kMouseWheelUp 64
#define kMouseWheelDown 65
#define kMouseCtrlWheelUp 80
#define kMouseCtrlWheelDown 81
#define CTRL(C) ((C) ^ 0100)
struct MemoryView {
int64_t start;
unsigned zoom;
};
struct MachineState {
uint64_t ip;
uint8_t cs[8];
@ -204,6 +212,8 @@ struct Panels {
};
};
static const signed char kThePerfectKernel[8] = {-1, -3, 3, 17, 17, 3, -3, -1};
static const char kRegisterNames[16][4] = {
"RAX", "RCX", "RDX", "RBX", "RSP", "RBP", "RSI", "RDI",
"R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15",
@ -215,6 +225,7 @@ static bool alarmed;
static bool colorize;
static bool mousemode;
static bool printstats;
static bool showhighsse;
static int tyn;
static int txn;
@ -228,37 +239,32 @@ static int opline;
static int action;
static int xmmdisp;
static int exitcode;
static int codezoom;
static int readzoom;
static int writezoom;
static int stackzoom;
static long ips;
static long rombase;
static long codesize;
static int64_t opstart;
static int64_t codestart;
static int64_t readstart;
static int64_t mapsstart;
static int64_t writestart;
static int64_t stackstart;
static int64_t framesstart;
static int64_t breakpointsstart;
static uint64_t last_opcount;
static char *codepath;
static void *onbusted;
static char *statusmessage;
static struct Machine *m;
static struct Pty *pty;
static struct Machine *m;
static struct Panels pan;
static struct MemoryView codeview;
static struct MemoryView readview;
static struct MemoryView writeview;
static struct MemoryView stackview;
static struct MachineState laststate;
static struct Breakpoints breakpoints;
static struct MachineMemstat lastmemstat;
static struct XmmType xmmtype;
static struct Elf elf[1];
static struct Dis dis[1];
static uint8_t xmmtype[16];
static uint8_t xmmsize[16];
long double last_seconds;
static long double statusexpires;
@ -308,9 +314,9 @@ static bool IsRet(void) {
}
static int GetXmmTypeCellCount(int r) {
switch (xmmtype[r]) {
switch (xmmtype.type[r]) {
case kXmmIntegral:
return 16 / xmmsize[r];
return 16 / xmmtype.size[r];
case kXmmFloat:
return 4;
case kXmmDouble:
@ -398,162 +404,6 @@ static int64_t ReadWord(uint8_t *p) {
}
}
static void UpdateXmmTypes(int regtype, int rmtype) {
xmmtype[RexrReg(m->xedd->op.rde)] = regtype;
if (IsModrmRegister(m->xedd->op.rde)) {
xmmtype[RexbRm(m->xedd->op.rde)] = rmtype;
}
}
static void UpdateXmmSizes(int regsize, int rmsize) {
xmmsize[RexrReg(m->xedd->op.rde)] = regsize;
if (IsModrmRegister(m->xedd->op.rde)) {
xmmsize[RexbRm(m->xedd->op.rde)] = rmsize;
}
}
static void UpdateXmmType(void) {
switch (m->xedd->op.dispatch) {
case 0x12E: // UCOMIS
case 0x12F: // COMIS
case 0x151: // SQRT
case 0x152: // RSQRT
case 0x153: // RCP
case 0x158: // ADD
case 0x159: // MUL
case 0x15C: // SUB
case 0x15D: // MIN
case 0x15E: // DIV
case 0x15F: // MAX
case 0x1C2: // CMP
if (Osz(m->xedd->op.rde) || Rep(m->xedd->op.rde) == 2) {
UpdateXmmTypes(kXmmDouble, kXmmDouble);
} else {
UpdateXmmTypes(kXmmFloat, kXmmFloat);
}
break;
case 0x12A: // CVTPI2PS,CVTSI2SS,CVTPI2PD,CVTSI2SD
if (Osz(m->xedd->op.rde) || Rep(m->xedd->op.rde) == 2) {
UpdateXmmSizes(8, 4);
UpdateXmmTypes(kXmmDouble, kXmmIntegral);
} else {
UpdateXmmSizes(4, 4);
UpdateXmmTypes(kXmmFloat, kXmmIntegral);
}
break;
case 0x15A: // CVT{P,S}{S,D}2{P,S}{S,D}
if (Osz(m->xedd->op.rde) || Rep(m->xedd->op.rde) == 2) {
UpdateXmmTypes(kXmmFloat, kXmmDouble);
} else {
UpdateXmmTypes(kXmmDouble, kXmmFloat);
}
break;
case 0x15B: // CVT{,T}{DQ,PS}2{PS,DQ}
UpdateXmmSizes(4, 4);
if (Osz(m->xedd->op.rde) || Rep(m->xedd->op.rde) == 3) {
UpdateXmmTypes(kXmmIntegral, kXmmFloat);
} else {
UpdateXmmTypes(kXmmFloat, kXmmIntegral);
}
break;
case 0x17C: // HADD
case 0x17D: // HSUB
case 0x1D0: // ADDSUB
if (Osz(m->xedd->op.rde)) {
UpdateXmmTypes(kXmmDouble, kXmmDouble);
} else {
UpdateXmmTypes(kXmmFloat, kXmmFloat);
}
break;
case 0x164: // PCMPGTB
case 0x174: // PCMPEQB
case 0x1D8: // PSUBUSB
case 0x1DA: // PMINUB
case 0x1DC: // PADDUSB
case 0x1DE: // PMAXUB
case 0x1E0: // PAVGB
case 0x1E8: // PSUBSB
case 0x1EC: // PADDSB
case 0x1F8: // PSUBB
case 0x1FC: // PADDB
UpdateXmmSizes(1, 1);
UpdateXmmTypes(kXmmIntegral, kXmmIntegral);
break;
case 0x165: // PCMPGTW
case 0x175: // PCMPEQW
case 0x171: // PSRLW,PSRAW,PSLLW
case 0x1D1: // PSRLW
case 0x1D5: // PMULLW
case 0x1D9: // PSUBUSW
case 0x1DD: // PADDUSW
case 0x1E1: // PSRAW
case 0x1E3: // PAVGW
case 0x1E4: // PMULHUW
case 0x1E5: // PMULHW
case 0x1E9: // PSUBSW
case 0x1EA: // PMINSW
case 0x1ED: // PADDSW
case 0x1EE: // PMAXSW
case 0x1F1: // PSLLW
case 0x1F6: // PSADBW
case 0x1F9: // PSUBW
case 0x1FD: // PADDW
UpdateXmmSizes(2, 2);
UpdateXmmTypes(kXmmIntegral, kXmmIntegral);
break;
case 0x166: // PCMPGTD
case 0x176: // PCMPEQD
case 0x172: // PSRLD,PSRAD,PSLLD
case 0x1D2: // PSRLD
case 0x1E2: // PSRAD
case 0x1F2: // PSLLD
case 0x1FA: // PSUBD
case 0x1FE: // PADDD
UpdateXmmSizes(4, 4);
UpdateXmmTypes(kXmmIntegral, kXmmIntegral);
break;
case 0x173: // PSRLQ,PSRLQ,PSRLDQ,PSLLQ,PSLLDQ
case 0x1D3: // PSRLQ
case 0x1D4: // PADDQ
case 0x1F3: // PSLLQ
case 0x1F4: // PMULUDQ
case 0x1FB: // PSUBQ
UpdateXmmSizes(8, 8);
UpdateXmmTypes(kXmmIntegral, kXmmIntegral);
break;
case 0x16B: // PACKSSDW
case 0x1F5: // PMADDWD
UpdateXmmSizes(4, 2);
UpdateXmmTypes(kXmmIntegral, kXmmIntegral);
break;
case 0x163: // PACKSSWB
case 0x167: // PACKUSWB
UpdateXmmSizes(1, 2);
UpdateXmmTypes(kXmmIntegral, kXmmIntegral);
break;
case 0x128: // MOVAPS Vps Wps
if (IsModrmRegister(m->xedd->op.rde)) {
xmmtype[RexrReg(m->xedd->op.rde)] = xmmtype[RexbRm(m->xedd->op.rde)];
xmmsize[RexrReg(m->xedd->op.rde)] = xmmsize[RexbRm(m->xedd->op.rde)];
}
break;
case 0x129: // MOVAPS Wps Vps
if (IsModrmRegister(m->xedd->op.rde)) {
xmmtype[RexbRm(m->xedd->op.rde)] = xmmtype[RexrReg(m->xedd->op.rde)];
xmmsize[RexbRm(m->xedd->op.rde)] = xmmsize[RexrReg(m->xedd->op.rde)];
}
break;
case 0x16F: // MOVDQA Vdq Wdq
if (Osz(m->xedd->op.rde) && IsModrmRegister(m->xedd->op.rde)) {
xmmtype[RexrReg(m->xedd->op.rde)] = xmmtype[RexbRm(m->xedd->op.rde)];
xmmsize[RexrReg(m->xedd->op.rde)] = xmmsize[RexbRm(m->xedd->op.rde)];
}
break;
default:
return;
}
}
static void CopyMachineState(struct MachineState *ms) {
ms->ip = m->ip;
memcpy(ms->cs, m->cs, sizeof(m->cs));
@ -861,12 +711,14 @@ static bool IsSegNonZero(void) {
static int PickNumberOfXmmRegistersToShow(void) {
if (IsXmmNonZero(0, 8) || IsXmmNonZero(8, 16)) {
if (IsXmmNonZero(8, 16)) {
if (showhighsse || IsXmmNonZero(8, 16)) {
showhighsse = true;
return 16;
} else {
return 8;
}
} else {
showhighsse = false;
return 0;
}
}
@ -1248,7 +1100,7 @@ static void DrawXmm(struct Panel *p, long i, long r) {
cellwidth = MIN(MAX(0, (panwidth - left) / cells - 1), sizeof(buf) - 1);
for (j = 0; j < cells; ++j) {
AppendPanel(p, i, " ");
switch (xmmtype[r]) {
switch (xmmtype.type[r]) {
case kXmmFloat:
memcpy(&f, xmm + j * sizeof(f), sizeof(f));
FormatDouble(buf, f);
@ -1259,8 +1111,8 @@ static void DrawXmm(struct Panel *p, long i, long r) {
break;
case kXmmIntegral:
ival = 0;
for (k = 0; k < xmmsize[r]; ++k) {
itmp = xmm[j * xmmsize[r] + k] & 0xff;
for (k = 0; k < xmmtype.size[r]; ++k) {
itmp = xmm[j * xmmtype.size[r] + k] & 0xff;
itmp <<= k * 8;
ival |= itmp;
}
@ -1268,10 +1120,10 @@ static void DrawXmm(struct Panel *p, long i, long r) {
if (xmmdisp == kXmmChar && iswalnum(ival)) {
sprintf(buf, "%lc", ival);
} else {
uint64toarray_fixed16(ival, buf, xmmsize[r] * 8);
uint64toarray_fixed16(ival, buf, xmmtype.size[r] * 8);
}
} else {
int64toarray_radix10(SignExtend(ival, xmmsize[r] * 8), buf);
int64toarray_radix10(SignExtend(ival, xmmtype.size[r] * 8), buf);
}
break;
default:
@ -1288,91 +1140,125 @@ static void DrawXmm(struct Panel *p, long i, long r) {
}
static void DrawSse(struct Panel *p) {
long i;
if (p->top == p->bottom) return;
for (i = 0; i < MIN(16, MAX(0, p->bottom - p->top)); ++i) {
DrawXmm(p, i, i);
}
}
static void ScrollCode(struct Panel *p) {
long i, n;
n = p->bottom - p->top;
i = GetIp() / DUMPWIDTH;
if (!(codestart <= i && i < codestart + n)) {
codestart = i;
if (n > 0) {
for (i = 0; i < MIN(16, n); ++i) {
DrawXmm(p, i, i);
}
}
}
static void ScrollReadData(struct Panel *p) {
long i, n, addr;
n = p->bottom - p->top;
i = m->readaddr / (DUMPWIDTH * (1 << readzoom));
if (!(readstart <= i && i < readstart + n)) {
readstart = i - n / 3;
}
}
static void ScrollWriteData(struct Panel *p) {
long i, n, addr;
n = p->bottom - p->top;
i = m->writeaddr / DUMPWIDTH;
if (!(writestart <= i && i < writestart + n)) {
writestart = i - n / 3;
}
}
static void ScrollStack(struct Panel *p) {
static void ScrollMemoryView(struct Panel *p, struct MemoryView *v, int64_t a) {
long i, n;
n = p->bottom - p->top;
i = GetSp() / DUMPWIDTH;
if (!(stackstart <= i && i < stackstart + n)) {
stackstart = i;
i = a / (DUMPWIDTH * (1ull << v->zoom));
if (!(v->start <= i && i < v->start + n)) {
v->start = i;
}
}
static uint8_t Downsample(uint8_t al, uint8_t bl, uint8_t cl, uint8_t dl) {
int16_t ax, bx;
bx = bl;
bx += cl;
bx *= 3;
ax = al;
ax += dl;
ax += bx;
ax += 4;
ax >>= 3;
al = ax;
return al;
static void ZoomMemoryView(struct MemoryView *v, int dy) {
v->start *= (DUMPWIDTH * (1ull << v->zoom));
v->zoom = MIN(MAXZOOM, MAX(0, v->zoom + dy));
v->start /= (DUMPWIDTH * (1ull << v->zoom));
}
static uint8_t Sharpen(uint8_t al, uint8_t bl, uint8_t cl) {
int16_t ax, bx, cx;
ax = al;
bx = bl;
cx = cl;
ax *= -1;
bx *= +6;
cx *= -1;
ax += bx;
ax += cx;
ax += 2;
ax >>= 2;
return MIN(255, MAX(0, ax));
static void ScrollMemoryViews(void) {
ScrollMemoryView(&pan.code, &codeview, GetIp());
ScrollMemoryView(&pan.readdata, &readview, m->readaddr);
ScrollMemoryView(&pan.writedata, &writeview, m->writeaddr);
ScrollMemoryView(&pan.stack, &stackview, GetSp());
}
static void DrawMemory(struct Panel *p, int zoom, long startline, long histart,
long hiend) {
char buf[16];
static void ZoomMemoryViews(struct Panel *p, int dy) {
if (p == &pan.code) {
ZoomMemoryView(&codeview, dy);
} else if (p == &pan.readdata) {
ZoomMemoryView(&readview, dy);
} else if (p == &pan.writedata) {
ZoomMemoryView(&writeview, dy);
} else if (p == &pan.stack) {
ZoomMemoryView(&stackview, dy);
}
}
static void DrawMemoryZoomed(struct Panel *p, struct MemoryView *view,
long histart, long hiend) {
bool high, changed;
uint8_t *canvas, *chunk, *invalid;
int64_t a, b, c, d, n, i, j, k, size;
struct ContiguousMemoryRanges ranges;
a = view->start * DUMPWIDTH * (1ull << view->zoom);
b = (view->start + (p->bottom - p->top)) * DUMPWIDTH * (1ull << view->zoom);
size = (p->bottom - p->top) * DUMPWIDTH;
canvas = xcalloc(1, size);
invalid = xcalloc(1, size);
memset(&ranges, 0, sizeof(ranges));
FindContiguousMemoryRanges(m, &ranges);
for (k = i = 0; i < ranges.i; ++i) {
if ((a >= ranges.p[i].a && a < ranges.p[i].b) ||
(b >= ranges.p[i].a && b < ranges.p[i].b) ||
(a < ranges.p[i].a && b >= ranges.p[i].b)) {
c = MAX(a, ranges.p[i].a);
d = MIN(b, ranges.p[i].b);
n = ROUNDUP(ROUNDUP(d - c, 16), 1ull << view->zoom);
chunk = xmalloc(n);
VirtualSend(m, chunk, c, d - c);
memset(chunk + (d - c), 0, n - (d - c));
for (j = 0; j < view->zoom; ++j) {
cDecimate2xUint8x8(ROUNDUP(n, 16), chunk, kThePerfectKernel);
n >>= 1;
}
j = (c - a) / (1ull << view->zoom);
memset(invalid + k, -1, j - k);
memcpy(canvas + j, chunk, MIN(n, size - j));
k = j + MIN(n, size - j);
free(chunk);
}
}
memset(invalid + k, -1, size - k);
free(ranges.p);
high = false;
for (c = i = 0; i < p->bottom - p->top; ++i) {
AppendFmt(&p->lines[i], "%p ",
(view->start + i) * DUMPWIDTH * (1ull << view->zoom));
for (j = 0; j < DUMPWIDTH; ++j, ++c) {
a = ((view->start + i) * DUMPWIDTH + j + 0) * (1ull << view->zoom);
b = ((view->start + i) * DUMPWIDTH + j + 1) * (1ull << view->zoom);
changed = ((histart >= a && hiend < b) ||
(histart && hiend && histart >= a && hiend < b));
if (changed && !high) {
high = true;
AppendStr(&p->lines[i], "\e[7m");
} else if (!changed && high) {
AppendStr(&p->lines[i], "\e[27m");
high = false;
}
if (invalid[c]) {
AppendWide(&p->lines[i], u'');
} else {
AppendWide(&p->lines[i], kCp437[canvas[c]]);
}
}
if (high) {
AppendStr(&p->lines[i], "\e[27m");
high = false;
}
}
free(invalid);
free(canvas);
}
static void DrawMemoryUnzoomed(struct Panel *p, struct MemoryView *view,
long histart, long hiend) {
long i, j, k, c;
bool high, changed;
long i, j, k, c, width;
if (p->top == p->bottom) return;
high = false;
width = DUMPWIDTH * (1 << zoom);
for (i = 0; i < p->bottom - p->top; ++i) {
snprintf(buf, sizeof(buf), "%p ", (startline + i) * width);
AppendStr(&p->lines[i], buf);
for (j = 0; j < width; ++j) {
k = (startline + i) * DUMPWIDTH + j;
AppendFmt(&p->lines[i], "%p ", (view->start + i) * DUMPWIDTH);
for (j = 0; j < DUMPWIDTH; ++j) {
k = (view->start + i) * DUMPWIDTH + j;
c = VirtualBing(k);
changed = histart <= k && k < hiend;
if (changed && !high) {
@ -1391,6 +1277,16 @@ static void DrawMemory(struct Panel *p, int zoom, long startline, long histart,
}
}
static void DrawMemory(struct Panel *p, struct MemoryView *view, long histart,
long hiend) {
if (p->top == p->bottom) return;
if (view->zoom) {
DrawMemoryZoomed(p, view, histart, hiend);
} else {
DrawMemoryUnzoomed(p, view, histart, hiend);
}
}
static void DrawMaps(struct Panel *p) {
int i;
char *text, *p1, *p2;
@ -1597,14 +1493,11 @@ static void Redraw(void) {
DrawMaps(&pan.maps);
DrawFrames(&pan.frames);
DrawBreakpoints(&pan.breakpoints);
DrawMemory(&pan.code, codezoom, codestart, GetIp(),
GetIp() + m->xedd->length);
DrawMemory(&pan.readdata, readzoom, readstart, m->readaddr,
m->readaddr + m->readsize);
DrawMemory(&pan.writedata, writezoom, writestart, m->writeaddr,
DrawMemory(&pan.code, &codeview, GetIp(), GetIp() + m->xedd->length);
DrawMemory(&pan.readdata, &readview, m->readaddr, m->readaddr + m->readsize);
DrawMemory(&pan.writedata, &writeview, m->writeaddr,
m->writeaddr + m->writesize);
DrawMemory(&pan.stack, stackzoom, stackstart, GetSp(),
GetSp() + GetPointerWidth());
DrawMemory(&pan.stack, &stackview, GetSp(), GetSp() + GetPointerWidth());
DrawStatus(&pan.status);
PreventBufferbloat();
if (PrintPanels(ttyout, ARRAYLEN(pan.p), pan.p, tyn, txn) == -1) {
@ -2295,16 +2188,16 @@ static void OnRestart(void) {
static void OnXmmType(void) {
uint8_t t;
unsigned i;
t = CycleXmmType(xmmtype[0]);
t = CycleXmmType(xmmtype.type[0]);
for (i = 0; i < 16; ++i) {
xmmtype[i] = t;
xmmtype.type[i] = t;
}
}
static void SetXmmSize(int bytes) {
unsigned i;
for (i = 0; i < 16; ++i) {
xmmsize[i] = bytes;
xmmtype.size[i] = bytes;
}
}
@ -2313,7 +2206,7 @@ static void SetXmmDisp(int disp) {
}
static void OnXmmSize(void) {
SetXmmSize(CycleXmmSize(xmmsize[0]));
SetXmmSize(CycleXmmSize(xmmtype.size[0]));
}
static void OnXmmDisp(void) {
@ -2328,8 +2221,67 @@ static void Sleep(int ms) {
poll((struct pollfd[]){{ttyin, POLLIN}}, 1, ms);
}
static void OnMouseWheelUp(struct Panel *p) {
if (p == &pan.disassembly) {
opstart -= WHEELDELTA;
} else if (p == &pan.code) {
codeview.start -= WHEELDELTA;
} else if (p == &pan.readdata) {
readview.start -= WHEELDELTA;
} else if (p == &pan.writedata) {
writeview.start -= WHEELDELTA;
} else if (p == &pan.stack) {
stackview.start -= WHEELDELTA;
} else if (p == &pan.maps) {
mapsstart = MAX(0, mapsstart - 1);
} else if (p == &pan.frames) {
framesstart = MAX(0, framesstart - 1);
} else if (p == &pan.breakpoints) {
breakpointsstart = MAX(0, breakpointsstart - 1);
}
}
static void OnMouseWheelDown(struct Panel *p) {
if (p == &pan.disassembly) {
opstart += WHEELDELTA;
} else if (p == &pan.code) {
codeview.start += WHEELDELTA;
} else if (p == &pan.readdata) {
readview.start += WHEELDELTA;
} else if (p == &pan.writedata) {
writeview.start += WHEELDELTA;
} else if (p == &pan.stack) {
stackview.start += WHEELDELTA;
} else if (p == &pan.maps) {
mapsstart += 1;
} else if (p == &pan.frames) {
framesstart += 1;
} else if (p == &pan.breakpoints) {
breakpointsstart += 1;
}
}
static void OnMouseCtrlWheelUp(struct Panel *p) {
ZoomMemoryViews(p, -1);
}
static void OnMouseCtrlWheelDown(struct Panel *p) {
ZoomMemoryViews(p, +1);
}
static struct Panel *LocatePanel(int y, int x) {
int i;
for (i = 0; i < ARRAYLEN(pan.p); ++i) {
if ((pan.p[i].left <= x && x < pan.p[i].right) &&
(pan.p[i].top <= y && y < pan.p[i].bottom)) {
return &pan.p[i];
}
}
return NULL;
}
static void OnMouse(char *p) {
int e, i, x, y, dy;
int e, x, y;
struct Panel *ep;
e = strtol(p, &p, 10);
if (*p == ';') ++p;
@ -2337,37 +2289,19 @@ static void OnMouse(char *p) {
if (*p == ';') ++p;
y = min(tyn, max(1, strtol(p, &p, 10))) - 1;
e |= (*p == 'm') << 2;
for (ep = 0, i = 0; i < ARRAYLEN(pan.p); ++i) {
if ((pan.p[i].left <= x && x < pan.p[i].right) &&
(pan.p[i].top <= y && y < pan.p[i].bottom)) {
ep = &pan.p[i];
break;
}
}
if (ep) {
dy = 3;
if ((ep = LocatePanel(y, x))) {
switch (e) {
case kMouseWheelUp:
if (ep == &pan.disassembly) opstart -= dy;
if (ep == &pan.code) codestart -= dy;
if (ep == &pan.readdata) readstart -= dy;
if (ep == &pan.writedata) writestart -= dy;
if (ep == &pan.stack) stackstart -= dy;
if (ep == &pan.maps) mapsstart = MAX(0, mapsstart - 1);
if (ep == &pan.frames) framesstart = MAX(0, framesstart - 1);
if (ep == &pan.breakpoints) {
breakpointsstart = MAX(0, breakpointsstart - 1);
}
OnMouseWheelUp(ep);
break;
case kMouseWheelDown:
if (ep == &pan.disassembly) opstart += dy;
if (ep == &pan.code) codestart += dy;
if (ep == &pan.readdata) readstart += dy;
if (ep == &pan.writedata) writestart += dy;
if (ep == &pan.stack) stackstart += dy;
if (ep == &pan.maps) mapsstart += 1;
if (ep == &pan.frames) framesstart += 1;
if (ep == &pan.breakpoints) breakpointsstart += 1;
OnMouseWheelDown(ep);
break;
case kMouseCtrlWheelUp:
OnMouseCtrlWheelUp(ep);
break;
case kMouseCtrlWheelDown:
OnMouseCtrlWheelDown(ep);
break;
default:
break;
@ -2377,6 +2311,7 @@ static void OnMouse(char *p) {
static void ReadKeyboard(void) {
char buf[64], *p = buf;
memset(buf, 0, sizeof(buf));
if (readansi(ttyin, buf, sizeof(buf)) == -1) {
if (errno == EINTR) {
LOGF("readkeyboard interrupted");
@ -2573,7 +2508,6 @@ static void Tui(void) {
for (;;) {
if (!(action & FAILURE)) {
LoadInstruction(m);
UpdateXmmType();
if ((action & (FINISH | NEXT | CONTINUE)) &&
(bp = IsAtBreakpoint(&breakpoints, GetIp())) != -1) {
action &= ~(FINISH | NEXT | CONTINUE);
@ -2598,10 +2532,7 @@ static void Tui(void) {
HandleAlarm();
}
if (action & FAILURE) {
ScrollCode(&pan.code);
ScrollStack(&pan.stack);
ScrollReadData(&pan.readdata);
ScrollWriteData(&pan.writedata);
ScrollMemoryViews();
}
if (!(action & CONTINUE) || interactive) {
tick = 0;
@ -2666,13 +2597,11 @@ static void Tui(void) {
}
}
if (!IsDebugBreak()) {
UpdateXmmType(m, &xmmtype);
ExecuteInstruction(m);
++opcount;
if (!(action & CONTINUE) || interactive) {
ScrollCode(&pan.code);
ScrollStack(&pan.stack);
ScrollReadData(&pan.readdata);
ScrollWriteData(&pan.writedata);
ScrollMemoryViews();
}
} else {
m->ip += m->xedd->length;
@ -2703,7 +2632,7 @@ static void Tui(void) {
static void GetOpts(int argc, char *argv[]) {
int opt;
stpcpy(stpcpy(stpcpy(logpath, kTmpPath), basename(argv[0])), ".log");
while ((opt = getopt(argc, argv, "hvtrRsb:HL:")) != -1) {
while ((opt = getopt(argc, argv, "hvtrzRsb:HL:")) != -1) {
switch (opt) {
case 't':
tuimode = true;
@ -2730,6 +2659,12 @@ static void GetOpts(int argc, char *argv[]) {
case 'L':
strcpy(logpath, optarg);
break;
case 'z':
++codeview.zoom;
++readview.zoom;
++writeview.zoom;
++stackview.zoom;
break;
case 'h':
PrintUsage(EXIT_SUCCESS, stdout);
default:

View File

@ -1,180 +0,0 @@
/*───────────────────────────────────────────────────────────────────────────│─╗
The LISP Challenge § Hardware Integration w/ x86_64 Linux & 8086 PC BIOS
*/
#define CompilerBarrier() asm volatile("" ::: "memory");
#define ISATOM(x) /* a.k.a. !(x&1) */ \
({ \
_Bool IsAtom; \
asm("test%z1\t$1,%1" : "=@ccz"(IsAtom) : "Qm"((char)x)); \
IsAtom; \
})
#define OBJECT(t, v) /* a.k.a. v<<1|t */ \
({ \
__typeof(v) Val = (v); \
asm("shl\t%0" : "+r"(Val)); \
Val | (t); \
})
#define SUB(x, y) /* a.k.a. x-y */ \
({ \
__typeof(x) Reg = (x); \
asm("sub\t%1,%0" : "+rm"(Reg) : "g"(y)); \
Reg; \
})
#define STOS(di, c) asm("stos%z1" : "+D"(di), "=m"(*(di)) : "a"(c))
#define LODS(si) \
({ \
typeof(*(si)) c; \
asm("lods%z2" : "+S"(si), "=a"(c) : "m"(*(si))); \
c; \
})
#define PEEK_(REG, BASE, INDEX, DISP) \
({ \
__typeof(*(BASE)) Reg; \
if (__builtin_constant_p(INDEX) && !(INDEX)) { \
asm("mov\t%c2(%1),%0" \
: REG(Reg) \
: "bDS"(BASE), "i"((DISP) * sizeof(*(BASE))), \
"m"(BASE[(INDEX) + (DISP)])); \
} else { \
asm("mov\t%c3(%1,%2),%0" \
: REG(Reg) \
: "b"(BASE), "DS"((long)(INDEX) * sizeof(*(BASE))), \
"i"((DISP) * sizeof(*(BASE))), "m"(BASE[(INDEX) + (DISP)])); \
} \
Reg; \
})
#define PEEK(BASE, INDEX, DISP) /* a.k.a. b[i] */ \
(sizeof(*(BASE)) == 1 ? PEEK_("=Q", BASE, INDEX, DISP) \
: PEEK_("=r", BASE, INDEX, DISP))
#define PEEK_ARRAY_(REG, OBJECT, MEMBER, INDEX, DISP) \
({ \
__typeof(*(OBJECT->MEMBER)) Reg; \
if (!(OBJECT)) { \
asm("mov\t%c2(%1),%0" \
: REG(Reg) \
: "bDS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \
"i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \
sizeof(*(OBJECT->MEMBER)) * (DISP)), \
"m"(OBJECT->MEMBER)); \
} else { \
asm("mov\t%c3(%1,%2),%0" \
: REG(Reg) \
: "b"(OBJECT), "DS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \
"i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \
sizeof(*(OBJECT->MEMBER)) * (DISP)), \
"m"(OBJECT->MEMBER)); \
} \
Reg; \
})
#define PEEK_ARRAY(OBJECT, MEMBER, INDEX, DISP) /* o->m[i] */ \
(sizeof(*(OBJECT->MEMBER)) == 1 \
? PEEK_ARRAY_("=Q", OBJECT, MEMBER, INDEX, DISP) \
: PEEK_ARRAY_("=r", OBJECT, MEMBER, INDEX, DISP))
#define POKE_ARRAY_(REG, OBJECT, MEMBER, INDEX, DISP, VALUE) \
do { \
if (!(OBJECT)) { \
asm("mov\t%1,%c3(%2)" \
: "=m"(OBJECT->MEMBER) \
: REG((__typeof(*(OBJECT->MEMBER)))(VALUE)), \
"bDS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \
"i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \
sizeof(*(OBJECT->MEMBER)) * (DISP))); \
} else { \
asm("mov\t%1,%c4(%2,%3)" \
: "=m"(OBJECT->MEMBER) \
: REG((__typeof(*(OBJECT->MEMBER)))(VALUE)), "b"(OBJECT), \
"DS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \
"i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \
sizeof(*(OBJECT->MEMBER)) * (DISP))); \
} \
} while (0)
#define POKE_ARRAY(OBJECT, MEMBER, INDEX, DISP, VALUE) /* o->m[i]=v */ \
do { \
__typeof(*(OBJECT->MEMBER)) Reg; \
switch (sizeof(*(OBJECT->MEMBER))) { \
case 1: \
POKE_ARRAY_("Q", OBJECT, MEMBER, INDEX, DISP, VALUE); \
break; \
default: \
POKE_ARRAY_("r", OBJECT, MEMBER, INDEX, DISP, VALUE); \
break; \
} \
} while (0)
static inline void *SetMemory(void *di, int al, unsigned long cx) {
asm("rep stosb"
: "=D"(di), "=c"(cx), "=m"(*(char(*)[cx])di)
: "0"(di), "1"(cx), "a"(al));
return di;
}
static inline void *CopyMemory(void *di, void *si, unsigned long cx) {
asm("rep movsb"
: "=D"(di), "=S"(si), "=c"(cx), "=m"(*(char(*)[cx])di)
: "0"(di), "1"(si), "2"(cx));
return di;
}
static void RawMode(void) {
#ifndef __REAL_MODE__
int rc;
int c[14];
asm volatile("syscall"
: "=a"(rc)
: "0"(0x10), "D"(0), "S"(0x5401), "d"(c)
: "rcx", "r11", "memory");
c[0] &= ~0b0000010111111000; // INPCK|ISTRIP|PARMRK|INLCR|IGNCR|ICRNL|IXON
c[2] &= ~0b0000000100110000; // CSIZE|PARENB
c[2] |= 0b00000000000110000; // CS8
c[3] &= ~0b1000000001011010; // ECHONL|ECHO|ECHOE|IEXTEN|ICANON
asm volatile("syscall"
: "=a"(rc)
: "0"(0x10), "D"(0), "S"(0x5402), "d"(c)
: "rcx", "r11", "memory");
#endif
}
__attribute__((__noinline__)) static void PrintChar(long c) {
#ifdef __REAL_MODE__
asm volatile("mov\t$0x0E,%%ah\n\t"
"int\t$0x10"
: /* no outputs */
: "a"(c), "b"(7)
: "memory");
#else
static short buf;
int rc;
buf = c;
asm volatile("syscall"
: "=a"(rc)
: "0"(1), "D"(1), "S"(&buf), "d"(1)
: "rcx", "r11", "memory");
#endif
}
static int ReadChar(void) {
int c;
#ifdef __REAL_MODE__
asm volatile("int\t$0x16" : "=a"(c) : "0"(0) : "memory");
c &= 0xff;
#else
static int buf;
asm volatile("syscall"
: "=a"(c)
: "0"(0), "D"(0), "S"(&buf), "d"(1)
: "rcx", "r11", "memory");
c = buf;
#endif
return c;
}

View File

@ -1,105 +0,0 @@
;; (setq lisp-indent-function 'common-lisp-indent-function)
;; (paredit-mode)
;; ________
;; /_ __/ /_ ___
;; / / / __ \/ _ \
;; / / / / / / __/
;; /_/ /_/ /_/\___/
;; __ _________ ____ ________ ____
;; / / / _/ ___// __ \ / ____/ /_ ____ _/ / /__ ____ ____ ____
;; / / / / \__ \/ /_/ / / / / __ \/ __ `/ / / _ \/ __ \/ __ `/ _ \
;; / /____/ / ___/ / ____/ / /___/ / / / /_/ / / / __/ / / / /_/ / __/
;; /_____/___//____/_/ \____/_/ /_/\__,_/_/_/\___/_/ /_/\__, /\___/
;; /____/
;;
;; THE LISP CHALLENGE
;;
;; PICK YOUR FAVORITE PROGRAMMING LANGUAGE
;; IMPLEMENT THE TINIEST POSSIBLE LISP MACHINE THAT
;; BOOTSTRAPS JOHN MCCARTHY'S METACIRCULAR EVALUATOR
;; WINNING IS DEFINED BY LINES OF CODE FOR SCRIPTING LANGUAGES
;; WINNING IS DEFINED BY BINARY FOOTPRINT FOR COMPILED LANGUAGES
;;
;; @SEE LISP FROM NOTHING; NILS M. HOLM; LULU PRESS, INC. 2020
;; @SEE RECURSIVE FUNCTIONS OF SYMBOLIC EXPRESSIONS AND THEIR
;; COMPUTATION BY MACHINE, PART I; JOHN MCCARTHY, MASSACHUSETTS
;; INSTITUTE OF TECHNOLOGY, CAMBRIDGE, MASS. APRIL 1960
;; NIL ATOM
;; ABSENCE OF VALUE
NIL
;; CONS CELL
;; BUILDING BLOCK OF DATA STRUCTURES
(CONS NIL NIL)
;; REFLECTION
;; EVERYTHING IS AN ATOM OR NOT AN ATOM
(ATOM NIL)
(ATOM (CONS NIL NIL))
;; QUOTING
;; CODE IS DATA AND DATA IS CODE
(QUOTE (CONS NIL NIL))
(CONS (QUOTE CONS) (CONS NIL (CONS NIL NIL)))
;; LOGIC
;; LAW OF IDENTITY VIA STRING INTERNING
(EQ (QUOTE A) (QUOTE A))
;; FIND FIRST ATOM IN TREE
;; RECURSIVE CONDITIONAL FUNCTION BINDING
((LAMBDA (FF X) (FF X))
(QUOTE (LAMBDA (X)
(COND ((ATOM X) X)
((QUOTE T) (FF (CAR X))))))
(QUOTE ((A) B C)))
;; LISP IMPLEMENTED IN LISP
;; USED TO EVALUATE FIND FIRST ATOM IN TREE
;; REQUIRES CONS CAR CDR QUOTE ATOM EQ LAMBDA COND
;; FIXES BUGS FROM JOHN MCCARTHY PAPER AND MORE MINIMAL
((LAMBDA (ASSOC EVCON BIND APPEND EVAL)
(EVAL (QUOTE ((LAMBDA (FF X) (FF X))
(QUOTE (LAMBDA (X)
(COND ((ATOM X) X)
((QUOTE T) (FF (CAR X))))))
(QUOTE ((A) B C))))
NIL))
(QUOTE (LAMBDA (X E)
(COND ((EQ E NIL) NIL)
((EQ X (CAR (CAR E))) (CDR (CAR E)))
((QUOTE T) (ASSOC X (CDR E))))))
(QUOTE (LAMBDA (C E)
(COND ((EVAL (CAR (CAR C)) E) (EVAL (CAR (CDR (CAR C))) E))
((QUOTE T) (EVCON (CDR C) E)))))
(QUOTE (LAMBDA (V A E)
(COND ((EQ V NIL) E)
((QUOTE T) (CONS (CONS (CAR V) (EVAL (CAR A) E))
(BIND (CDR V) (CDR A) E))))))
(QUOTE (LAMBDA (A B)
(COND ((EQ A NIL) B)
((QUOTE T) (CONS (CAR A) (APPEND (CDR A) B))))))
(QUOTE (LAMBDA (E A)
(COND
((ATOM E) (ASSOC E A))
((ATOM (CAR E))
(COND
((EQ (CAR E) NIL) (QUOTE *UNDEFINED))
((EQ (CAR E) (QUOTE QUOTE)) (CAR (CDR E)))
((EQ (CAR E) (QUOTE ATOM)) (ATOM (EVAL (CAR (CDR E)) A)))
((EQ (CAR E) (QUOTE EQ)) (EQ (EVAL (CAR (CDR E)) A)
(EVAL (CAR (CDR (CDR E))) A)))
((EQ (CAR E) (QUOTE CAR)) (CAR (EVAL (CAR (CDR E)) A)))
((EQ (CAR E) (QUOTE CDR)) (CDR (EVAL (CAR (CDR E)) A)))
((EQ (CAR E) (QUOTE CONS)) (CONS (EVAL (CAR (CDR E)) A)
(EVAL (CAR (CDR (CDR E))) A)))
((EQ (CAR E) (QUOTE COND)) (EVCON (CDR E) A))
((EQ (CAR E) (QUOTE LABEL)) (EVAL (CAR (CDR (CDR E)))
(APPEND (CAR (CDR E)) A)))
((EQ (CAR E) (QUOTE LAMBDA)) E)
((QUOTE T) (EVAL (CONS (EVAL (CAR E) A) (CDR E)) A))))
((EQ (CAR (CAR E)) (QUOTE LAMBDA))
(EVAL (CAR (CDR (CDR (CAR E))))
(BIND (CAR (CDR (CAR E))) (CDR E) A)))))))

View File

@ -1,23 +0,0 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify │
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License. │
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of │
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software │
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/macros.h"
_start: jmp main
.endfn _start,globl

View File

@ -1,51 +0,0 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify │
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License. │
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of │
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software │
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
.code16
.section .start,"ax",@progbits
_start: jmp 1f
1: ljmp $0x600>>4,$_begin
.type _start,@function
.size _start,.-_start
.globl _start
_begin: push %cs
pop %ds
push %cs
pop %es
mov $0x70000>>4,%ax
cli
mov %ax,%ss
xor %sp,%sp
sti
cld
xor %ax,%ax
xor %di,%di
mov $0x7c00-0x600,%cx
rep stosb
xchg %di,%bx
inc %cx
xor %dh,%dh
mov $v_sectors+0x0200,%ax
int $0x13
xor %bp,%bp
jmp main
.type _begin,@function
.size _begin,.-_begin

View File

@ -49,13 +49,16 @@ void AppendStr(struct Buffer *b, const char *s) {
}
void AppendWide(struct Buffer *b, wint_t wc) {
unsigned i;
uint64_t wb;
wb = wc;
if (!isascii(wb)) wb = tpenc(wb);
char buf[8];
i = 0;
wb = tpenc(wc);
do {
AppendChar(b, wb & 0xFF);
buf[i++] = wb & 0xFF;
wb >>= 8;
} while (wb);
AppendData(b, buf, i);
}
int AppendFmt(struct Buffer *b, const char *fmt, ...) {
@ -72,24 +75,31 @@ int AppendFmt(struct Buffer *b, const char *fmt, ...) {
}
/**
* Writes buffer until completion, interrupt, or error occurs.
* Writes buffer until completion, or error occurs.
*/
ssize_t WriteBuffer(struct Buffer *b, int fd) {
bool t;
char *p;
ssize_t rc;
size_t wrote, n;
p = b->p;
n = b->i;
t = false;
do {
if ((rc = write(fd, p, n)) != -1) {
wrote = rc;
p += wrote;
n -= wrote;
} else if (errno == EINTR) {
break;
t = true;
} else {
return -1;
}
} while (n);
return 0;
if (!t) {
return 0;
} else {
errno = EINTR;
return -1;
}
}

View File

@ -28,6 +28,7 @@
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/mremap.h"
#include "libc/sysv/consts/msync.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/ok.h"
#include "libc/sysv/consts/prot.h"
@ -177,6 +178,7 @@ struct ElfWriter *elfwriter_open(const char *path, int mode) {
void elfwriter_close(struct ElfWriter *elf) {
size_t i;
FlushTables(elf);
CHECK_NE(-1, msync(elf->map, elf->wrote, MS_SYNC));
CHECK_NE(-1, munmap(elf->map, elf->mapsize));
CHECK_NE(-1, ftruncate(elf->fd, elf->wrote));
CHECK_NE(-1, close(elf->fd));

View File

@ -1632,6 +1632,7 @@ static void OpMovRqCq(struct Machine *m, uint32_t rde) {
}
static void OpMovCqRq(struct Machine *m, uint32_t rde) {
int64_t cr3;
switch (ModrmReg(rde)) {
case 0:
m->cr0 = Read64(RegRexbRm(m, rde));
@ -1640,7 +1641,12 @@ static void OpMovCqRq(struct Machine *m, uint32_t rde) {
m->cr2 = Read64(RegRexbRm(m, rde));
break;
case 3:
m->cr3 = Read64(RegRexbRm(m, rde));
cr3 = Read64(RegRexbRm(m, rde));
if (0 <= cr3 && cr3 + 512 * 8 <= m->real.n) {
m->cr3 = cr3;
} else {
ThrowProtectionFault(m);
}
break;
case 4:
m->cr4 = Read64(RegRexbRm(m, rde));

View File

@ -1,5 +1,5 @@
/*-*- mode: ld-script; indent-tabs-mode: nil; tab-width: 2; coding: utf-8 -*-│
vi: set et sts=2 tw=2 fenc=utf-8 :vi
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
@ -17,39 +17,47 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/alg/arraylist2.h"
#include "tool/build/lib/endian.h"
#include "tool/build/lib/machine.h"
#include "tool/build/lib/pml4t.h"
ENTRY(_start)
static void AppendContiguousMemoryRange(struct ContiguousMemoryRanges *ranges,
int64_t a, int64_t b) {
APPEND(&ranges->p, &ranges->i, &ranges->n,
(&(struct ContiguousMemoryRange){a, b}));
}
SECTIONS {
.text 0x7c00 - 0x600 : {
*(.start .start.*)
rodata = .;
*(.rodata .rodata.*)
. = 0x1fe;
SHORT(0xaa55);
*(.text .text.*)
/*BYTE(0x90);*/
_etext = .;
. = ALIGN(512);
}
.bss : {
bss = .;
*(.bss .bss.*)
*(COMMON)
}
/DISCARD/ : {
*(.*)
static void FindContiguousMemoryRangesImpl(
struct Machine *m, struct ContiguousMemoryRanges *ranges, int64_t addr,
unsigned level, int64_t pt, int64_t a, int64_t b) {
int64_t i, page, entry;
for (i = a; i < b; ++i) {
entry = Read64(m->real.p + pt + i * 8);
if (!(entry & 1)) continue;
entry &= 0x7ffffffff000;
page = (addr | i << level) << 16 >> 16;
if (level == 12) {
if (ranges->i && page == ranges->p[ranges->i - 1].b) {
ranges->p[ranges->i - 1].b += 0x1000;
} else {
AppendContiguousMemoryRange(ranges, page, page + 0x1000);
}
} else if (entry + 512 * 8 <= m->real.n) {
FindContiguousMemoryRangesImpl(m, ranges, page, level - 9, entry, 0, 512);
}
}
}
boot = 0x7c00;
q.syntax = 8192*2;
q.look = 8192*2+256;
q.globals = 8192*2+256+2;
q.index = 8192*2+256+2+2;
q.token = 8192*2+256+2+2+2;
q.str = 8192*2+256+2+2+2+128;
v_sectors = SIZEOF(.text) / 512;
void FindContiguousMemoryRanges(struct Machine *m,
struct ContiguousMemoryRanges *ranges) {
uint64_t cr3;
ranges->i = 0;
if ((m->mode & 3) == XED_MODE_LONG) {
cr3 = m->cr3 & 0x7ffffffff000;
FindContiguousMemoryRangesImpl(m, ranges, 0, 39, cr3, 256, 512);
FindContiguousMemoryRangesImpl(m, ranges, 0, 39, cr3, 0, 256);
} else {
AppendContiguousMemoryRange(ranges, 0, m->real.n);
}
}

View File

@ -8,6 +8,17 @@ COSMOPOLITAN_C_START_
#define UnmaskPageAddr(x) SignExtendAddr(MaskPageAddr(x))
#define SignExtendAddr(x) ((int64_t)((uint64_t)(x) << 16) >> 16)
struct ContiguousMemoryRanges {
size_t i, n;
struct ContiguousMemoryRange {
int64_t a;
int64_t b;
} * p;
};
void FindContiguousMemoryRanges(struct Machine *,
struct ContiguousMemoryRanges *);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_TOOL_BUILD_LIB_PML4T_H_ */

View File

@ -0,0 +1,179 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "tool/build/lib/modrm.h"
#include "tool/build/lib/xmmtype.h"
static void UpdateXmmTypes(struct Machine *m, struct XmmType *xt, int regtype,
int rmtype) {
xt->type[RexrReg(m->xedd->op.rde)] = regtype;
if (IsModrmRegister(m->xedd->op.rde)) {
xt->type[RexbRm(m->xedd->op.rde)] = rmtype;
}
}
static void UpdateXmmSizes(struct Machine *m, struct XmmType *xt, int regsize,
int rmsize) {
xt->size[RexrReg(m->xedd->op.rde)] = regsize;
if (IsModrmRegister(m->xedd->op.rde)) {
xt->size[RexbRm(m->xedd->op.rde)] = rmsize;
}
}
void UpdateXmmType(struct Machine *m, struct XmmType *xt) {
switch (m->xedd->op.dispatch) {
case 0x12E: // UCOMIS
case 0x12F: // COMIS
case 0x151: // SQRT
case 0x152: // RSQRT
case 0x153: // RCP
case 0x158: // ADD
case 0x159: // MUL
case 0x15C: // SUB
case 0x15D: // MIN
case 0x15E: // DIV
case 0x15F: // MAX
case 0x1C2: // CMP
if (Osz(m->xedd->op.rde) || Rep(m->xedd->op.rde) == 2) {
UpdateXmmTypes(m, xt, kXmmDouble, kXmmDouble);
} else {
UpdateXmmTypes(m, xt, kXmmFloat, kXmmFloat);
}
break;
case 0x12A: // CVTPI2PS,CVTSI2SS,CVTPI2PD,CVTSI2SD
if (Osz(m->xedd->op.rde) || Rep(m->xedd->op.rde) == 2) {
UpdateXmmSizes(m, xt, 8, 4);
UpdateXmmTypes(m, xt, kXmmDouble, kXmmIntegral);
} else {
UpdateXmmSizes(m, xt, 4, 4);
UpdateXmmTypes(m, xt, kXmmFloat, kXmmIntegral);
}
break;
case 0x15A: // CVT{P,S}{S,D}2{P,S}{S,D}
if (Osz(m->xedd->op.rde) || Rep(m->xedd->op.rde) == 2) {
UpdateXmmTypes(m, xt, kXmmFloat, kXmmDouble);
} else {
UpdateXmmTypes(m, xt, kXmmDouble, kXmmFloat);
}
break;
case 0x15B: // CVT{,T}{DQ,PS}2{PS,DQ}
UpdateXmmSizes(m, xt, 4, 4);
if (Osz(m->xedd->op.rde) || Rep(m->xedd->op.rde) == 3) {
UpdateXmmTypes(m, xt, kXmmIntegral, kXmmFloat);
} else {
UpdateXmmTypes(m, xt, kXmmFloat, kXmmIntegral);
}
break;
case 0x17C: // HADD
case 0x17D: // HSUB
case 0x1D0: // ADDSUB
if (Osz(m->xedd->op.rde)) {
UpdateXmmTypes(m, xt, kXmmDouble, kXmmDouble);
} else {
UpdateXmmTypes(m, xt, kXmmFloat, kXmmFloat);
}
break;
case 0x164: // PCMPGTB
case 0x174: // PCMPEQB
case 0x1D8: // PSUBUSB
case 0x1DA: // PMINUB
case 0x1DC: // PADDUSB
case 0x1DE: // PMAXUB
case 0x1E0: // PAVGB
case 0x1E8: // PSUBSB
case 0x1EC: // PADDSB
case 0x1F8: // PSUBB
case 0x1FC: // PADDB
UpdateXmmSizes(m, xt, 1, 1);
UpdateXmmTypes(m, xt, kXmmIntegral, kXmmIntegral);
break;
case 0x165: // PCMPGTW
case 0x175: // PCMPEQW
case 0x171: // PSRLW,PSRAW,PSLLW
case 0x1D1: // PSRLW
case 0x1D5: // PMULLW
case 0x1D9: // PSUBUSW
case 0x1DD: // PADDUSW
case 0x1E1: // PSRAW
case 0x1E3: // PAVGW
case 0x1E4: // PMULHUW
case 0x1E5: // PMULHW
case 0x1E9: // PSUBSW
case 0x1EA: // PMINSW
case 0x1ED: // PADDSW
case 0x1EE: // PMAXSW
case 0x1F1: // PSLLW
case 0x1F6: // PSADBW
case 0x1F9: // PSUBW
case 0x1FD: // PADDW
UpdateXmmSizes(m, xt, 2, 2);
UpdateXmmTypes(m, xt, kXmmIntegral, kXmmIntegral);
break;
case 0x166: // PCMPGTD
case 0x176: // PCMPEQD
case 0x172: // PSRLD,PSRAD,PSLLD
case 0x1D2: // PSRLD
case 0x1E2: // PSRAD
case 0x1F2: // PSLLD
case 0x1FA: // PSUBD
case 0x1FE: // PADDD
UpdateXmmSizes(m, xt, 4, 4);
UpdateXmmTypes(m, xt, kXmmIntegral, kXmmIntegral);
break;
case 0x173: // PSRLQ,PSRLQ,PSRLDQ,PSLLQ,PSLLDQ
case 0x1D3: // PSRLQ
case 0x1D4: // PADDQ
case 0x1F3: // PSLLQ
case 0x1F4: // PMULUDQ
case 0x1FB: // PSUBQ
UpdateXmmSizes(m, xt, 8, 8);
UpdateXmmTypes(m, xt, kXmmIntegral, kXmmIntegral);
break;
case 0x16B: // PACKSSDW
case 0x1F5: // PMADDWD
UpdateXmmSizes(m, xt, 4, 2);
UpdateXmmTypes(m, xt, kXmmIntegral, kXmmIntegral);
break;
case 0x163: // PACKSSWB
case 0x167: // PACKUSWB
UpdateXmmSizes(m, xt, 1, 2);
UpdateXmmTypes(m, xt, kXmmIntegral, kXmmIntegral);
break;
case 0x128: // MOVAPS Vps Wps
if (IsModrmRegister(m->xedd->op.rde)) {
xt->type[RexrReg(m->xedd->op.rde)] = xt->type[RexbRm(m->xedd->op.rde)];
xt->size[RexrReg(m->xedd->op.rde)] = xt->size[RexbRm(m->xedd->op.rde)];
}
break;
case 0x129: // MOVAPS Wps Vps
if (IsModrmRegister(m->xedd->op.rde)) {
xt->type[RexbRm(m->xedd->op.rde)] = xt->type[RexrReg(m->xedd->op.rde)];
xt->size[RexbRm(m->xedd->op.rde)] = xt->size[RexrReg(m->xedd->op.rde)];
}
break;
case 0x16F: // MOVDQA Vdq Wdq
if (Osz(m->xedd->op.rde) && IsModrmRegister(m->xedd->op.rde)) {
xt->type[RexrReg(m->xedd->op.rde)] = xt->type[RexbRm(m->xedd->op.rde)];
xt->size[RexrReg(m->xedd->op.rde)] = xt->size[RexbRm(m->xedd->op.rde)];
}
break;
default:
return;
}
}

View File

@ -0,0 +1,21 @@
#ifndef COSMOPOLITAN_TOOL_BUILD_LIB_XMMTYPE_H_
#define COSMOPOLITAN_TOOL_BUILD_LIB_XMMTYPE_H_
#include "tool/build/lib/machine.h"
#define kXmmIntegral 0
#define kXmmDouble 1
#define kXmmFloat 2
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
struct XmmType {
uint8_t type[16];
uint8_t size[16];
};
void UpdateXmmType(struct Machine *, struct XmmType *);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_TOOL_BUILD_LIB_XMMTYPE_H_ */

View File

@ -44,6 +44,7 @@
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/msync.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/prot.h"
#include "libc/time/time.h"
@ -362,6 +363,7 @@ void OpenObject(struct Package *pkg, struct Object *obj, int mode, int prot,
}
void CloseObject(struct Object *obj) {
msync(obj->elf, obj->size, MS_SYNC);
CHECK_NE(-1, munmap(obj->elf, obj->size));
}

940
tool/viz/memzoom.c 100644
View File

@ -0,0 +1,940 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "dsp/scale/cdecimate2xuint8x8.h"
#include "libc/bits/bits.h"
#include "libc/bits/hilbert.h"
#include "libc/bits/morton.h"
#include "libc/bits/safemacros.h"
#include "libc/calls/calls.h"
#include "libc/calls/ioctl.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/siginfo.h"
#include "libc/calls/struct/stat.h"
#include "libc/calls/struct/termios.h"
#include "libc/calls/struct/winsize.h"
#include "libc/calls/ucontext.h"
#include "libc/conv/conv.h"
#include "libc/conv/itoa.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/log/log.h"
#include "libc/macros.h"
#include "libc/nexgen32e/bsf.h"
#include "libc/runtime/runtime.h"
#include "libc/sock/sock.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/str/tpenc.h"
#include "libc/sysv/consts/ex.h"
#include "libc/sysv/consts/exit.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/poll.h"
#include "libc/sysv/consts/prot.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/consts/termios.h"
#include "libc/time/time.h"
#include "libc/unicode/unicode.h"
#include "third_party/getopt/getopt.h"
#define USAGE \
" [-hznmH] [-p PID] [PATH]\n\
\n\
DESCRIPTION\n\
\n\
Memory Viewer\n\
\n\
FLAGS\n\
\n\
-h help\n\
-z zoom\n\
-w white bg color\n\
-m morton ordering\n\
-H hilbert ordering\n\
-n natural scrolling\n\
-p PID shows process virtual memory\n\
-f INT frames per second [default 10]\n\
\n\
SHORTCUTS\n\
\n\
z or + zoom\n\
Z or - unzoom\n\
ctrl+wheel zoom point\n\
wheel scroll\n\
l linearize\n\
m mortonize\n\
h hilbertify\n\
n next mapping\n\
N next mapping ending\n\
p prev mapping\n\
P prev mapping ending\n\
k up\n\
j down\n\
b page up\n\
space page down\n\
g home\n\
G end\n\
q quit\n\
\n"
#define CTRL(C) ((C) ^ 0100)
#define MAXZOOM 14
#define COLOR 253
#define LINEAR 0
#define MORTON 1
#define HILBERT 2
#define INTERRUPTED 0x1
#define RESIZED 0x2
#define MOUSE_LEFT_DOWN 0
#define MOUSE_MIDDLE_DOWN 1
#define MOUSE_RIGHT_DOWN 2
#define MOUSE_LEFT_UP 4
#define MOUSE_MIDDLE_UP 5
#define MOUSE_RIGHT_UP 6
#define MOUSE_LEFT_DRAG 32
#define MOUSE_MIDDLE_DRAG 33
#define MOUSE_RIGHT_DRAG 34
#define MOUSE_WHEEL_UP 64
#define MOUSE_WHEEL_DOWN 65
#define MOUSE_CTRL_WHEEL_UP 80
#define MOUSE_CTRL_WHEEL_DOWN 81
struct Ranges {
long i;
struct Range {
long a;
long b;
} p[512];
};
static const signed char kThePerfectKernel[8] = {-1, -3, 3, 17, 17, 3, -3, -1};
static bool white;
static bool natural;
static bool mousemode;
static int fd;
static int pid;
static int out;
static int fps;
static int zoom;
static int order;
static int action;
static long tyn;
static long txn;
static long size;
static long offset;
static long lowest;
static long highest;
static long canvassize;
static long buffersize;
static long displaysize;
static char *buffer;
static uint8_t *canvas;
static struct stat st;
static struct Ranges ranges;
static struct termios oldterm;
static char path[PATH_MAX];
static char mapspath[PATH_MAX];
static int Write(const char *s) {
return write(out, s, strlen(s));
}
static void HideCursor(void) {
Write("\e[?25l");
}
static void ShowCursor(void) {
Write("\e[?25h");
}
static void EnableMouse(void) {
mousemode = true;
Write("\e[?1000;1002;1015;1006h");
}
static void DisableMouse(void) {
mousemode = false;
Write("\e[?1000;1002;1015;1006l");
}
static void LeaveScreen(void) {
Write("\e[H\e[J");
}
static void GetTtySize(void) {
struct winsize wsize;
wsize.ws_row = tyn + 1;
wsize.ws_col = txn;
getttysize(out, &wsize);
tyn = MAX(2, wsize.ws_row) - 1;
txn = MAX(17, wsize.ws_col) - 16;
tyn = rounddown2pow(tyn);
txn = rounddown2pow(txn);
tyn = MIN(tyn, txn);
}
static void EnableRaw(void) {
struct termios term;
memcpy(&term, &oldterm, sizeof(term));
term.c_cc[VMIN] = 1;
term.c_cc[VTIME] = 1;
term.c_iflag &= ~(INPCK | ISTRIP | PARMRK | INLCR | IGNCR | ICRNL | IXON);
term.c_lflag &= ~(IEXTEN | ICANON | ECHO | ECHONL);
term.c_cflag &= ~(CSIZE | PARENB);
term.c_cflag |= CS8;
term.c_iflag |= IUTF8;
ioctl(out, TCSETS, &term);
}
static void OnExit(void) {
LeaveScreen();
ShowCursor();
DisableMouse();
ioctl(out, TCSETS, &oldterm);
}
static void OnSigInt(int sig, struct siginfo *sa, struct ucontext *uc) {
action |= INTERRUPTED;
}
static void OnSigWinch(int sig, struct siginfo *sa, struct ucontext *uc) {
action |= RESIZED;
}
static void Setup(void) {
tyn = 80;
txn = 24;
action = RESIZED;
ioctl(out, TCGETS, &oldterm);
HideCursor();
EnableRaw();
EnableMouse();
atexit(OnExit);
sigaction(SIGINT, &(struct sigaction){.sa_sigaction = OnSigInt}, NULL);
sigaction(SIGWINCH, &(struct sigaction){.sa_sigaction = OnSigWinch}, NULL);
}
static noreturn void FailPath(const char *s, int rc) {
Write("error: ");
Write(s);
Write(": ");
Write(path);
Write("\n");
exit(rc);
}
static void SetExtent(long lo, long hi) {
lowest = lo;
highest = hi;
offset = MIN(hi, MAX(lo, offset));
}
static void Open(void) {
int err;
if ((fd = open(path, O_RDONLY)) == -1) {
FailPath("open() failed", errno);
}
fstat(fd, &st);
size = st.st_size;
SetExtent(0, size);
}
static void *Allocate(size_t n) {
return mmap(NULL, n, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1,
0);
}
static void SetupCanvas(void) {
if (canvassize) {
munmap(canvas, canvassize);
munmap(buffer, buffersize);
}
displaysize = ROUNDUP(ROUNDUP(tyn * txn * (1ul << zoom), 16), 1ul << zoom);
canvassize = ROUNDUP(displaysize, FRAMESIZE);
buffersize = ROUNDUP(tyn * txn * 16 + PAGESIZE, FRAMESIZE);
canvas = Allocate(canvassize);
buffer = Allocate(buffersize);
}
static long IndexSquare(long y, long x) {
switch (order) {
case LINEAR:
return y * txn + x;
case MORTON:
return morton(y, x);
case HILBERT:
return hilbert(txn, y, x);
default:
return 0;
}
}
static long Index(long y, long x) {
long i;
if (order == LINEAR) {
i = 0;
} else {
i = x / tyn;
x = x % tyn;
}
return i * tyn * tyn + IndexSquare(y, x);
}
static void PreventBufferbloat(void) {
long double now, rate;
static long double last;
now = nowl();
rate = 1. / fps;
if (now - last < rate) {
dsleep(rate - (now - last));
}
last = now;
}
static bool HasPendingInput(void) {
struct pollfd fds[1];
fds[0].fd = 0;
fds[0].events = POLLIN;
fds[0].revents = 0;
poll(fds, ARRAYLEN(fds), 0);
return fds[0].revents & (POLLIN | POLLERR);
}
static int GetCurrentRange(void) {
int i;
if (ranges.i) {
for (i = 0; i < ranges.i; ++i) {
if (offset < ranges.p[i].a) return MAX(0, i - 1);
if (offset < ranges.p[i].b) return i;
}
return ranges.i - 1;
} else {
return -1;
}
}
static void Move(long d) {
offset = MIN(highest, MAX(lowest, ROUNDDOWN(offset + d, 1L << zoom)));
}
static void SetZoom(long y, long x, int d) {
long a, b, i;
if ((0 <= y && y < tyn) && (0 <= x && x < txn)) {
i = Index(y, x);
a = zoom;
b = MIN(MAXZOOM, MAX(0, a + d));
zoom = b;
Move(i * (1L << a) - i * (1L << b));
SetupCanvas();
}
}
static void OnZoom(long y, long x) {
SetZoom(y, x, +1);
}
static void OnUnzoom(long y, long x) {
SetZoom(y, x, -1);
}
static void OnUp(void) {
Move(-(txn * (1l << zoom)));
}
static void OnDown(void) {
Move(txn * (1l << zoom));
}
static void OnPageUp(void) {
Move(-(txn * (tyn - 2) * (1l << zoom)));
}
static void OnPageDown(void) {
Move(txn * (tyn - 2) * (1l << zoom));
}
static void OnHome(void) {
offset = lowest;
}
static void OnEnd(void) {
offset = MAX(lowest, highest - txn * tyn * (1l << zoom));
}
static void OnLinear(void) {
order = LINEAR;
GetTtySize();
SetupCanvas();
}
static void OnMorton(void) {
order = MORTON;
SetupCanvas();
}
static void OnHilbert(void) {
order = HILBERT;
SetupCanvas();
}
static void OnNext(void) {
int i;
if ((i = GetCurrentRange()) != -1) {
if (i + 1 < ranges.i) {
offset = ranges.p[i + 1].a;
}
}
}
static void OnPrev(void) {
int i;
if ((i = GetCurrentRange()) != -1) {
if (i) {
offset = ranges.p[i - 1].a;
}
}
}
static void OnNextEnd(void) {
long i, n;
if ((i = GetCurrentRange()) != -1) {
n = tyn * txn * (1L << zoom);
if (offset < ranges.p[i].b - n) {
offset = ranges.p[i].b - n;
} else if (i + 1 < ranges.i) {
offset = MAX(ranges.p[i + 1].a, ranges.p[i + 1].b - n);
}
}
}
static void OnPrevEnd(void) {
long i, n;
if ((i = GetCurrentRange()) != -1) {
n = tyn * txn * (1L << zoom);
if (i) {
offset = MAX(ranges.p[i - 1].a, ranges.p[i - 1].b - n);
}
}
}
static void OnMouse(char *p) {
int e, x, y;
struct Panel *ep;
e = strtol(p, &p, 10);
if (*p == ';') ++p;
x = min(txn, max(1, strtol(p, &p, 10))) - 1;
if (*p == ';') ++p;
y = min(tyn, max(1, strtol(p, &p, 10))) - 1;
e |= (*p == 'm') << 2;
switch (e) {
case MOUSE_WHEEL_UP:
if (natural) {
OnDown();
OnDown();
OnDown();
} else {
OnUp();
OnUp();
OnUp();
}
break;
case MOUSE_WHEEL_DOWN:
if (natural) {
OnUp();
OnUp();
OnUp();
} else {
OnDown();
OnDown();
OnDown();
}
break;
case MOUSE_CTRL_WHEEL_UP:
if (natural) {
OnZoom(y, x);
} else {
OnUnzoom(y, x);
}
break;
case MOUSE_CTRL_WHEEL_DOWN:
if (natural) {
OnUnzoom(y, x);
} else {
OnZoom(y, x);
}
break;
default:
break;
}
}
static void ReadKeyboard(void) {
char buf[32], *p = buf;
memset(buf, 0, sizeof(buf));
if (readansi(0, buf, sizeof(buf)) == -1) {
if (errno == EINTR) return;
exit(errno);
}
switch (*p++) {
case 'q':
exit(0);
case '+':
case 'z':
OnZoom(0, 0);
break;
case '-':
case 'Z':
OnUnzoom(0, 0);
break;
case 'b':
OnPageUp();
break;
case 'n':
OnNext();
break;
case 'p':
OnPrev();
break;
case 'N':
OnNextEnd();
break;
case 'P':
OnPrevEnd();
break;
case ' ':
case CTRL('V'):
OnPageDown();
break;
case 'g':
OnHome();
break;
case 'G':
OnEnd();
break;
case 'k':
case CTRL('P'):
OnUp();
break;
case 'j':
case CTRL('N'):
OnDown();
break;
case 'l':
OnLinear();
break;
case 'm':
if (order == MORTON) {
OnLinear();
} else {
OnMorton();
}
break;
case 'M':
if (mousemode) {
DisableMouse();
} else {
EnableMouse();
}
break;
case 'h':
case 'H':
if (order == HILBERT) {
OnLinear();
} else {
OnHilbert();
}
break;
case '\e':
switch (*p++) {
case 'v':
OnPageUp();
break;
case '[':
switch (*p++) {
case '<':
OnMouse(p);
break;
case 'A':
OnUp();
break;
case 'B':
OnDown();
break;
case 'F':
OnEnd();
break;
case 'H':
OnHome();
break;
case '1':
switch (*p++) {
case '~':
OnHome();
break;
default:
break;
}
break;
case '4':
switch (*p++) {
case '~':
OnEnd();
break;
default:
break;
}
break;
case '5':
switch (*p++) {
case '~':
OnPageUp();
break;
default:
break;
}
break;
case '6':
switch (*p++) {
case '~':
OnPageDown();
break;
default:
break;
}
break;
case '7':
switch (*p++) {
case '~':
OnHome();
break;
default:
break;
}
break;
case '8':
switch (*p++) {
case '~':
OnEnd();
break;
default:
break;
}
break;
default:
break;
}
break;
default:
break;
}
break;
default:
break;
}
}
static void LoadRanges(void) {
char b[512];
struct Range range;
int i, t, n, fd, err;
if ((fd = open(mapspath, O_RDONLY)) == -1) {
err = errno;
Write("error: process died\n");
exit(err);
}
t = 0;
range.a = 0;
range.b = 0;
ranges.i = 0;
for (;;) {
if ((n = read(fd, b, sizeof(b))) == -1) exit(1);
if (!n) break;
for (i = 0; i < n; ++i) {
switch (t) {
case 0:
if (isxdigit(b[i])) {
range.a <<= 4;
range.a += hextoint(b[i]);
} else if (b[i] == '-') {
t = 1;
}
break;
case 1:
if (isxdigit(b[i])) {
range.b <<= 4;
range.b += hextoint(b[i]);
} else if (b[i] == ' ') {
t = 2;
}
break;
case 2:
if (b[i] == '\n') {
if (ranges.i < ARRAYLEN(ranges.p)) {
ranges.p[ranges.i++] = range;
}
range.a = 0;
range.b = 0;
t = 0;
}
break;
default:
unreachable;
}
}
}
close(fd);
if (ranges.i) {
SetExtent(ranges.p[0].a, ranges.p[ranges.i - 1].b);
} else {
SetExtent(0, 0);
}
}
static int InvertXtermGreyscale(int x) {
return -(x - 232) + 255;
}
static void Render(void) {
char *p;
int c, fg2, rc, fg;
long i, y, x, w, n, got;
p = buffer;
p = stpcpy(p, "\e[H");
for (y = 0; y < tyn; ++y) {
fg = -1;
for (x = 0; x < txn; ++x) {
c = canvas[Index(y, x)];
if (c < 32) {
fg2 = 237 + c * ((COLOR - 237) / 32.);
} else if (c >= 232) {
fg2 = COLOR + (c - 232) * ((255 - COLOR) / (256. - 232));
} else {
fg2 = COLOR;
}
if (fg2 != fg) {
fg = fg2;
if (white) {
fg = InvertXtermGreyscale(fg);
}
p = stpcpy(p, "\e[38;5;");
p += int64toarray_radix10(fg, p);
*p++ = 'm';
}
w = tpenc(kCp437[c]);
do {
*p++ = w & 0xff;
w >>= 8;
} while (w);
}
p = stpcpy(p, "\e[0m ");
p += uint64toarray_radix16(offset + y * txn * (1ul << zoom), p);
p = stpcpy(p, "\e[K\r\n");
}
p = stpcpy(p, "\e[7m\e[K");
n = strlen(path);
if (n > txn - 3 - 1 - 7) {
p = mempcpy(p, path, txn - 1 - 7 - 3);
p = stpcpy(p, "...");
} else {
p = stpcpy(p, path);
for (i = n; i < txn - 1 - 7; ++i) {
*p++ = ' ';
}
}
p = stpcpy(p, " memzoom\e[0m ");
if (!pid) {
p += uint64toarray_radix10(MIN(offset / (long double)size * 100, 100), p);
p = stpcpy(p, "%-");
p += uint64toarray_radix10(
MIN((offset + tyn * txn * (1l << zoom)) / (long double)size * 100, 100),
p);
p = stpcpy(p, "% ");
}
p += uint64toarray_radix10(1L << zoom, p);
p = stpcpy(p, "x\e[J");
PreventBufferbloat();
for (i = 0, n = p - buffer; i < n; i += got) {
got = 0;
if ((rc = write(out, buffer + i, n - i)) == -1) {
if (errno == EINTR) continue;
exit(errno);
}
got = rc;
}
}
static void Zoom(long have) {
long i, n, r;
n = canvassize;
for (i = 0; i < zoom; ++i) {
cDecimate2xUint8x8(n, canvas, kThePerfectKernel);
n >>= 1;
}
if (n < tyn * txn) {
memset(canvas + n, 0, canvassize - n);
}
if (have != -1) {
n = have / (1L << zoom);
i = n / txn;
r = n % txn;
if (r) ++i;
if (order == LINEAR) {
for (; i < tyn; ++i) {
canvas[txn * i] = '~';
}
}
}
}
static void FileZoom(void) {
long have;
have = MIN(displaysize, size - offset);
have = pread(fd, canvas, have, offset);
have = MAX(0, have);
memset(canvas + have, 0, canvassize - have);
Zoom(have);
Render();
}
static void RangesZoom(void) {
long a, b, c, d, i;
LoadRanges();
memset(canvas, 1, canvassize);
a = offset;
b = MIN(highest, offset + tyn * txn * (1ul << zoom));
for (i = 0; i < ranges.i; ++i) {
if ((a >= ranges.p[i].a && a < ranges.p[i].b) ||
(b >= ranges.p[i].a && b < ranges.p[i].b) ||
(a < ranges.p[i].a && b >= ranges.p[i].b)) {
c = MAX(a, ranges.p[i].a);
d = MIN(b, ranges.p[i].b);
pread(fd, canvas + (c - offset), d - c, c);
}
}
Zoom(-1);
Render();
}
static void MemZoom(void) {
char *p;
int c, fg2, rc, fg;
long i, n, r, w, y, x, got, have;
do {
if (action & INTERRUPTED) {
break;
}
if (action & RESIZED) {
GetTtySize();
SetupCanvas();
action &= ~RESIZED;
}
if (HasPendingInput()) {
ReadKeyboard();
continue;
}
if (pid) {
RangesZoom();
} else {
FileZoom();
}
} while (!(action & INTERRUPTED));
}
static noreturn void PrintUsage(int rc) {
Write("SYNOPSIS\n\n ");
Write(program_invocation_name);
Write(USAGE);
exit(rc);
}
static void GetOpts(int argc, char *argv[]) {
int opt;
char *p;
fps = 10;
while ((opt = getopt(argc, argv, "hzHnwf:p:")) != -1) {
switch (opt) {
case 'z':
++zoom;
break;
case 'n':
natural = true;
break;
case 'm':
order = MORTON;
break;
case 'H':
order = HILBERT;
break;
case 'w':
white = true;
break;
case 'f':
fps = strtol(optarg, NULL, 0);
fps = MAX(1, fps);
break;
case 'p':
if (strcmp(optarg, "self") == 0) {
pid = getpid();
} else {
pid = strtol(optarg, NULL, 0);
}
break;
case 'h':
PrintUsage(EXIT_SUCCESS);
default:
PrintUsage(EX_USAGE);
}
}
if (pid) {
p = stpcpy(path, "/proc/");
p += int64toarray_radix10(pid, p);
stpcpy(p, "/mem");
p = stpcpy(mapspath, "/proc/");
p += int64toarray_radix10(pid, p);
stpcpy(p, "/maps");
} else {
if (optind == argc) {
PrintUsage(EX_USAGE);
}
if (!memccpy(path, argv[optind], '\0', sizeof(path))) {
PrintUsage(EX_USAGE);
}
}
}
int main(int argc, char *argv[]) {
if (!NoDebug()) showcrashreports();
out = 1;
GetOpts(argc, argv);
Open();
Setup();
MemZoom();
return 0;
}

View File

@ -364,7 +364,7 @@ void WithImageFile(const char *path,
void fn(long yn, long xn, unsigned char RGB[3][yn][xn])) {
struct stat st;
void *map, *data, *data2;
int fd, yn, xn, cn, dyn, dxn;
int fd, yn, xn, cn, dyn, dxn, syn, sxn;
CHECK_NE(-1, (fd = open(path, O_RDONLY)), "%s", path);
CHECK_NE(-1, fstat(fd, &st));
CHECK_GT(st.st_size, 0);
@ -387,10 +387,26 @@ void WithImageFile(const char *path,
cn = 3;
}
if (g_flags.height && g_flags.width) {
syn = yn;
sxn = xn;
dyn = g_flags.height;
dxn = g_flags.width;
while (HALF(syn) > dyn || HALF(sxn) > dxn) {
if (HALF(sxn) > dxn) {
Magikarp2xX(yn, xn, data, syn, sxn);
Magikarp2xX(yn, xn, (char *)data + yn * xn, syn, sxn);
Magikarp2xX(yn, xn, (char *)data + yn * xn * 2, syn, sxn);
sxn = HALF(sxn);
}
if (HALF(syn) > dyn) {
Magikarp2xY(yn, xn, data, syn, sxn);
Magikarp2xY(yn, xn, (char *)data + yn * xn, syn, sxn);
Magikarp2xY(yn, xn, (char *)data + yn * xn * 2, syn, sxn);
syn = HALF(syn);
}
}
data = EzGyarados(3, dyn, dxn, gc(memalign(32, dyn * dxn * 3)), cn, yn, xn,
data, 0, cn, dyn, dxn, yn, xn, 0, 0, 0, 0);
data, 0, cn, dyn, dxn, syn, sxn, 0, 0, 0, 0);
yn = dyn;
xn = dxn;
}