X816 Assembler > YMAS

Discussion of hardware and software development for Super NES and Super Famicom. See the SNESdev wiki for more information.

Moderator: Moderators

Forum rules
  • For making cartridges of your Super NES games, see Reproduction.
noyen1973
Posts: 32
Joined: Sat Oct 06, 2018 10:15 am

X816 Assembler > YMAS

Post by noyen1973 »

Since I no longer had the original X816 assembler source code I began rebuilding a new 65816 assembler a few months ago using Free Pascal but I've gotten to the point where it needs some real world testing and suggestions. The archive contains only the executable and a readme which is the actual directive/opcode table embedded into the .exe. It's a brief rundown of all the current and some future planned directives and functions. Scroll further down the readme and you will see the opcode tables for 65816, 6502, SPC700 (both native and 65816-style), as well as a partial SuperFX implementation.

Here's a short example of the setup, with a couple of nested repeats and a final bin save.

Code: Select all

.snes lorom
.list 
.symbol
.verbose off
.repeat 3,@y
.repeat 10,@x
nop
.echox "current address:",@pc
.echox @x+@y*10
.endrepeat
.endrepeat
.snes savebin
How functionable is it? I've been able to convert some older simple source codes, including the Tricks Zoomer which I recoded into mode 21 and use some direct page addressing just for testing. Really only the 65816 side has been regularly tested. I cut/pasted some SPC700 instructions with deliberate errors to test but that's about it.

Code: Select all

   36 00008042                                               .cpu      spc700                  
.target cpu SPC700
   37 00000000                                               .base     $0000                   
   38 00000000 39                                            and       (x),(y)                 ;$39,1,0,
   39 00000001 28 34                                         and       a,#$1234                ;$28,2,4,
*** error #2 line 39 value out of range >255
   40 00000003 26                                            and       a,(x)                   ;$26,1,0,
   41 00000004 37 34                                         and       a,[$1234]+y             ;$37,2,1,
*** error #3 line 41 address out of range >255
   42 00000006 27 35                                         and       a,[$1234+1+x]           ;$27,2,1,
*** error #4 line 42 address out of range >255
   43 00000008 24 12                                         and       a,$12                   ;$24,2,1,
   44 0000000A 34 12                                         and       a,$12+x                 ;$34,2,1,
   45 0000000C 25 34 12                                      and       a,$1234                 ;$25,3,2,
   46 0000000F 35 35 12                                      and       a,$1234+1+x             ;$35,3,2,
   47 00000012 36 35 12                                      and       a,$1234+1+y             ;$36,3,2,
   48 00000015 38 34 34                                      and       $1234,#$1234            ;$38,3,33,
*** error #5 line 48 value out of range >255
   49 00000018 29 34 34                                      and       $1234,$1234             ;$29,3,32,
*** error #6 line 49 address out of range >255
   50 0000001B 6A 34 32                                      and1      c,/$1234.1              ;$6a,3,42,
   51 0000001E 4A 34 52                                      and1      c,$1234.2               ;$4a,3,42,
   52 00000021 88 21                                         adc       a,#$4321                
*** error #7 line 52 value out of range >255
   53 00000023 97 70                                         adc       a,[main&$ff]+y          
   54 00000025 0A 34 72                                      or1       c,$1234.3               
   55 00000028 3B 12                                         rol       $12+x                   
   56 0000002A 75 59 01                                      cmp       a,345+x                 
   57 0000002D 6F                                            ret                               
   58 00008042 [00002E]                                      .endb                             
   59 00008070                                                                                 
   60 00008070                                               .cpu      65816                   
.target cpu 65816
   61 00008070                             main:                                               
   62 00008070 4C 70 80                    -                 jmp       -                       
Last edited by noyen1973 on Thu Nov 14, 2019 1:30 pm, edited 3 times in total.
User avatar
Memblers
Site Admin
Posts: 4044
Joined: Mon Sep 20, 2004 6:04 am
Location: Indianapolis
Contact:

Re: X816 Assembler > YMAS 0.0180c

Post by Memblers »

I tried it on my old x816 project, only changing ".hirom on" to ".snes hirom". It makes it up until it hits an .incbin, then does this:

Code: Select all

