Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port to ASM6 #9

Closed
pinobatch opened this Issue Sep 8, 2016 · 9 comments

Comments

Projects
None yet
3 participants
@pinobatch
Copy link
Owner

pinobatch commented Sep 8, 2016

Pently is made for ca65. But loopy wrote in the README file for his ASM6 assembler that ca65 "took too much work to set up", and several users of NESdev BBS agree.

In a forum post about MML processors, Pokun considers a requirement of ca65 to be a disadvantage for NSDL. In a forum post about music engines used by a ROM hacker turned original game programmer, dougeff expressed a bit of frustration about upstream (us) being ahead of his port to ASM6.

So consider ways to accommodate ASM6 users without having to Write Everything Twice (WET). This may involve a preprocessor of some sort to turn ca65 assembly source code to something ASM6 accepts. (I wrote a similar preprocessor for NESASM years ago as part of Action 53.) Steps I know I'll need to take:

  • Assembler directives need not have a leading dot.
  • equ replaces =; = instead replaces .set
  • db or byte replaces .byt
  • dw or word replaces .addr
  • dsb replaces .res
  • rept (with no second argument) replaces .repeat; emulate the second argument with =.
  • rept 1 replaces .scope
  • No .bss; emulate it with .enum and = $. May need host to allocate both zero page and BSS memory.
  • No other .segment; treat code and read-only data the same.
  • No .proc; emulate it by either prefixing the namespace to non-@ labels or perhaps wrapping the body in a .rept 1.
  • No .assert; emulate it with if.
  • No second argument to .assert either, but it can be emulated with if.
  • Unnamed labels follow the x816 convention (-, --, +foo), not the ca65 convention (:). Change them to @-labels in the source code.
@Qix-

This comment has been minimized.

Copy link
Collaborator

Qix- commented Sep 8, 2016

I like ASM6 already. Do we know the percentage of users currently using ASM6 vs. CA65?

@pinobatch pinobatch added the driver label Jan 14, 2018

@darkhog

This comment has been minimized.

Copy link

darkhog commented Oct 20, 2018

Actually, dots before directives like byte in ASM6 are optional, but legal. So you don't have to remove them. I'm using them for consistency & readability anyway. This would simplify the converter.

@pinobatch

This comment has been minimized.

Copy link
Owner Author

pinobatch commented Jan 17, 2019

100 percent of NESmaker users are using ASM6

@pinobatch

This comment has been minimized.

Copy link
Owner Author

pinobatch commented Jan 17, 2019

I may not want to bother making the tool translate the scope resolution operator, which allows referring to labels inside a different .proc. If we use rept 1 to replace .scope, we can't do this. Fortunately, most of the over 80 uses of scope resolution are .if :: to force use of the top-level scope as a workaround for cc65 issue #796 in order to prove to ca65 that a constant isn't redefined later in the same scope. For the others, break out a separate .proc with a fallthrough.

  • pently_resume_music::have_music_playing becomes pently_set_music_playing
  • skip_to_row_top::bottom becomes pently_skip_to_row
  • pently_play_note::skipAttackPart becomes pently_play_note_rts
  • sustain_phase::silenced becomes silence_out_volume

Anonymous labels should be easier to translate automatically.

  • At the start of translation, set a counter to 0.
  • When a line's label is :, increase the counter by 1 and then emit a label of the form @ca65toasm6_anonlabel_1:.
  • Replace :+ in an expression with the next @ca65toasm6_anonlabel_{n+1}.
  • Replace :- in an expression with the next @ca65toasm6_anonlabel_{n}.

pinobatch added a commit that referenced this issue Jan 18, 2019

remove references to proc-local labels
Issue #9 concerns translating Pently to ASM6.  Unlike named scopes
in ca65, which allow other scopes to reach in and see their symbols
using the scope resolution operator x::y, ASM6 scopes defined using
rept 1 lack a name.  So instead, use fallthrough to promote these
four inner labels to top-level labels.

pinobatch added a commit that referenced this issue Jan 18, 2019

@pinobatch

This comment has been minimized.

Copy link
Owner Author

pinobatch commented Mar 6, 2019

As of March 2019, Pokun still rejects NSDL because of ca65.

pinobatch added a commit that referenced this issue Mar 16, 2019

@pinobatch

This comment has been minimized.

Copy link
Owner Author

pinobatch commented Mar 16, 2019

And right now I'm stuck on what to use to replace .ifblank and .ifnblank in a couple macros in pentlyseq.inc

pinobatch added a commit that referenced this issue Mar 18, 2019

get something to build in ASM6
I got green screen of death to build successfully in ASM6.
Now for something nontrivial before I can proceed on #9

pinobatch added a commit that referenced this issue Mar 30, 2019

pentlyas: always emit both drumdef arguments
ASM6 does not support variadic macros.  Begin changes to pentlyas
to work around that for #9.

pinobatch added a commit that referenced this issue Mar 31, 2019

asm6: collect all SEGMENTs, not just zp/bss
Turns out we need assignments within the corresponding ifdef, not
pulled out into a separate section.

for #9

pinobatch added a commit that referenced this issue Mar 31, 2019

delay some labels to avoid "Incomplete expression"
The following lines in ASM6 trigger the error "Incomplete expression"
because the = command occurs before the label to which it refers.

; 66
PENTLYZP_SIZE = 32
musicPatternPos = pently_zp_state + 2
enum $00D0
pently_zp_state: dsb PENTLYZP_SIZE
ende
lda (musicPatternPos,x)
; 99

So move = commands referring to labels that appear in zero page or
BSS below the definitions of those labels.

for #9

pinobatch added a commit that referenced this issue Mar 31, 2019

@pinobatch

This comment has been minimized.

Copy link
Owner Author

pinobatch commented Mar 31, 2019

