Giter Club home page Giter Club logo

gvasm's People

Contributors

sean-ac avatar velipso avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

gvasm's Issues

Loads from pool literals can be optimized further in ARM

This:

ldr r5, =0x10101000

is currently translated to:

ldr r5, [#@@data]
@@data: .i32 0x10101000

But testing indicates that this is faster:

mov r5, #0x10000000
orr r5, #0x100000
orr r5, #0x1000

Likewise, a two instruction version is obviously faster too.

Some tests should be performed for thumb instructions too, but that's harder due to no shifted immediate.

Need a better way to load code into RAM

Now that we can allocate structs to IWRAM or EWRAM, it would be nice to have the equivalent for code:

.begin irqNone = iwram
  .arm
  mov   r0, #0x04000000
  ldr   ip, [r0, #0x200]!
  and   r2, ip, ip, lsr #16
  strh  r2, [r0, #2]
  ldr   r3, [r0, #-0x208]
  orr   r3, r3, r2
  str   r3, [r0, #-0x208]
  bx    lr
.end

This should expose:

.printf "%08X", irqNone       // the location in IWRAM
.printf "%08X", irqNone._rom  // the location in ROM
.printf "%d", irqNone._bytes  // the size of the code

So it is up to the user to memcpy the function from ROM to IWRAM, then they can bl irqNone.

Larger blocks should be supported, and layering too (eventually):

.begin iwramCode = iwram(-1) // all layers
  .include './saveCopy.gvasm'
  .include './sndFrame.gvasm'
  .embed './silence.gvsong'
.end
.begin iwramTemp = iwram(1) // layer 1 only
  .include './saveInit.gvasm'
  .include './sndInit.gvasm'
.end
.struct Player = iwram(2) // layer 2 only
  .i32 health
.end

This means Player will overlap iwramTemp. Also, you can copy all of iwramCode in one memcpy.

Then, iwramCode.saveCopy should point to RAM. Maybe there should be iwramCopy._rom.saveCopy too.

.push statement

I sometimes forget to pop what I pushed... why not let the assembler do it.

From:

push {r4-r6}
...
pop {r4-r6}

to:

.push {r4-r6}
  ...
.pop // automatically pop {r4-r6}

Another variation of this feature...

sub sp, #value
...
add sp, #value

to:

.subsp value
  ...
.addsp

Maybe just a .defer would be better?

.begin
  push {r4-r6}
  .defer pop {r4-r6}
  ...
.end // -> defers happen in reverse order

Apparently you can `mov r0, r1` in thumb without setting status

Word on discord is you can perform a mov in thumb on low registers without setting status. They claim it's undocumented.

I should verify this on real hardware and, assuming it's true, implement it in gvasm somehow.

Gericom: The mov opcode for hi registers (which doesn't update flags) works just fine for low registers on arm7tdmi, but since that's not officially defined behavior it won't be used in the compiler

should use cpy mnemonic

Somehow Watcher can throw if file is missing

[08:36:39] Error:
  src/lib/tilesOverlay.gvasm:5:1: Failed to import file: /Users/sean/Sync/2022/dev/tilegame2/src/lib/config.gvasm
[08:36:39] Watching 59 files
NotFound: No path was found. about ["/Users/sean/Sync/2022/dev/tilegame2/src/lib/config.gvasm"]
    at new FsWatcher (deno:runtime/js/40_fs_events.js:19:23)
    at Object.watchFs (deno:runtime/js/40_fs_events.js:64:12)
    at Watcher.watch (https://raw.githubusercontent.com/velipso/gvasm/main/src/watcher.ts:59:26)
    at https://raw.githubusercontent.com/velipso/gvasm/main/src/make.ts:104:15
    at makeFromFile (https://raw.githubusercontent.com/velipso/gvasm/main/src/make.ts:54:29)
    at async makeResult (https://raw.githubusercontent.com/velipso/gvasm/main/src/make.ts:75:3)
    at async make (https://raw.githubusercontent.com/velipso/gvasm/main/src/make.ts:157:5)
    at async main (https://raw.githubusercontent.com/velipso/gvasm/main/src/main.ts:388:12)
    at async https://raw.githubusercontent.com/velipso/gvasm/main/gvasm.ts:10:11
Unknown fatal error

Compile-time resource management

This idea isn't fully fleshed out, just recording it for my own posterity.

I would like for people to be able to do something like:

gvasm install 'https://github.com/example/test/someLibrary.json'

This would download the library, and any dependencies, perhaps in a lib/ directory.

Then, the main code could somehow include it via:

.import '~/lib/someLibrary/main.gvasm' { main }

or something like that, where ~ always resolves to the root of the make.

Ok -- that would be somewhat easy to create, but the issue is that how would the code actually work together with user code?

If the library just needs memory, then that can be solved by having the user pass pre-allocated memory to the library. But what if the library needs a timer? Or to install an IRQ handler?

It seems the fundamental issue is that all the code needs to use shared resources intelligently. And it somehow needs to communicate those needs. Memory is a shared resource, but so are timers, IRQ handlers, etc.

So... it would be great if the library could somehow say:

Hey, I need a timer (don't care which one), and I need it to fire an interrupt, and here is my interrupt handler when that happens.

Typically, you would expect the library documentation to say those things, and it would be up to the programmer to handle setup correctly. But that seems strange -- after all, we don't expect a C library to tell us how to place globals into memory. We expect the compiler to figure that out. And we don't expect a TypeScript library to tell us how to perform dynamic memory allocation. Again, that's handled by the environment automatically.

So could we solve this idea generally? We have a shared resource, and want to coordinate usage across different libraries.

Another issue is that the resource usage changes over run-time. Perhaps we only want a timer used for a portion of run-time. How can we verify at compile-time that the library has a timer allocated to it?

So when it comes to static memory, there is at least some prior art that I know about, called overlays.


So how could this ultimately work in gvasm?

One thought is to have a sink script that registers itself as managing a particular resource at compile-time. Something like gvlib-timers.sink:

var rm = gvasm.registerResourceManager 'timer'
do
  var {kind, data} = gvasm.nextEvent {rm}
while kind != gvasm.event.DONE
  if kind == gvasm.event.REQUEST
    // ?
  elseif kind == gvasm.event.FINISH
    // ?
  end
end

Then you would:

gvasm make -r src/gvlib-timers.sink src/main.gvasm

Now, programs can request access to the 'timer' resource.

.request timer 'timer'
  .func install
    .thumb
    ldr    r0, =timer.REG
    // ...
    bx    lr
    .pool
  .end

  .func uninstall
    // ...
  .end
.end

And somehow timer.REG needs to be specified by the gvlib-timers.sink script file.

Next, the user of the install function needs to somehow tell the compiler when the timer is in use at run-time.

So maybe something like:

.import './timer.gvasm' { install, uninstall }

.begin main
    .thumb
    .reserve 'timer'
      bl    install
      // timer is in use here!
      bl    uninstall
    .end
.end

I don't know, something like that :-/

Allow .align to use nops

.align 4, nop

should insert mode-equivalent nop instructions until alignment

needed for veneers for switching mode

.thumb
// thumb code
.align 4, nop
bx pc
.arm
// arm code

Function wrappers

Would be cool to have something like:

.tfunc someName a, b, c, d // thumb
  ...
  .return a, b
.end

.afunc someName a, b, c, d // arm
  ...
  .return
.end

which does a few things...

  1. Maps registers to .regs a, b, c, d, r4-r11
  2. Switches to ARM/Thumb depending on function type
  3. Tracks which registers of r4-r11,lr are used inside the body and pushes them automatically
  4. Upon .return <reserved regs>, it pops the registers and lr

Alternative syntax:

.func someName a, b, c, d
  // stack variables
  .i32 one
  .i32 two
  .thumb // triggers end of stack variables
  ...code...
  ldrx r0, [sp, #one]
  strx r0, [sp, #one]
  ...code...
  .return r0
.end

Assert statement

Since .if is very restrictive, we need an .assert statement:

// could refuse to compile due to unknown values
.if foo != bar
  .error "foo isn't bar!"
.end

// could be scheduled to be asserted instead
.assert "foo should be bar", foo == bar

Register allocator..?

Wondering if it would be nice if there was a register allocator algorithm.

It could look something like:

.fthumb @lerp ?a, ?b, ?t
  subs  ?b, ?a
  muls  ?b, ?t
  lsrs  ?b, #8 
  adds  ?a, ?b
  .ret  ?a
.end
.call @lerp 1, 2, 3

Static memory allocation should support layering somehow

.struct Player = iwram(-1) // all layers
  // ...
.end

Something like iwram(v) where v is a 32-bit number that says whether the struct should be allocated to that layer.

So that means there are 32 layers to work with. iwram(-1) means all layers due to all bits being on.

This complicates the allocation algorithm and could leave gaps, depending on the layering.

Maybe the syntax should be nicer though, like ranges:

.struct Player = iwram(0-4, 8, 10)
  // ...
.end

Though would still limit it 0-31, and default to all if no range provided.

strx/ldrx should work with sp

These should work in Thumb:

strx r0, [sp, #S.foo]
strx r0, [sp] (S.foo)
ldrx r0, [sp, #S.foo]
ldrx r0, [sp] (S.foo)

Base doesn't track dependencies correctly

Something like this:

// file1.gvasm
.def start = 8
// file2.gvasm
.import './file1.gvasm' { start }
.base start
.i32 _here

Changing .def start = 16 should change file2.gvasm.

Structs should have optional types with type checking

Something like:

.struct $O
  .uint scriptPC
  .short originX
  .short originY
.end

.struct $G = 0x03000000
  .short somethingElse
.end

Then, this should generate a warning:

ldr r0, [base, #$O.originX]
str r0, [base, #$O.originX]

// or, much harder
ldr r1, =$G.somethingElse
ldr r0, [r1]
str r0, [r1]

Because I'm using ldr instead of ldrh.

Macro idea

What if macros were just:

.script
  export foo = {
    { 'temp:reg', 'shift:int', 'save:regrange' }, // parameters
    ` // body
    ldr   $temp, =123
    lsls  $temp, #$shift
    push  $save
    `
  }
.end

// invoked via:
movs  r1, #23
%foo  r0, 10, {r0-r3}

Support compression algorithms

Would be nice to do something like:

.script
  i8 pack.huff pack.lz77 {1, 2, 3, 4, 5}
.end

and also:

.pack huff
  .pack lz77
    .i8 1, 2, 3, 4
    .embed 'file.bin'
  .end
.end

BIOS compression algorithms, according to GBATEK:

  • BitUnPack bit
  • Diff8bitUnFilter diff
  • HuffUnComp huff
  • LZ77UnComp lz77
  • RLUnComp rle

Also, maybe a best option, which would try all of them, and pick whichever is smallest -- decompression works via checking the header.

see: https://github.com/devkitPro/grit/tree/master/libgrit

gvasm v3 ideas

The big feature for v3 should be a system for distributing libraries (sharing code).

I should be able to pull in a music library, or a saving library, and everything should be able to work easily.

One big element of that is iwram / ewram allocation. Specifically:

  1. Structs should be able to allocate static memory, with layering.
  2. Code blocks should be able to say they belong in memory, with layering. This might mean getting rid of .base entirely.

I would also like to have bug-free dependency management. That could mean a few specific things:

  1. Deterministic random numbers. Scripts should have a default random seed, not based on the timer.
  2. Pools might have to be flushed at the end of every file? or every module?
  3. Pools will probably have to allocate space prior to knowing the values stored in the pool, which means there could be some small wasted space due to duplicate values.

Looking at the Inky source code, only main.gvasm has .include statements. This might need to be required going forward. Specifically, I'm imagining everything is a "library" except the main file. The main file has global config, GBA header, and include order. Then, all libraries can access the global config, or something like that.

So maybe main cannot import, and can only include. Kind of like main is package.json, and wires up packages and configuration, along with the ability to output some code so that short single-file ROMs can be made.

Maybe .gvmain (entry point) and .gvasm (libraries) or something.

// ./inky.gvmain

.begin header
  .arm
  b main
  ...
  main:
.end

.lib '@src'
  .include './src'
.end

.lib '@mem-1'
  .include './lib/mem-1.0'
.end

.lib '@mem-2'
  .include './lib/mem-2.0'
.end

.lib '@gvsong'
  .def MAX_CHANNEL = 6
  .def MAX_SFX = 3
  .maplib '@mem' = '@mem-1'
  .include './lib/gvsong-1.0'
.end

.lib '@gvsave'
  .maplib '@mem' = '@mem-2'
  .include './lib/gvsave-1.0'
.end

// ./lib/gvsong-1.0/gvsong.gvasm

.dep '@another'
  .repo 'https://github.com/velipso/another'
  .tag 'branch, tag, or hash'
  // this gets constructed into a git submodule command:
  //   git submodule add -b <tag> <repo> lib/<repo_name>-<tag>
.end

.import './relative/import.gvasm' { asdf }
.import '@/this/library/root.gvasm' { asdf }
.import '@another/library/import.gvasm' { asfd }

This doesn't seem entirely correct... need to think through what it means when a library depends on another library, and how to wire up those versions correctly.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.