An unhandled exception occurred at $0040EAD3:
EInOutError: Access denied
  $0040EAD3
  $0040A8F6
  $0041A2CA
  $0041B3B3
  $0041B793
If I comment it out, it goes further until it hits the next .incbin. The included files do exist in the same directory (and the source .include directive worked fine).
noyen1973
Posts: 32
Joined: Sat Oct 06, 2018 10:15 am

Re: X816 Assembler > YMAS 0.0180c

Post by noyen1973 »

Check the file attributes for your bin files, they might be set as read-only. I don't know why that should matter in Free Pascal as the file is only opened for reading and not writing. I've fixed the problem in my current version.
User avatar
Hamtaro126
Posts: 818
Joined: Thu Jan 19, 2006 5:08 pm

Re: X816 Assembler > YMAS 0.0180c

Post by Hamtaro126 »

I would like a way for using text with ASCII to use SNES's properties/attributes with it (i.e. .TW, also known as TEXTWORD), In other words, two ways to implement this is to:

1: add .StrAt(String) like in CA65, as I have a "TW" macro that uses that.

2: Look at the implementation for ASM16's version of TW, search for "ASM16" on GITHUB (NicklausW maintained it, but no longer updates it as far as I know.)
AKA SmilyMZX/AtariHacker.
noyen1973
Posts: 32
Joined: Sat Oct 06, 2018 10:15 am

Re: X816 Assembler > YMAS 0.0180c

Post by noyen1973 »

Are you referring to string handling functions like these?

Code: Select all

$2000,%11,$21,.strlen		; .strlen(string)
$2100,%11,$22,.leftstr		; .leftstr(string,index)
$2100,%11,$22,.left			; same as .leftstr
$2200,%11,$22,.rightstr		; .rightstr(string,index)
$2200,%11,$22,.right		; same as .rightstr
$2300,%11,$22,.midstr		; .midstr(string,index,length) -no length is to end=255
$2300,%11,$22,.mid			; same as .midstr
$2300,%11,$22,.copy			; same as .midstr
$2400,%11,$22,.delstr		; .delete(string,index,length) -no length is to end=255
$2400,%11,$22,.delete		; same as .delstr
$2500,%11,$21,.instr		; .instr(substring,string)
$2500,%11,$21,.pos			; .same as .instr
$2600,%11,$22,.trim			; .trim(string)
$3000,%11,$12,.inttostr		; .intostr(expr)
$3100,%11,$21,.strtoint		; .strtoint(string)
$3200,%11,$12,.inttohex		; .inttohex(expr)
$3300,%11,$21,.hextoint		; .hextoint(string)
That's a cut from the current version which has them all implemented. The version I put up had some of them commented out as they were on my todo list. I just tested them in a macro and it works. Prior to your message I had only tested them using .echox directive. Here's a sample of the string functions in a macro. I believe the function you are looking for is .instr(<substring>,<string>) which returns the position.
Here's the macro definition:

Code: Select all

.macro cowboy greeting
	.echox @nargs
      	nop
        .if "@1"="Howdy"
        .echo "greeting"
        .else
        .echo "I'm not a cowboy."
        .endif
        .if .instr("y","@1")=5
        .echox "'y' is the ",.instr("y","@1"),"th character"
        .endif
.endm
Here's the assembly listing:

Code: Select all

   25v00008000                                               .verbose  off                     
   26M00008000                                               cowboy    Howdy                   
.macro cowboy greeting(1)=Howdy(1)
   27 00008000                                               .echox    @nargs                  
             1
   28 00008000 EA                                            nop                               
   29T00008001                                               .if       "Howdy"="Howdy"         
   30 00008001                                               .echo     "Howdy"                 
             Howdy
   31 00008001                                               .else                             
   33 00008001                                               .endif                            
   34T00008001                                               .if       .instr("y","Howdy")=5   
   35 00008001                                               .echox    "'y' is the ",.instr("y","Howdy"),"th character"
             'y' is the 5th character
   36 00008001                                               .endif                            
   37M00008001                                               cowboy    Hello                   
.macro cowboy greeting(1)=Hello(1)
   38 00008001                                               .echox    @nargs                  
             1
   39 00008001 EA                                            nop                               
   40F00008002                                               .if       "Hello"="Howdy"         
   42 00008002                                               .else                             
   43 00008002                                               .echo     "I'm not a cowboy."     
             I'm not a cowboy.
   44 00008002                                               .endif                            
   45F00008002                                               .if       .instr("y","Hello")=5   
   47 00008002                                               .endif                            
   48V00008002                                               .verbose  on                      