Right now I'm stuck on trying to get pentlyseq.inc macros like sfxdef to work in ASM6. Here's an MCVE for what I'm seeing, heavily reduced from the definition of sfxdef. It gives an Illegal instruction error in ASM6, but no error if I remove the include guard or remove the use of addy within the macro.

; include guard mcve
; changing dw addy to dw 0 causes no error
; removing the ifndef/endif causes no error
ifndef INCLUDE_GUARD
INCLUDE_GUARD = 1
macro hello addy
dw addy
endm
endif
base $C000
hi:
hello data1
hello data2
data1:
data2:

pinobatch added a commit that referenced this issue Apr 1, 2019

remove .pushseg/.popseg
The way I'm modeling ca65 segments in the ASM6 converter (#9)
doesn't work with changing segment within a scope.  So remove
the only use of .pushseg/.popseg in the driver.

pinobatch added a commit that referenced this issue Apr 1, 2019

pinobatch added a commit that referenced this issue Apr 1, 2019

ASM6 translation builds (yay)
Now that we predefine names defined in macros, the ASM6 translation
(#9) builds (thanks overkill/egg boy color) but entire macro calls
are still getting skipped

pinobatch added a commit that referenced this issue Apr 2, 2019

sort arpInterval in pentlybss.py
For various reasons, some fields of the music state must appear
before others in memory.  One example is arpInterval1 before
arpInterval2 because of how indexed mode for arpeggio works.
Since the introduction of pentlybss.py (then called mkrammap.py)
in #22, pentlymusic.py has been responsible for sorting these
pairs.  But this introduces problematic forward references into
the ASM6 translation (#9).  So instead, sort them in pentlybss.py.

pinobatch added a commit that referenced this issue Apr 2, 2019

ASM6 plays something resembling music
Among demo songs, Twinkle and Individual features play correctly.
Others make it launch into spasms.  Attack injection comes close,
though it pauses at certain play commands and the first note repeats.

But at least we have audible progress on #9

pinobatch added a commit that referenced this issue Apr 2, 2019

fix subseq_pool and pently_zp_state forward refs
ASM6 appears to have bugs.  Avoiding them gets me that much
closer to #9, as all ten songs are playing correctly.

- Forward references to instrument attack data in the subsequence
  pool were confusing ASM6.  Move them after the pool itself.
- Music has different zero page layouts for attack phase or not.
  The delay_labels mechanism, which reduces forward references to
  pentlymusicbase members, was pulling ZP definitions out of the
  .if block that controls them, causing the ZP layout to be wrong.
  For example, songs would stop prematurely when the playing flag
  got overwritten.

pinobatch added a commit that referenced this issue Apr 2, 2019

avoid NESASM/ASM6F reserved words
ASM6F turns the NESASM names for iNES header fields into reserved
words.  Avoid them.

For #9
@pinobatch

This comment has been minimized.

Copy link
Owner Author

pinobatch commented Apr 2, 2019

Good news: 63ff9ae got all 10 sample songs playing. The next steps I can see:

  • Preprocess the code and data separately, possibly involving changes to pentlyas.py
  • Ask how ASM6 users expect libraries to allocate RAM
  • Ask what sort of build system ASM6 users use
  • Package the preprocessed code
@pinobatch

This comment has been minimized.

Copy link
Owner Author

pinobatch commented Apr 6, 2019

Answers to some questions have arrived:

  • Members of the NESdev Discord server recommended following the example of GGSound, which is shipped as three files: a first to include in zero page, a second one to include in BSS, and a third to include in code. The zero page and BSS files are supposed to dsb all bytes used in the library. pentlybss.py could be given this responsibility.
  • Discussion in NESdev BBS showed that ASM6 users expect to break large build processes that involve time-consuming asset conversion into two build processes, one run after the other. The first converts assets; the second assembles code. The process to build Pently would run during the second.

pinobatch added a commit that referenced this issue Apr 7, 2019

pentlyas: add ASM6 mode; make include relative
ASM6 mode in pentlyas.py makes these changes to output:

- Omit .export
- Omit .segment
- Omit .include
- Create labels for each object's asmname with "= 0"

While I was at it, because I run the ca65 and ASM6 build processes
from different directories:

- pentlyas.py: include file's path is relative to including file's
  directory, not current working directory

For #9

pinobatch added a commit that referenced this issue Apr 7, 2019

pentlybss: add ASM6 mode
ASM6 mode adds dsb statements for pentlymusicbase, as well as all
statically allocated ZP and BSS variables, to be included within
an enum...ende block in the Application.

Caveat: Only BSS, not zero page, is dynamic this way.  Until
there's a way to have pentlyzp.inc see pentlyconfig.inc, ZP uses
the largest of the three layouts (32 bytes), as opposed to the
16-byte Pently 1 layout without support for music or the 21-byte
Pently 3 layout without support for attack envelopes.

for #9

pinobatch added a commit that referenced this issue Apr 8, 2019

remove delay labels from ASM6 translator
Allocating all variables in a separate file simplifies label
handling in the translator.

for #9

pinobatch added a commit that referenced this issue Apr 8, 2019

ASM6: factor anonymous label counter to class
reduce top-level global state, moving toward command-line
arguments for #9

pinobatch added a commit that referenced this issue Apr 8, 2019

move ASM6 translation into a function
Removing mutable globals allows multiple jobs, whether within
a script or from the command line.

for #9

@pinobatch pinobatch closed this in 51a0514 Apr 8, 2019

pinobatch added a commit that referenced this issue Apr 11, 2019

have pentlyzp.inc include header files
Including the macro and constant definitions from the zero page
definition file makes order of the driver and data not matter, so
long as they're both below pentlyzp.inc.

Fine-tuning the approach for #9 before I package it
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.