{"id":22176926,"url":"https://github.com/jeremydavis519/rusty-asm","last_synced_at":"2025-07-26T16:31:08.073Z","repository":{"id":57665826,"uuid":"131090714","full_name":"jeremydavis519/rusty-asm","owner":"jeremydavis519","description":"A layer of syntactic sugar between Rust and inline assembly","archived":false,"fork":false,"pushed_at":"2018-11-07T02:39:45.000Z","size":106,"stargazers_count":11,"open_issues_count":8,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-10-31T12:13:15.476Z","etag":null,"topics":["assembly","inline-asm","rust"],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jeremydavis519.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-04-26T02:45:13.000Z","updated_at":"2022-10-17T05:27:57.000Z","dependencies_parsed_at":"2022-09-14T13:10:18.749Z","dependency_job_id":null,"html_url":"https://github.com/jeremydavis519/rusty-asm","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jeremydavis519%2Frusty-asm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jeremydavis519%2Frusty-asm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jeremydavis519%2Frusty-asm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jeremydavis519%2Frusty-asm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jeremydavis519","download_url":"https://codeload.github.com/jeremydavis519/rusty-asm/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":227565416,"owners_count":17787169,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["assembly","inline-asm","rust"],"created_at":"2024-12-02T08:21:42.685Z","updated_at":"2024-12-02T08:21:43.284Z","avatar_url":"https://github.com/jeremydavis519.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build Status](https://travis-ci.org/jeremydavis519/rusty-asm.svg?branch=master)](https://travis-ci.org/jeremydavis519/rusty-asm) [![Coverage Status](https://coveralls.io/repos/github/jeremydavis519/rusty-asm/badge.svg?branch=master)](https://coveralls.io/github/jeremydavis519/rusty-asm?branch=master)\n\n# rusty-asm\n\nA layer of syntactic sugar between Rust and inline assembly\n\nRust currently has the [`asm!`] macro for writing inline ASM within a function defined in Rust. It uses the same basic\nformat as GCC uses for its own inline ASM--and that format isn't the most ergonomic. Here's a small example, taken from\n[the OSDev wiki] and translated into Rust:\n\n[`asm!`]: https://doc.rust-lang.org/1.12.0/book/inline-assembly.html\n[the OSDev wiki]: https://wiki.osdev.org/Inline_Assembly/Examples\n\n```rust\n// Retrieves a value from memory in a different segment than the one currently being used (x86[-64])\nunsafe fn farpeekl(segment_selector: u16, offset: *const u32) -\u003e u32 {\n    let ret: u32;\n    asm!(\"\n        push %fs\n        mov $1, %fs\n        mov %fs:($2), $0\n        pop %fs\n    \"   : \"=r\"(ret) : \"r\"(segment_selector), \"r\"(offset)\n    );\n    ret\n}\n```\n\n(This example actually looks a little cleaner in my opinion than it does when written for GCC, but it could still use some work.)\n\nThe `asm!` macro is currently an unstable, nightly-only feature. From what I've seen, there are several reasons, but one of\nthem is the syntax. It's too easy to forget the precise order of things (which come first: inputs or outputs?), and parts of\nit are needlessly redundant. Using `\"=r\"`, `\"r\"`, or `\"~r\"` means the register is, respectively, an output, an input, or\nclobbered, but the different types also have to be separated by colons. So using `asm!`, the programmer has to remember both\nways to tell the compiler what it should expect to happen to each register.\n\nThis crate attempts to improve the syntax surrounding inline ASM so that it's both easier to read and easier to write without\nlooking up the required format every time. It works by using a procedural macro (1) to overload Rust's `let` statements, making\nvariables capable of storing information about how they'll be used in upcoming inline ASM blocks, and (2) to parse `asm`\nblocks that allow variables defined with the new syntax to be used directly in the ASM code.\n\n### Change Log\n\n* 0.1 - Initial release\n* 0.2 - Inner blocks are now supported.\n\n## Setup\n\nTo use this crate, add the following to `Cargo.toml`:\n\n```toml\n[dependencies]\nrusty-asm = \"0.2.1\"\n```\n\nThen reference the crate in your main source file and activate the features you'll need:\n\n```rust\nextern crate rusty_asm;\nuse rusty_asm::rusty_asm; // Because who wants to write `rusty_asm::rusty_asm!`?\n```\n\nNote that you'll still need a nightly compiler for this. `rusty_asm` doesn't make inline ASM stable.\n\n### Supported Features\n\nThe following features are available:\n\n* `proc-macro`: Causes [`proc-macro2`](https://crates.io/crates/proc-macro2) to act as a thin wrapper over\n  [`proc_macro`](https://doc.rust-lang.org/proc_macro/index.html), including the parts that are still unstable.\n  The benefit of this feature is that it allows `rusty-asm` to provide its own warnings, which should make\n  debugging your own code easier.\n\n## Basic Syntax\n\nIn the place where you want to add some inline ASM, call `rusty_asm!` like so:\n\n```rust\nrusty_asm! {\n    // (arbitrary Rust statements go here)\n\n    asm (/* maybe some options in here */) {\n        // (insert your ASM code here, in quotes)\n    }\n\n    // (possibly some cleanup code here)\n}\n```\n\nThe contents of the `asm` block need to be a string literal to make sure that Rust's parser doesn't mess up the\nformatting. (Macros currently don't have access to whitespace information.) See the examples below for more specifics\nabout how it should look.\n\nAlso, it's possible to have multiple `asm` blocks in the same `rusty_asm!` block, in case you want to reuse your bridge\nvariables (see below).\n\n## Bridge Variables\n\nA _bridge variable_ is a variable that bridges the gap between Rust and ASM by incorporating the input/ouput/clobber\ninformation in its definition. They can only be defined inside `rusty_asm!` blocks, and because the macro makes a new scope,\nthey are dropped when execution leaves those blocks (along with any other variables that are defined in the same scope). In\norder to define a bridge variable, you'll need to use one of three keywords that are only valid inside `rusty_asm!` blocks:\n\n* `in`\n* `out`\n* `inout`\n\nEach of these keywords is used in a \"let\" statement to define a bridge variable. The exact syntax is as follows:\n\n```text\nlet [mut] \u003cidentifier\u003e: [\u003ctype\u003e:] in(\u003cconstraint\u003e) [= \u003cexpression\u003e];\nlet [mut] \u003cidentifier\u003e: [\u003ctype\u003e:] out(\u003cconstraint\u003e) [= \u003cexpression\u003e];\nlet [mut] \u003cidentifier\u003e: [\u003ctype\u003e:] inout(\u003cconstraint\u003e) [= \u003cexpression\u003e];\n```\n\nThe optional `\u003ctype\u003e` is any Rust type, as far as the macro knows, but it should be something that makes sense to put in the\nappropriate register (e.g. `usize`, `i8`, etc. for a general-purpose integer register).\n\nIn addition, you can specify that you'll clobber a particular register (or that you'll clobber memory) with this syntax:\n\n```text\nclobber(\u003cconstraint\u003e);\n```\n\nwhere `\u003cconstraint\u003e` is either the name of a register (like `\"eax\"`) or `\"memory\"`.\n\nThese statements correspond to LLVM constraints in the following way:\n\n```text\n// in, out, or inout:\n\u003cnew-constraint\u003e(\u003cidentifier\u003e)\n// clobber\n\u003cnew-constraint\u003e\n```\n\nIn each case, `\u003cnew_constraint\u003e` is equivalent to `\u003cconstraint\u003e` except that for the `out` and `clobber` keywords, the `'='`\nor `'~'` is prepended to satisfy `asm!` and LLVM. So, for instance, if you write the constraint as `\"r\"`, it will be\nautomatically translated to `\"=r\"` or `\"~r\"` as needed before being given to the compiler. The `inout` keyword results in two\nnew constraints: (1) the equivalent constraint for the `out` keyword (e.g. `\"=r\"`) and (2) an input constraint that's tied to\nit (e.g. `\"0\"`).\n\nIn order to let Rust know how to work with the bridge variables, `rusty_asm!` removes the new keywords and constraints during\nmacro expansion, so as far as Rust knows, they're just ordinary variables.\n\n## The `asm` Block\n\nWhen an `asm` block is encountered, it is converted directly into an asm! invocation, using all of the constraints that have\nbeen created thus far. The `asm` block's syntax is as follows:\n\n```text\nasm [(\u003coptions\u003e)] {\n    \"\u003casm-code\u003e\"\n}\n```\n\n`\u003coptions\u003e` is an optional comma-separated list of the options that would be after the 4th colon if `asm!` were being used, such\nas `\"volatile\"`. `\u003casm-code\u003e` is pure ASM code, enclosed in quotes, except that it can (and should) use the bridge variables\nthat have been defined above the `asm` block.\n\nIn order to reference a bridge variable from inside an `asm` block, insert `$\u003cident\u003e` into the code, where `\u003cident\u003e` is the\nvariable's identifier. As with the `asm!` macro, `$$` encodes a literal dollar sign.\n\n## The `rusty_asm!` Block and Scope\n\nThe new macro puts its entire contents inside a new scope, so that any variables defined therein are dropped at the end. Their\nvalues can be moved to variables outside the macro's scope before it ends, using regular Rust code, if they need to be preserved.\nIn addition, just like any of Rust's code blocks, this one has a return value that can be used by ending the block with an\nexpression.\n\nAlso, as of version 0.2, the macro also correctly handles inner blocks, shadowing and dropping bridge variables just like Rust\nshadows and drops regular variables. That means you can now write code like this:\n\n```rust\n// Sends 1, 2, or 4 bytes at once to an ISA address (x86/x64).\nunsafe fn poke_isa(port: u16, value: usize, bytes: u8) {\n    rusty_asm! {\n        let port: in(\"{dx}\") = port;\n        if bytes == 1 {\n            let value: in(\"{al}\") = value as u8;\n            asm(\"volatile\", \"intel\") {\n                \"out $port, $value\"\n            }\n        } else if bytes == 2 {\n            let value: in(\"{ax}\") = value as u16;\n            asm(\"volatile\", \"intel\") {\n                \"out $port, $value\"\n            }\n        } else {\n            assert_eq!(bytes, 4);\n            let value: in(\"{eax}\") = value as u32;\n            asm(\"volatile\", \"intel\") {\n                \"out $port, $value\"\n            }\n        }\n    };\n}\n```\n\nDefining bridge variables in `if let` and `while let` constructions is still not supported, since Rust doesn't support explicit\ntype annotations in them either, and I imagine the syntax would become overly complex.\n\n## Further Reading\n\nThere are too many platform-specific constraints and options that you can specify to list them all here. Follow these links for\nmore information.\n\n* [The Rust book: Inline Assembly chapter]. Discusses what can be done with the `asm!` macro.\n* [LLVM's inline assembly documentation]. Documents exactly what is allowed in LLVM inline assembly (and therefore in Rust's `asm!`\n  invocations), along with platform-specific details.\n\n[The Rust book: Inline Assembly chapter]: https://doc.rust-lang.org/1.12.0/book/inline-assembly.html\n[LLVM's inline assembly documentation]: http://llvm.org/docs/LangRef.html#inline-assembler-expressions\n\n## Usage Examples\n\nNote that while all of these examples use x86 assembly, `rusty_asm!` should work with any assembly dialect that Rust supports (which\nprobably means any dialect that LLVM supports).\n\n```rust\n// Disables interrupts on an x86 CPU.\nunsafe fn disable_interrupts() {\n    rusty_asm! {\n        asm(\"volatile\") { // This block has to be marked \"volatile\" to make sure the compiler, seeing\n           \"cli\"          // no outputs and no clobbers, doesn't assume it does nothing and\n        }                 // decide to \"optimize\" it away.\n    };\n}\n```\n\n```rust\n// Shifts the hexadecimal digits of `existing` up and puts `digit` in the resulting gap.\nfn append_hex_digit(existing: usize, digit: u8) -\u003e usize {\n    assert!(digit \u003c 0x10);\n    unsafe {\n        rusty_asm! {\n            let mut big: inout(\"r\") = existing;\n            let little: in(\"r\") = digit as usize;\n\n            asm {\"\n                shll %4, $big\n                orl $little, $big\n            \"}\n\n            big\n        }\n    }\n}\n\nassert_eq!(append_hex_digit(0, 0), 0);\nassert_eq!(append_hex_digit(0, 0xf), 0xf);\nassert_eq!(append_hex_digit(4, 2), 0x42);\n```\n\n## Limitations\n\nThe bridge variable declaration syntax is slightly more restrictive than that of general `let` statements in that it only allows\nan identifier after the `let` keyword, not an arbitrary pattern. So, for instance, this statement would not work:\n\n```rust\nrusty_asm! {\n    let (a, b): (in(\"r\"), out(\"r\")) = (12, 14);\n    /* ... */\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjeremydavis519%2Frusty-asm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjeremydavis519%2Frusty-asm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjeremydavis519%2Frusty-asm/lists"}