.verbose reporting on
Hamtaro126 wrote:I would like a way for using text with ASCII to use SNES's properties/attributes with it (i.e. .TW, also known as TEXTWORD), In other words, two ways to implement this is to:

1: add .StrAt(String) like in CA65, as I have a "TW" macro that uses that.

2: Look at the implementation for ASM16's version of TW, search for "ASM16" on GITHUB (NicklausW maintained it, but no longer updates it as far as I know.)
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: X816 Assembler > YMAS 0.0180c

Post by koitsu »

I can't figure out what Hamtaro is actually asking for. Another case where being terse is not helpful... :\

asm16 is a fork of asm6/asm6f (6502 assembler) but intended for 65816. It comes with no documentation, no syntax details, no examples (the stuff in test/ does not actually use TW). which to me immediately says "throw this out and do not use it ever". I had to refer to commits and source code to figure out what this TW directive actually did:

https://github.com/nicklausw/asm16/comm ... 82355f4df0

Without any examples, and with virtually no source code comments, I can't figure out the exact syntax or behaviour of this psuedo-op. It looks like it might be something like .TW $1234,"some string" but I don't know for sure. The changelog and top-of-function comment says that it parses a string a byte at a time and emits a word/16-bit unsigned value based upon character | $1234 (using my example) for each byte. E.g. .TW $1234,"Hello" would emit five (5) 16-bit words total, of $48 | $1234 (H), $65 | $1234 (e), etc... ? Is this actually what it does?

As for ca65's .strat(), that looks to be completely different. That, to me, looks like a way to get the raw byte/value from a string (or any part of a generated label/ROM) based upon an index/offset, starting at 0. In other words:

Code: Select all

input: .byte "Hello World"

.strat(input, 0) == would return "H"
.strat(input, 1) == would return "e"
.strat(input, 2) == would return "l"
.strat(input, 10) == would return "d"
To me this looks like .midstr in YMAS, with a length of 1.

You could therefore simulate TW by using a macro with .repeat and .strlen(), though I dunno if .midstr and the like start at 0 or 1 for offsets (I suspect 1 given that YMAS is in FreePascal).
User avatar
nicklausw
Posts: 376
Joined: Sat Jan 03, 2015 5:58 pm
Location: ...
Contact:

Re: X816 Assembler > YMAS 0.0180c

Post by nicklausw »

koitsu wrote:asm16 is a fork of asm6/asm6f (6502 assembler) but intended for 65816. It comes with no documentation, no syntax details, no examples (the stuff in test/ does not actually use TW). which to me immediately says "throw this out and do not use it ever".
Good thinking, as much as I hate to say it. asm16 was just another "I'm never satisfied with existing assemblers" project that even once it was working, I didn't actually care for that much; took more to byuu's bass, which I've stuck with. Also the test program...just...no. Golden example of throwing code around without knowing what it does.

The TW directive takes one word and one string, and each character of the string is OR'd with the word to output two new bytes.

Code: Select all

.TW $FF00,"HIYA"
HIYA is 48 49 59 41, so the output is FF 48 FF 49 FF 59 FF 41.

It's freaky, I know, but asm6 has always allowed some arithmetic on strings ("HIYA"-1 outputs 47 48 58 40) so this didn't seem too far out there. Hopefully a better solution can be found in some other assembler (most likely ca65) so my one loyal asm16 user can move on to better, much more well-maintained things.
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: X816 Assembler > YMAS 0.0180c

Post by koitsu »

Re: .incbin-related "Access denied" error: I wonder if this relates to incbin'd file size (or file offset, ex. reading past EOF), referencing the same file multiple times and stomping over a filehandle (?), or bank padding somehow. Windows' "access denied" error is not always about file permissions.
noyen1973
Posts: 32
Joined: Sat Oct 06, 2018 10:15 am

Re: X816 Assembler > YMAS 0.0180c

Post by noyen1973 »

