{"id":16572293,"url":"https://github.com/jonocarroll/r64","last_synced_at":"2026-02-12T08:33:35.731Z","repository":{"id":71014092,"uuid":"438960646","full_name":"jonocarroll/r64","owner":"jonocarroll","description":"A c64/6502 assembler in R","archived":false,"fork":false,"pushed_at":"2021-12-16T11:12:33.000Z","size":368,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-09-19T10:46:48.773Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://coolbutuseless.github.io/package/r64","language":"HTML","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"coolbutuseless/r64","license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jonocarroll.png","metadata":{"files":{"readme":"README.Rmd","changelog":"NEWS.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-12-16T11:11:10.000Z","updated_at":"2021-12-16T11:12:36.000Z","dependencies_parsed_at":null,"dependency_job_id":"3b70d570-2029-4a9c-8714-104fb26c0991","html_url":"https://github.com/jonocarroll/r64","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/jonocarroll/r64","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonocarroll%2Fr64","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonocarroll%2Fr64/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonocarroll%2Fr64/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonocarroll%2Fr64/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jonocarroll","download_url":"https://codeload.github.com/jonocarroll/r64/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonocarroll%2Fr64/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29361819,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-12T01:03:07.613Z","status":"online","status_checked_at":"2026-02-12T02:00:06.911Z","response_time":55,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":[],"created_at":"2024-10-11T21:27:02.146Z","updated_at":"2026-02-12T08:33:35.716Z","avatar_url":"https://github.com/jonocarroll.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"---\noutput: github_document\n---\n\n```{r setup, include = FALSE}\nsuppressPackageStartupMessages({\n  library(dplyr)\n  library(r64)  \n})\n\nknitr::opts_chunk$set(echo=TRUE)\n```\n\n\n\n\n`r64` - a c64/6502 assembler in R\n==============================================================================\n\nFull documentation is available here: [coolbutuseless.github.io/package/r64](https://coolbutuseless.github.io/package/r64)\n\n\nRationale\n------------------------------------------------------------------------------\n\n* Who *doesn't* want a compiler for a **1 MHz** 8-bit computer with 16 colours and a max resolution of 320x200?\n* A 6502 assembler in R will allow for preparing/calculating \n  data in R and then incorporating directly into the assembly code. e.g. \n    * creating character sets \n    * computing animation paths\n\n\nFeatures\n------------------------------------------------------------------------------\n\nGeneral features\n\n* Basic syntax only. Similar to TASS64 syntax.\n* Settable program counter\n    * e.g. `* = $0801`\n* Defined variables\n    * e.g. `border = $d020`\n* Low/High byte extraction from symbol (similar to TASS)\n    * e.g. `lda #\u003croutine` will store the low byte of the address of symbol `routine` in the `A` register\n\nFor integration with R\n\n* `.rtext` directive to include an `R` string as text data\n* `.rbyte` directive to include an `R` integer vector as bytes \n* `{...}` to delimit code to be evaluated at run time to manipulate labels and variables e.g. `lda {border + 1}`\n\n\nLimitations\n------------------------------------------------------------------------------\n\nThis is the very definition of a *toy assembler*.  There is enough support for\nwriting simple code, but it is lacking a lot of niceties.\n\n* For most basic assembly code, the syntax is very similar to TASS64\n* Errors will be thrown for most syntax errors, but there are still corner cases\n  that are untested. It might just turn some things into code even if\n  it doesn't make sense.  e.g. a `jmp` to a location more than 128 bytes away.\n* `indirect indexed` and `indexed indirect` modes using symbolic addresses seems\n  to work, but need more tests.\n* No Support for decimal and binary literals. e.g. Currently can't do `lda #127` or `lda #b01111111`\n* All bytes must be in 2-character hexadecimal and preceded by a `$`.  I.e. `$01` is ok, but `$1` won't work.\n* All word values by in 4-character hexadecimal and preceded by a `$`.  I.e. `$0820` is ok, but `$820` won't work.\n* You may need to force zero page addressing modes with symbols/variables by explicitly noting that only the low byte should\n  be used. e.g.\n    * `BUFPNT = $A6`  is the pointer to the tape i/o buffer\n    * `lda \u003cBUFPNT` should be used to force zero page addressing mode, otherwise `lda BUFPNT` would assume absolute addressing mode.\n    * The actual result would be the same, but zero page addressing takes fewer bytes and is faster than absolute.\n\n\nInstallation\n------------------------------------------------------------------------------\n\n`r64` depends on a lot of tidyverse packages as well as the [flexo](https://github.com/coolbutuseless/flexo)\npackage (for splitting the c64 asm code into tokens).\n\n```{r eval=FALSE}\ndevtools::install_github('coolbutuseless/flexo') # for lexing the 6502 assembly into tokens\ndevtools::install_github('coolbutuseless/r64')\n```\n\n\nDocumentation\n------------------------------------------------------------------------------\n\nThe documentation for the package is available online at [coolbutuseless.github.io](https://coolbutuseless.github.io/package/r64) \n(thanks to [pkgdown](https://github.com/r-lib/pkgdown))\n\n\n\nVignettes\n------------------------------------------------------------------------------\n\n* `helloworld` - write text to the screen\n* `helloborder` - colour cycling in the border as fast as possible\n* `helloworld_details` - a look at the intermediate assembly outputs during compilation\n* `ascii` - Using R-specific `.rbyte` directive to put characters from an R variable into the assembly code\n* `custom_character_set` - Building a custom character set in R and passing it into the assemblly code with the `.rbyte` directive.\n* `symbol-arithmetic` - How to do arithmetic on the program counter and address labels\n\nThe code for the vignettes is also available in the `prg/` directory of this repository.\n\n\n\nRunning c64 programs\n------------------------------------------------------------------------------\n\n* Online emulators (both of which will let you drag-and-drop)\n    * [c64js.com](http://www.c64js.com/)\n    * [virtualconsoles.com](https://virtualconsoles.com/online-emulators/c64/)\n\n* Native\n    * [VICE](http://vice-emu.sourceforge.net/)\n\n    \n    \n\nA simple 6502 program \n------------------------------------------------------------------------------\n\nThe following c64/6502 ASM code will clear the screen and then \nwrite `Hello #rstats!` at the top\n\n```{r}\nasm \u003c- '\n*=$0801\n  .byte $0c, $08, $0a, $00, $9e, $20  ; 10 SYS 2080\n  .byte $32, $30, $38, $30, $00, $00\n  .byte $00\n\n*=$0820\n      lda #$93        ; Clear the screen\n      jsr $ffd2\n\n      ldx #$00        ; initialise the offset pointer into our message\nloop  lda message,x   ; load a character and write it to screen \n      and #$3f        ; Manually place chars on screen\n      sta $0400,x\n      inx\n      cpx #$0e\n      bne loop\n\n      rts\n\nmessage\n    .text \"Hello #rstats!\"\n'\n```\n\n\nCompile the program and show the compiled program bytes\n------------------------------------------------------------------------------\n\n```{r}\nprg_df \u003c- r64::compile(asm)\nr64::extract_prg_bytes(prg_df)\n```\n\n\nCompare `r64` output to `TASS64` output\n------------------------------------------------------------------------------\n\nTASS64 is a well known 6502 assembler.  Here i will use it to compile the same code and \ncompare the output. It should be identical. `r64::TASS` is a an [R6](https://cran.r-project.org/package=R6)\nwrapper around the command line executable to just make things a bit easier.\n\n```{r}\ntass \u003c- r64::TASS$new(asm = asm)$compile()\n\nidentical(r64::extract_prg_bytes(prg_df), tass$get_prg())\n```\n\n\nRun c64 programs using VICE\n------------------------------------------------------------------------------\n\n```{r eval=FALSE}\nprg_filename \u003c- tempfile()\nr64::save_prg(prg_df, prg_filename)\nsystem(paste(\"/usr/local/opt/vice/x64.app/Contents/MacOS/x64\", prg_filename), wait=FALSE)\n```\n\n![helloworld output](vignettes/img/helloworld.gif)\n\n\nBreakdown of assembly process\n------------------------------------------------------------------------------\n\nThe compiler makes a few passes through the data to resolve symbol values.\n\nThe `r64::compile()` function is just a wrapper which calls the following 4 functions\n\n1. `line_tokens \u003c- r64::create_line_tokens(asm)`\n    * For each line in the input break it into tokens.\n    * Filter any rows that contain no instructions\n2. `prg_df \u003c- r64::create_prg_df(line_tokens)`\n    * Create a data.frame from `line_tokens`\n    * This is the key data structure for the compilation process\n    * The compilation process is just a matter of manipulating this data.frame and merging with information about the instructions\n3. `prg_df \u003c- r64::process_symbols(prg_df)`\n    * Resolve labels to their actual addresses\n    * Replace any defined variables with their values\n4. `prg_df \u003c- r64::process_zero_padding(prg_df)`\n    * If there are gaps between blocks of code, insert zero bytes\n\nAn example of the final form of the `prg_df` data.frame is show below.  The actual contents of the c64 `prg` file\nis just the sequence of values in the `hexbytes` column. \n\n```{r}\nprg_df %\u003e%\n  select(addr, label, line, opcommand, op, ophex, nbytes, hexbytes) %\u003e%\n  knitr::kable()\n```\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjonocarroll%2Fr64","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjonocarroll%2Fr64","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjonocarroll%2Fr64/lists"}