{"id":62,"date":"2023-05-05T14:33:00","date_gmt":"2023-05-05T13:33:00","guid":{"rendered":"https:\/\/techblog.dansbo.dk\/?p=62"},"modified":"2024-01-04T14:57:56","modified_gmt":"2024-01-04T13:57:56","slug":"how-to-make-library-modules-for-cc65-ca65","status":"publish","type":"post","link":"https:\/\/techblog.dansbo.dk\/?p=62","title":{"rendered":"How to make library modules for CC65\/CA65"},"content":{"rendered":"\n<p><em>The text below is copied directly from the posts at\u00a0<a href=\"https:\/\/web.archive.org\/web\/20230929175400\/https:\/\/www.commanderx16.com\/forum\/viewtopic.php?p=25298\">https:\/\/<\/a><a href=\"https:\/\/www.commanderx16.com\/forum\/viewtopic.php?p=25298\">www.commanderx16.com<\/a><a href=\"https:\/\/web.archive.org\/web\/20230929175400\/https:\/\/www.commanderx16.com\/forum\/viewtopic.php?p=25298\">\/forum\/viewtopic.php?p=25298<\/a>. All credits should go to the respective authors, ZeroByte and Kliepatsch.<\/em><\/p>\n\n\n\n<p>Making libraries in CA65 is quite straightforward, but your code needs to be configured properly for use in \u201cmulti-file\u201d projects.<\/p>\n\n\n\n<p><strong>TL;DR:<\/strong><\/p>\n\n\n\n<p>Below you will find a simple set of example code and build instructions if you want to \u201cjust do it\u201d without any further explanations.<\/p>\n\n\n\n<p><strong>The basics:<\/strong><\/p>\n\n\n\n<p>Creating a library is essentially just packaging up .o files into a library file. Therefore, your library routines should be written so that they would work as independent source files in any other multi-source-file project. For this howto, let\u2019s suppose you want to create a library routine \u201chelloworld\u201d which just uses CHROUT to print \u201chello world.\u201d First, let\u2019s consider how you would use this routine if it were written in a stand-alone source file.<\/p>\n\n\n\n<p>First, you would create a file \u201chello.asm\u201d which defines the routine and imports\/exports the necessary symbols to perform its task and be reachable from the rest of the project. (more on symbol export\/import later). With your routine in its own file, you build your project by listing all source files on the command line:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cl65 -t cx16 -o MYPROG.PRG myprog.asm hello.asm<\/code><\/pre>\n\n\n\n<p>Assuming that these assembly files know how to properly import and export the required symbols, this command will build MYPROG.PRG which can now be run. Ostensibly, somewhere in main.asm, there is a call to \u201chelloworld\u201d which prints that string to the screen. This command works pretty much the same for C projects:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cl65 -t cx16 -o MYPROG.PRG main.c hello.c<\/code><\/pre>\n\n\n\n<p>Of course, you can mix and match \u2013 main.c can use assembly code from assembly sources, e.g: (all-in-one method)<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cl65 -t cx16 -o MYPROG.PRG myprog.c hello.asm<\/code><\/pre>\n\n\n\n<p>Keep in mind that cl65 is sort of a front-end batch processor command that performs several sub-steps in order to assemble and link your program.<\/p>\n\n\n\n<p><strong>Step-by-step building:<\/strong><\/p>\n\n\n\n<p>The reason you can easily mix C and assembly is that behind the scenes, cl65 first compiles\/assembles each source file into an object file (.o) An object file is as close as the compiler or assembler can get to the final binary machine code of your program. The one thing that\u2019s missing during this step is the final resolution of symbols into the actual memory addresses based on where the program is set up to be loaded in memory. If \u201chelloworld\u201d is built at the beginning of your program, it will be in a different location in memory than it would if it were appended to the end of your program. Thus, it\u2019s not known just yet exactly what address to use for any JSR \/ JMP statements calling your code. Thus the symbol \u201chelloworld:\u201d remains unresolved.<\/p>\n\n\n\n<p>The final step of resolving these symbols is done by the linker (ld65). It puts all of the pieces of object code together into the final program. Since the linker is what decides the final locations where everything is actually going to go, it has enough information to turn these remaining unresolved symbols into their final actual memory addresses. It does this, combines all of the object code together, and writes out the .PRG file which can now be loaded into memory and run on the X16.<\/p>\n\n\n\n<p>Before, we performed all of these steps at once. However, you can tell cl65 to skip the linking phase on your project and do only the compiling\/assembling. You do this by adding the -c command-line switch. This will cause cl65 to stop short of calling the linker to create a complete executable program. Instead, it will simply create the object files from your sources.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cl65 -t cx16 -c myprog.c\n\ncl65 -t cx16 -c hello.asm<\/code><\/pre>\n\n\n\n<p>Running these two commands produces the files main.o and hello.o \u2013 note that you need to run them as separate commands \u2013 once for each source file.<\/p>\n\n\n\n<p>You can then finish your build just as if these files were the source files: (precompiled method)<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cl65 -t cx16 -o MYPROG.PRG myprog.o hello.o<\/code><\/pre>\n\n\n\n<p>Running these two commands produces the files main.o and hello.o \u2013 note that you need to run them as separate commands \u2013 once for each source file.<\/p>\n\n\n\n<p>You can then finish your build just as if these files were the source files: (precompiled method)<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cl65 -t cx16 -o MYPROG.PRG myprog.o hello.o<\/code><\/pre>\n\n\n\n<p>You could even pre-assemble the helloworld module, but compile directly from source on the main .c file: (hybrid method)<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cl65 -t cx16 -c hello.asm\n\ncl65 -t cx16 -o MYPROG.PRG myprog.c hello.o<\/code><\/pre>\n\n\n\n<p>All of these methods will produce the same executable program MYPROG.PRG \u2013 the second and third methods just take multiple steps. These \u201cgranular\u201d methods are useful in larger projects. If you compile your sources into object code, then you don\u2019t need to recompile everything whenever you only make changes to one source file. Suppose hello.asm did not change, but you made changes to myprog.c \u2013 you would recompile myprog.c as above in the (precompiled method), or using the hybrid method, but you would not need to reassemble hello.o as it has not changed. Make is designed to work with this mode of project building. Make will go through and determine which .o files are older than their source files, and only recompile those components.<\/p>\n\n\n\n<p>If your project had more auxilliary functions such as \u201cgoodbyeworld\u201d and \u201cwaitforkey\u201d in their own source files, you would pre-compile them and then add their filenames to the final build command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cl65 -t cx16 -o MYPROG.PRG myprog.c hello.o goodbye.o waitkey.o<\/code><\/pre>\n\n\n\n<p><strong>Now we get to libraries:<\/strong><\/p>\n\n\n\n<p>A library file is simply an archive of .o files all packed together into a single file. Instead of having a bunch of .o files laying around in a directory, you simply have one library file, e.g. \u201cmytools.lib\u201d which includes routines like helloworld: from hello.asm, etc.<\/p>\n\n\n\n<p>So this means that to create it, all you need to do is first compile or assemble your sources into object files, and then add the object files to your library archive using the archive tool ar65.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ar65 a mytools.lib hello.o goodbye.o waitkey.o<\/code><\/pre>\n\n\n\n<p>Note that these must be object files, not the uncompiled sources. Now, you no longer need to specify all of your individual source files:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cl65 -t cx16 -o MYPROG.PRG myprog.c hello.o goodbye.o waitkey.o<\/code><\/pre>\n\n\n\n<p>Instead, you can just reference the library file:<\/p>\n\n\n\n<p>cl65 -t cx16 -o MYPROG.PRG myprog.c mytools.lib<\/p>\n\n\n\n<p>ld65 will go through the library archive when it needs to resolve symbols such as the names of your functions and variables defined in the archive.<\/p>\n\n\n\n<p><strong>What about the symbols?<\/strong><\/p>\n\n\n\n<p>ld65 is able to find the symbols in the archive and use them for any code that needs them. However, the archive only contains the symbols that the sources told the compiler to export. Export means to make this symbol available from outside of this particular source file. If you do not export \u201chelloworld\u201d then that symbol can\u2019t be called from myprog.prg.<\/p>\n\n\n\n<p>C vs Assembly symbols: The cc65\/ca65 suite uses the convention that any symbols in C will be presented to assembly with an underscore _ prepended to them. Thus a variable \u201cfoo\u201d in C would be seen as the symbol \u201c_foo\u201d in assembly. Likewise, any symbols with leading underscores in assembly will be presented in C with the leading underscore removed.<\/p>\n\n\n\n<p>Thus, your helloworld function could also have another symbol: \u201c_helloworld\u201d which points to the same code. Doing this, you may now call \u201chelloworld()\u201d from C just as you would from assembly.<\/p>\n\n\n\n<p>In myprog.c, you would declare it as follows:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>extern void helloworld();\n<\/code><\/pre>\n\n\n\n<p>That\u2019s all you need to do to access a simple function like helloworld: that was actually written in assembly.<\/p>\n\n\n\n<p>Had helloworld been written in C, then you could still access it from assembly projects:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.import _helloworld\n\n...\n\njsr _helloworld<\/code><\/pre>\n\n\n\n<p>If you would like to be able to refer to the same function as helloworld: in assembly and as helloworld() in C, you can simply create a second symbol using the := syntax in your assembly module, and export both symbols:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.export _helloworld\n.export helloworld\n\n_helloworld := helloworld\n\n.proc helloworld: near\n  ;code\n.endproc<\/code><\/pre>\n\n\n\n<p><strong>Variables:<\/strong><\/p>\n\n\n\n<p>Lastly, I should point out that importing and exporting variables works in exactly the same way. Just remember that \u201cvariables\u201d and \u201ctypes\u201d don\u2019t exist in assembly, at least not in the sense that they do in C. In assembly, everything is just a reference to an address. So if you reserve some space and give it a symbol name in assembly, and export that symbol, this is enough to pass it between C and assembly. (the _ rules still apply, obviously)<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.export foo\n.export _foo\n\n.segment \"BSS\"\n\nfoo:  .res 2 ; reserve 2 bytes of memory for a 16-bit value, foo\n_foo := foo ; alias it as _foo for importing into C projects<\/code><\/pre>\n\n\n\n<p>In case you\u2019re wondering, BSS segment is for uninitialized variables. I.e.: no values are emitted into the binary created by the build process. It is expected that programs will write values into this memory during run time. Basically, it just reserves memory space without adding size to the actual code. Other segments where you might put data:<\/p>\n\n\n\n<p>RODATA\u00a0:\u00a0for constants such as strings, lookup tables, etc.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.segment \"RODATA\"\nmessage: .asciiz \"hello world\"<\/code><\/pre>\n\n\n\n<p>DATA : for initialized variables:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.segment \"DATA\"\nbar:  .word  $1000<\/code><\/pre>\n\n\n\n<p>Any concept of type is defined in C, as assembly just doesn\u2019t care. In C, you would define foo: \u201cextern uint16_t foo;\u201d or if it\u2019s a pointer of some sort, it could be \u201cextern char* foo;\u201d etc.<\/p>\n\n\n\n<p><strong>Nutshell:<\/strong><\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Create the sources for your library just as any other components of a project<\/li>\n\n\n\n<li>Compile\/assemble them as object files\n<ul class=\"wp-block-list\">\n<li><code>cl65 -t cx16 -s hello.asm<\/code><\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>Combine the objects into a single archive\n<ul class=\"wp-block-list\">\n<li><code>ar65 a mytools.lib hello.o<\/code><\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>Create an include file for easy access to the required symbols (optional but recommended)\n<ul class=\"wp-block-list\">\n<li><code>.import helloworld (assembly .inc style)<\/code><\/li>\n\n\n\n<li><code>extern void helloworld(); (C .h style - be sure to export _helloworld if the source is assembly)<\/code><\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<p><strong>Conclusion:<\/strong><\/p>\n\n\n\n<p>These are just the basics, obviously. If you need to pass parameters between C and assembly, you should consult the cc65 documentation for more details on how that\u2019s done.<\/p>\n\n\n\n<p>While this howto is written as a mixed assembly \/ C example, there is no reason that your libraries need to be written in assembly.<\/p>\n\n\n\n<p>You could have some complicated routines, such as trig.c which creates SIN() COS() and TAN() functions, written entirely in C.<\/p>\n\n\n\n<p>Just build trig.c into trig.o and add trig.o to your archive, and it will work. The main thing to remember is to keep the underscores in mind if you\u2019re mixing assembly. If pure C, don\u2019t worry about it.<\/p>\n\n\n\n<p><strong>A little more about what\u2019s going on:<\/strong><\/p>\n\n\n\n<p>Let\u2019s unpack this C statement:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>extern void helloworld();<\/code><\/pre>\n\n\n\n<p>What you\u2019ve done is told the compiler \u201cokay, so there\u2019s this function helloworld. It doesn\u2019t return anything, and it needs no parameters. Furthermore, I\u2019m not going to define it here. It will come from some other source. (that\u2019s what the extern means). So \u2013 this is enough information for the compiler to work with. When it compiles this file, anywhere that it sees a call to helloworld(), it knows that all it needs to do is compile it to the assembly code: \u201cJSR _helloworld\u201d which is then assembled as \u201c$20 {_helloworld}\u201d. That\u2019s what goes in the .o file \u2013 the opcode for JSR ($20) and a marker \u201csome symbol from somewhere \u2013 linker, please fill in this blank when you do your thing.\u201d<\/p>\n\n\n\n<p>Basically, you\u2019re telling the compiler \u2013 here\u2019s how to use it. Do as much as you can. Someone else will come along and supply that code later. The compiler then trusts this and gets as far as it can go, leaving that {_helloworld} as filler.<\/p>\n\n\n\n<p>Later, the linker sees this mark, and then adds _helloworld to the list of symbols needed. As it goes through all of the .o files, whenever it sees some file saying \u201cI have this _helloworld symbol in case anyone needs it\u201d \u2013 that symbol gets plugged in. (This is what .export does in assembly \u2013 it tells the linker \u201cif anyone wants to use my symbol _helloworld, give them my address please.\u201d<\/p>\n\n\n\n<p>What happens if you fail to supply the code as promised? Say you forget to put \u201cmytools.lib\u201d in your list of files for cl65. What will happen is that ld65 will get to the end of the list and discover that nothing ever said what this _helloworld symbol actually is. It won\u2019t be able to build the executable because it can\u2019t resolve that symbol. It will throw an error: \u201cUnresolved symbol _helloworld\u201d and quit. Unresolved symbol errors typically mean that either you forgot to put all of your project\u2019s required modules into the cl65 command\u2019s list of files, or else cl65 can\u2019t find the file \u2013 for instance let\u2019s say mytools.lib is not in this project\u2019s directory, but somewhere else. If you didn\u2019t specify a complete path to the library, cl65 won\u2019t be able to find it, and will throw an error \u2013 and that one\u2019s usually pretty easy to spot as it will say that it can\u2019t find file \u201cmytools.lib\u201d \u2013 copy it into the current directory, specify a full path to it (e.g. ..\/mylibs\/mytools.lib) or else add its directory to the build\u2019s search path using the -L command line option \u201c-L ..\/mylibs\u201d<\/p>\n\n\n\n<p><strong>Example code:<\/strong><\/p>\n\n\n\n<p>mytools.h:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>extern void helloworld();<\/code><\/pre>\n\n\n\n<p>myprog.c:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#include \"mytools.h\"\n\nint main() {\n  helloworld();\n  return 0;\n}<\/code><\/pre>\n\n\n\n<p>hello.asm:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.export _helloworld ; make this function available to C programs\n.export helloworld ; make this function available to other assembly programs\n\n.segment \"CODE\"\n\n_helloworld := helloworld ; make _helloworld be the same address as helloworld for C compatibility\n\nCHROUT := $FFD2 ; kernal routine to print a character to the screen\n\n.proc helloworld: near ; create a scope so local symbols are hidden from the rest of the project\n   ldx #0\nnextchar:\n   lda str_hello,x\n   beq done ; check for null character marking end-of-string\n   jsr CHROUT\n   inx\n   bra nextchar\ndone:\n   rts\n.endproc\n\n.segment \"RODATA\"\n; place the actual string data into the \"RODATA\" region of memory\n; this symbol is NOT exported, so programs will not be able to refer to it directly.\n; this module, though, knows the symbol since it's in the same file, so it can be\n; referenced here and printed out to the screen.\nstr_hello: .asciiz \"hello world!\"<\/code><\/pre>\n\n\n\n<p><strong>Building the library:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cl65 -t cx16 -c hello.asm\n\nca65 a mytools.lib hello.o<\/code><\/pre>\n\n\n\n<p><strong>Using the library:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cl65 -t cx16 -o MYPROG.PRG myprog.c mytoools.lib<\/code><\/pre>\n\n\n\n<p><strong>C-callable functions:<\/strong><\/p>\n\n\n\n<p>For information on how to write C-callable functions with parameters and return values in assembly, the reader can refer to\u00a0<a href=\"https:\/\/cc65.github.io\/doc\/cc65-intern.html\">https:\/\/cc65.github.io\/doc\/cc65-intern.html<\/a><\/p>\n\n\n\n<p><strong>Parameter passing \/ return values<\/strong>:<\/p>\n\n\n\n<p>cc65 has \u201cregular mode\u201d and \u201c__fastcall__ mode.\u201d For regular mode, C will push all parameters onto the stack (C\u2019s stack, not the CPU stack) in order from left to right and then jump into the subroutine. Fastcall is the same as regular, except that the rightmost parameter is passed in via the .A and .X registers for 8\/16 bit values, and for a 32bit parameter, it places the upper two bytes in a temporary ZP space called sreg. To access sreg, just .import sreg in your assembly file.<\/p>\n\n\n\n<p>Your assembly program can import SP which is a zeropage pointer to the current top of the stack. Use it as with any other ZP pointer \u2013 with (ZP),y for offsets into the stack. It is your code\u2019s responsibility to remove the parameters from the stack prior to returning. Otherwise, you will create a memory leak. You can accomplish this by either incrementing SP to point above the parameters passed in, or else using one of the various utility functions in cc65 such as popa, popax, etc. I currently prefer to use popa \/ popax as they include some code to determine whether the top-of-stack has an 8bit value that\u2019s been promoted to 16bit size, etc.<\/p>\n\n\n\n<p>Return values are similar \u2013 .AX returns the lower 16 bits of the return value. If returning an 8bit value, it is promoted to 16bit anyway, so you will need to have a zero in .X or you will get strange results, especially if returning a signed value. If the return value is 32bits, the upper two bytes are returned in sreg.<\/p>\n\n\n\n<p>Example:<\/p>\n\n\n\n<p>in C :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>extern char __fastcall__ max(char b, char a);\n<\/code><\/pre>\n\n\n\n<p>in Assembly:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.import popa\n.export _max\n\n.segment \"RSS\"\ntmp: .res 1 ; create TMP space for the char a parameter.\n\n.segment \"CODE\"\n_max:\n  STA tmp ; store parameter \"char a\" in tmp\n  JSR popa ; popa clobbers .Y but leaves .X alone\n\n  ; the stack has now been cleared of the passed-in parameters.\n  LDX #0 ; clear the upper 8 bits of the return value, as we are returning a char.\n  CMP tmp\n  BCS exit   ; .A >= tmp - return .A\n  LDA tmp  ; tmp > .A return tmp\nexit:\n  RTS<\/code><\/pre>\n\n\n\n<p>Of course this could be done by comparing directly with the parameter b still remaining on the stack and not using a temp location, but this example is meant to be more straightforward and illustrate the use of popa to clear the stack.<\/p>\n\n\n\n<p>If your library\u2019s routine is written primarily for use by assembly and has other calling conventions, then you can do both at the same time by way of the fact that C and assembly have different symbols.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.export foo\n.export _foo\n\nfoo:\n  ... ; main code for the routine - use whatever parameter passing methodology you want\n  rts\n\n_foo:\n  ... ; pop the C parameters off the stack and arrange them in whatever manner is required for assembly call to foo\n  jmp foo<\/code><\/pre>\n\n\n\n<p>Of course, if foo also returns values in a manner inconsistent with C\u2019s return mechanism (.AXsreg), you would not JMP into foo but JSR and then convert the return into C\u2019s format. Suppose foo returns .C set = fail, clear = success. You must store this bit in .AX in order to return that to the caller in C. Furthermore, you may wish to present foo() to C such that it returns true on success and false on error. Here is an example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>_foo:\n  ...\n  jsr foo ; returns .C set = fail, clear = success\n  ldx #0\n  lda #0\n  rol A ; move .C into bit0 of return value\n  eor #1 ; invert logic so true=success, false=fail\n  rts<\/code><\/pre>\n\n\n\n<p>I\u2019m still learning about this myself, so I don\u2019t have any examples yet for going the other way \u2013 calling a C routine from assembly, but the basis would be to place the arguments onto the stack from left to right and then JSR _function. (don\u2019t forget to .import _function) If the C function happens to be defined as a fastcall, you would pass the rightmost parameter with .AX and sreg as required.<\/p>\n\n\n\n<p>One final note \u2013 C handles preservation of the registers, so you do not need to worry about whether your code clobbers them or not. C assumes you\u2019ll be clobbering everything.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The text below is copied directly from the posts at\u00a0https:\/\/www.commanderx16.com\/forum\/viewtopic.php?p=25298. All credits should go to the respective authors, ZeroByte and Kliepatsch. Making libraries in CA65 is quite straightforward, but your&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-62","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/techblog.dansbo.dk\/index.php?rest_route=\/wp\/v2\/posts\/62","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/techblog.dansbo.dk\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/techblog.dansbo.dk\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/techblog.dansbo.dk\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/techblog.dansbo.dk\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=62"}],"version-history":[{"count":1,"href":"https:\/\/techblog.dansbo.dk\/index.php?rest_route=\/wp\/v2\/posts\/62\/revisions"}],"predecessor-version":[{"id":63,"href":"https:\/\/techblog.dansbo.dk\/index.php?rest_route=\/wp\/v2\/posts\/62\/revisions\/63"}],"wp:attachment":[{"href":"https:\/\/techblog.dansbo.dk\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=62"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/techblog.dansbo.dk\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=62"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/techblog.dansbo.dk\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=62"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}