koitsu wrote:Re: .incbin-related "Access denied" error: I wonder if this relates to incbin'd file size (or file offset, ex. reading past EOF), referencing the same file multiple times and stomping over a filehandle (?), or bank padding somehow. Windows' "access denied" error is not always about file permissions.
The archive has been updated to version 0.0181e which has a rewrite of .incbin to use a filestream rather than directly opening a file. I couldn't recreate the access denied error with the changes but that may not be the same for others... Needs testing.

More of the string functions have also been implemented. However I have noticed the parser doesn't do well when combining multiple functions. The opcode matching was also adjusted because extra spaces in the operand field confused the decoding of addressing modes and still does. For example LDA ($32,X ) doesn't get recognized because of the extra space between the 'X' and ')'. This was apparent after trying to assemble the spc700 ipl rom from SPCTECH by mukunda.

At the moment I'm cleaning up the source code and trying to improve the internals. Catching up on 20+ years of assembler evolution is not easy.
Pokun
Posts: 2681
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: X816 Assembler > YMAS 0.0180c

Post by Pokun »

Could I ask for a bit of background to this? This is a new version of this? It seems to be modelled of the C64/128's native assembler.
The current state of the readme file does not explain much else but different directives, but I see that there are SNES-specific directives. Is this assembler able to do general 65816 stuff as well, or is it SNES only? I guess the 6502 must be generic at least.

I've been hoping for a good 65816 and SPC700 assembler that works on modern computers, and this looks promising.


BTW asm16 isn't that bad since it can do everything asm6 can do. It has a few new (undocumented) features, but it was never completed. I don't think the direct page directives was ever added.
I personally can't stand the non-standard syntax in bass, and stopped using it. I rather use ca65 or even asm16.
noyen1973
Posts: 32
Joined: Sat Oct 06, 2018 10:15 am

Re: X816 Assembler > YMAS 0.0180c

Post by noyen1973 »

Pokun wrote:Could I ask for a bit of background to this? This is a new version of this? It seems to be modelled of the C64/128's native assembler.
The current state of the readme file does not explain much else but different directives, but I see that there are SNES-specific directives. Is this assembler able to do general 65816 stuff as well, or is it SNES only? I guess the 6502 must be generic at least.

I've been hoping for a good 65816 and SPC700 assembler that works on modern computers, and this looks promising.


BTW asm16 isn't that bad since it can do everything asm6 can do. It has a few new (undocumented) features, but it was never completed. I don't think the direct page directives was ever added.
I personally can't stand the non-standard syntax in bass, and stopped using it. I rather use ca65 or even asm16.
I wrote X816 with the help of koitsu over 20 years ago and have recently rediscovered the SNES homebrew scene. X816 does not run under the modern Windows environment without using DOSBOX, so I thought I'd try writing a new assembler based on it. Unlike my older assembler attempts by the name of Snesasm and Tricksasm which I shared publicly, the X816 source code was never shared and is long lost. YMAS is my attempt at rewriting a SNES assembler and more. The core of the assembler works much like Table Assembler so without too much hard coding I can add in instruction sets for other CPUs. YMAS is far from being complete and remains largely untested aside from the 65816 end of things but I am actively working on it in.

Some other assemblers you might want to consider are ASAR or WLA-DX.
Pokun
Posts: 2681
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: X816 Assembler > YMAS 0.0180c

Post by Pokun »

I see, thanks for the explanation.

I meant to ask if it forces things like headers and ROM size for you or if you have control of it yourself. So that it could be used for non-SNES 65816 devices as well for example I mean.
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: X816 Assembler > YMAS 0.0180c

Post by koitsu »

It depends on the platform.

NES/NES 2.0 header support is planned but as usual, a complicated mess. For now you're better off just making the 16-byte header yourself through .db directives or the classic copy /b header.bin+build.bin > build.nes model.

SNES cartridge header support -- the "old style" header -- is provided through .snes. Please refer to the documentation as such:

Code: Select all

$0080,0,$00,.snes @*
;.snes <function>
;Once the .snes directive is used the assembler will assign the target
;platform to Super NES and list the header information.  Changing the
;MODE/TYPE in the header will also change the MAPMODE and ROMSPEED and
;vice versa as they are both part of MODE/TYPE.  Using lorom, hirom,
;slowrom or fastrom will change the proper header information and set
;the start address to either $008000, $808000 or $c00000 to match the
;mode.  This is best done at the beginning of your source code.  If your
;source code only represents a fragment to be injected into another file
;it is not necessary to use those .snes directives as their primary use
;is for a large project.
;
;   lorom    Sets header info for mode 20, default "slow", see below.
;     "lorom slow", mode 20 32kB, romspeed=slow, org $008000
;     "lorom fast", mode 20 32kB, romspeed=fast, org $808000
;   hirom    Sets header info for mode 21 64kB, romspeed=fast, org $c00000
;   savebin  Saves a binary file based on map mode and rom speed, or the
;            lowest and highest address.
;   list     Lists the cartridge header details

; ($ffc0)	TITLE=<21 bytes>
; ($ffd5)	MODE=<byte> same as ROMSPEED/MAPMODE
; ($ffd5)	TYPE=<byte> same as ROMSPEED/MAPMODE
; ($ffd5.4)	ROMSPEED=<slow/fast>
; ($ffd5.0-3)MAPMODE=<value 0-15>
; ($ffd6)	CHIPSET=<byte>
; ($ffd7)	ROMSIZE=<byte>
; ($ffd8)	RAMSIZE=<byte>
; ($ffd9)	COUNTRY=<byte>
; ($ffda)	DEVELOPER=<byte>
; ($ffdb)	VERSION=<byte>
; ($ffdc/d)	CHECKSUMX=<word or CALC/AUTO/-1>
; ($ffde/f)	CHECKSUM=<word or CALC/AUTO/-1>
; SNES header information taken from "Fullsnes - Nocash SNES Specs".
; *** Does not calculate the checksum/checksumx yet
Real world example:

Code: Select all

;
; Start of file
;
    .cpu 65816

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Cartridge header ($FFC0-FFDF)                                           ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    .snes lorom           ; Mode 20
    .snes slowrom         ; 2.68MHz
    .snes title="(C) 1993 Norman Yen"
    .snes chipset=$00     ; ROM only
    .snes romsize=$08     ; 1-2mbit
    .snes ramsize=$00     ; No SRAM
    .snes country=$01     ; North America
    .snes developer=$FF   ; Undefined
    .snes version=$00
    .snes checksumx=AUTO
    .snes checksum=AUTO

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Vectors ($FFE4-FFFF)                                                    ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; All your .irq statements go here
;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; General equates                                                         ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; All your equates, variables, etc. go here
;



    .org $008000   ; Or whatever address

;
; ... all your code, data, etc. goes here
;


;
; Save resulting ROM; this must be put at the end of the file!
;
    .snes savebin "build.sfc"   ; Save resulting ROM
Since checksum/complement generation isn't implemented yet, you can use ucon64 to generate them for you. I use this in my build script: ucon64.exe -q --nbak --snes --chk build.sfc

If you want the "new style" SNES cartridge header, you can add it yourself manually via .db/.dw statements at the appropriate offset using .org/.pad etc... You should probably not try to use both methods together (i.e. .snes combined with .db/.dw for $FFB0-FFBF) but do whatever you wish. It's a classic assembler: it does what you tell it.
Pokun
Posts: 2681
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: X816 Assembler > YMAS 0.0180c

Post by Pokun »

So I can do everything manually if I want with statements like db, org and pad. That's actually what I prefer. Checksum/complement calculation would be nice though, I'm currently also using ucon64 in my build script.
It's a classic assembler: it does what you tell it.
That's what I wanted to hear. :)
noyen1973
Posts: 32
Joined: Sat Oct 06, 2018 10:15 am

Re: X816 Assembler > YMAS 0.0180c

Post by noyen1973 »

Here's an update, v0.0181f.

Improved SPC700 assembly
- Tested with Asar's "arch-spc700.asm"
- Use .cpu spc700

Improved SuperFX assembly
- Tested with Asar's "arch-superfx.asm" or atleast a version of it as I've found different versions
- Use .cpu superfx

Implemented Checksum and ChecksumX autocalculation
- It's on by default with the first use of a .snes directive otherwise use both .snes checksumx=calc and .snes checksum=calc, and follow up with .snes savebin at the end of your source code

Fixed .byte "text"
- The length of string byte was mistakingly encoded into the bin
Last edited by noyen1973 on Thu Nov 14, 2019 1:31 pm, edited 1 time in total.
Post Reply