Jump to content
IGNORED

AS1600 quirks


GroovyBee

Recommended Posts

I thought I'd list the quirky things I find in AS1600 here.

 

For jzintv-1.0-beta3 :-

 

LISTING "off" doesn't quite do what I thought it would do. I'd expect the assembler to mute all output to the listing file after this point. However, it doesn't :(. The assembler still adds the hex values for any code or data produced.

 

The instruction "mvii #'0'-32, r0" isn't valid syntax for the reason explained here. Instead it must be replaced with "mvii #asc('0',0)-32, r0".

Link to comment
Share on other sites

I thought I'd list the quirky things I find in AS1600 here.

 

For jzintv-1.0-beta3 :-

 

LISTING "off" doesn't quite do what I thought it would do. I'd expect the assembler to mute all output to the listing file after this point. However, it doesn't :(. The assembler still adds the hex values for any code or data produced.

 

The instruction "mvii #'0'-32, r0" isn't valid syntax for the reason explained here. Instead it must be replaced with "mvii #asc('0',0)-32, r0".

 

 

GroovyBee: I have great news for you. I think I've fixed both issues. Wanna try the latest as1600? What OS are you on?

Link to comment
Share on other sites

GroovyBee: I have great news for you. I think I've fixed both issues. Wanna try the latest as1600? What OS are you on?

 

:cool: Yep! I'll give them a go. I'm on Windows XP

 

Okey dokey! I will get that built and posted shortly.

 

BTW, this version also has some new features which I think you'll find handy. Here's what I added to the documentation:

 

------------------------------------------------------------------------------
   label QSET expr     => "Quiet" SET
   label QEQU expr     => "Quiet" EQUate
------------------------------------------------------------------------------

These directives behave identically to SET/EQU, except that they mark
the symbol as "quiet."  A "quiet" symbol functions like an ordinary
symbol, but will not appear in AS1600's symbol table output.

This is useful inside macros to keep macro-internal symbols from
cluttering up the symbol table and listing output files.


------------------------------------------------------------------------------
   WMSG "string" => Warning message
   CMSG "string" => Comment message
   SMSG "string" => Status message
------------------------------------------------------------------------------

These three related directives write out messages of various forms:

-- WMSG writes out an assembler warning from "string"

-- CMSG writes out a comment message in the listing from "string"

-- SMSG writes out a status message in the listing and to stdout 
   from "string"


------------------------------------------------------------------------------
   Stringification operators:
       $( expr-list )      => Stringify expr-list
       $#( expr )          => Render expr as signed decimal string
       $$( expr )          => Render expr as a 4 character hex string
       $%( expr )          => Render expr as an 8 character hex string
------------------------------------------------------------------------------

These operators produce strings that are usable in most contexts 
that require or accept strings.  The only exceptions are INCLUDE,
ORG and LISTING directives.

The generic stringify operator $( ) produces a string from the lower
byte of each value in expr-list.  If one of the expressions in the
list is undefined or not computable, stringify will substitute in a 
question mark ('?').

The numeric stringify operators $#( ), $$( ), and $%( ) return a
string rendered either as a signed decimal value, 4 character hex
value or 8 character hex string.  If the value is undefined or
not computable, the stringify operator will return an appropriate
number of question marks.

NOTE:  The generic stringify operator $( ) assumes no character
translation is currently active, and will warn if it detects that
a translation table is currently active.  It warns due to the
potential for a subtle interaction between these two features.

Strings decay to expression lists as needed.  AS1600 applies
character translations when converting strings to expression lists.
The stringify operator takes expression lists and converts them back
into strings.  That makes complicated expressions such as this work:

SMSG $(" Game size:  $", $$(_SIZE), " (", $#(_SIZE), " decimal) words")

However, when a character translation table is active, the string
gets converted when decaying to an expression list.  When $( )
then converts the list back into a string, there is no way to do
a reverse mapping.

 

I also have an experimental "symbol array" feature in this version. Here's how I described it in an email to Arnauld previously (with some minor tweaks):

I just added the last of the nifty assembler features for now. First off, SET/EQU with an expression list will now initialize a symbol as an array. So, this example works now:

foo     SET     "Hello world!"

This will initialize foo[0] through foo[11] to the ASCII string indicated. It also sets "foo" to 11 to indicate that that's the highest initialized index.

 

Next up, I've added an array slice syntax. This allows you to request a set of consecutive array elements as an expression list. The syntax is "array[first_index, last_index]". Example:

       DECLE   foo[1,3]

In this example, that'll pull out "ell" from "Hello world!". The first_index can be after the last_index. This specifies a slice that goes backwards:

       DECLE   foo[3,1]

That gives you "lle" instead of "ell".

 

You can combine slices with SET. Check out this nifty example:

foo     SET     "Hello world!"
bar     SET     foo[foo, 0]

       DECLE   foo[0, foo]        ; Gives "Hello world!"
       DECLE   bar[0, bar]        ; Gives "!dlrow olleH"

For this last example, recall that the array name by itself gives the highest observed array index so far, although you can reset it. This means you need to be a little careful when assigning more than one string to an array, such as you might in a complex macro that you reuse:

foo     SET     "Hello world!"     ; foo = 11, foo[0,foo] = "Hello world!"
foo     SET     "Bob"              ; foo = 11, foo[0,foo] = "Boblo world!"

To avoid that silliness, you can reset the watermark whenever you like:

foo     SET     "Hello world!"     ; foo = 11, foo[0,foo] = "Hello world!"
foo     SET     0
foo     SET     "Bob"              ; foo = 3,  foo[0,foo] = "Bob"

Make sense?

 

I'm not 100% overloading "SET" like this is the right thing to do. Part of me wonders if I should instead add a new directive, "ASET" for "Array SET". It would at least make code a little clearer. At the same time, this does work just as I have it, and I'm not terribly inclined to change it. It feels a little Perl-like, doesn't it? If I were to change anything, I would make "SET exprlist" always reset the base label to the new array length, so that the "SET 0" in the most recent example isn't needed.

 

Oh, and did I mention that multidimensional arrays work? So, you can say "foo[x][y]" if you like. Also, array slices work on multidimensional arrays, but only on the last dimension. ie. "foo[x][a,b]" is OK, but "foo[a,b][y]" is not.

 

 

Also, you can assign to an array slice:

 

foo      SET     "Hello world!"
foo[0,4] SET     foo[4,0]            ; foo[0,foo] = "olleH world!"

 

 

Array references can be code labels. So, you can actually have macro-local labels now, after a fashion:

_add_if[0]  QSET    0

MACRO       skip_add_if cc, src, dst
           B%cc%   _add_if[_add_if + 1]
           ADDR    %src%, %dst%
_add_if[_add_if]:
ENDM

           skip_add_if OV,  R0, R1
           skip_add_if NOV, R1, R0

This correctly assembles as two short forward branches, as can be seen in the listing:

0x0                    _add_if[0]  QSET    0

                       MACRO       skip_add_if cc, src, dst
                                   B%cc%   _add_if[_add_if + 1]
                                   ADDR    %src%, %dst%
                       _add_if[_add_if]:
                       ENDM

                                   ORG     $5000

                       ;           skip_add_if OV,  R0, R1
5000 0202 0001                      BOV   _add_if[_add_if + 1]
5002 00c1                           ADDR    R0, R1
0x3                    _add_if[_add_if]:
                       ;           skip_add_if NOV, R1, R0
5003 020a 0001                      BNOV   _add_if[_add_if + 1]
5005 00c8                           ADDR    R1, R0
0x6                    _add_if[_add_if]:
ERROR SUMMARY - ERRORS DETECTED 0
              -  WARNINGS       0

If you really wanted to know all the addresses of the local labels, you could print them like so:

_i          QSET    0
           REPEAT  _add_if + 1
           SMSG    $( "_add_if[", $#(_i), "] = $", $%(_add_if[_i]))
_i          QSET    _i + 1
           ENDR

which will print out:

_add_if[0] = $00000000
_add_if[1] = $00005003
_add_if[2] = $00005006

which is what we expected. (Note that _add_if[0] is supposed to be 0. I could've set _add_if[-1] to 0 instead.)

 

I could imagine turning the above loop into a debug macro. ;-)

 

And yes, I do the move through an intermediate array, so the example above does work as advertised, despite the fact that the source and destination slices overlap. :D

 

 

I do know of one caveat with the array feature: It doesn't work with strings of length 1.

 

Anyway, let me know what you think of the new features. I'll post a link to update binaries shortly.

Link to comment
Share on other sites

Ok, I've put the latest build of jzIntv and AS1600 over here:

 

http://spatula-city.org/~im14u2c/intv/dl/jzintv-20100911.zip

 

This new version of jzIntv has a vastly upgraded debugger, BTW. And it should automatically toggle between fullscreen and windowed when you hit a breakpoint. Use '?' inside the debugger to get the updated usage info. The new --sym-file flag lets you load AS1600's symbol table into the debugger, too.

 

Note that this version of jzIntv also has new graphics scaling code and dirty rectangle updates. It's the version I run myself, and I haven't seen any issues in this latest version. If you see any, let me know.

 

The as1600 binary has the new features I mentioned above, and fixes for 'LISTING "off"' and characters-in-expressions. What I did was treat single character single quoted strings as their own special type distinct from strings. This type decays to an expression rather than an expression list. I don't think it should break most things. The few things that do get broken are easily fixed.

Link to comment
Share on other sites

I've tried the new version of the assembler with my project and encountered the following :-

 

1) Macro names now have to start in the label field. In the previous version they could start after some whitespace. This isn't an issue it just makes the assembler behaviour consistent.

 

2) I had this macro defined :-

 

MACRO GROM_INDEX(idx)
       (ASC(%idx%,0)-32) SHL 3
ENDM

 

The new assembler now gives the following error in the listing :-

 

;       mvii #GROM_INDEX(' ')+C_BLK, r0
       mvii #(ASC(' ',0)-32) SHL 3
castle.asm:151: ERROR - syntax error at/before token "' " 

Link to comment
Share on other sites

Ok, I've put the latest build of jzIntv and AS1600 over here:

 

http://spatula-city.org/~im14u2c/intv/dl/jzintv-20100911.zip

 

This new version of jzIntv has a vastly upgraded debugger, BTW. And it should automatically toggle between fullscreen and windowed when you hit a breakpoint. Use '?' inside the debugger to get the updated usage info. The new --sym-file flag lets you load AS1600's symbol table into the debugger, too.

 

Note that this version of jzIntv also has new graphics scaling code and dirty rectangle updates. It's the version I run myself, and I haven't seen any issues in this latest version. If you see any, let me know.

 

The as1600 binary has the new features I mentioned above, and fixes for 'LISTING "off"' and characters-in-expressions. What I did was treat single character single quoted strings as their own special type distinct from strings. This type decays to an expression rather than an expression list. I don't think it should break most things. The few things that do get broken are easily fixed.

 

Joe,

Any chance of getting that packaged for OS X?

 

-dZ.

Link to comment
Share on other sites

I've tried the new version of the assembler with my project and encountered the following :-

 

1) Macro names now have to start in the label field. In the previous version they could start after some whitespace. This isn't an issue it just makes the assembler behaviour consistent.

 

I'm not sure what you mean here. Can you give me an example?

 

2) I had this macro defined :-

 

MACRO GROM_INDEX(idx)
       (ASC(%idx%,0)-32) SHL 3
ENDM

 

The new assembler now gives the following error in the listing :-

 

;       mvii #GROM_INDEX(' ')+C_BLK, r0
       mvii #(ASC(' ',0)-32) SHL 3
castle.asm:151: ERROR - syntax error at/before token "' " 

 

*d'oh* This is a direct consequence of making single character strings "special." That change ripples through a few places. I'll go make sure the new not-quite-a-string gets handled properly. Unfortunately, AS1600 has a number of anti-patterns in it, being full of case-and-paste. It was like that already when I started, and I haven't made it much better.

 

FWIW, the ASC(...,0) part isn't necessary any longer. You can just say ' '-32. (That is emphatically not an excuse for my breaking it though. I'll go fix it now.)

 

Joe,

Any chance of getting that packaged for OS X?

 

-dZ.

 

Certainly! Let me go fix up some of the breakage that GroovyBee pointed out, and I'll post a new build for both OSes after. The OS X one may be delayed slightly, depending on how much StarCraft II my wife decides to play today.

Link to comment
Share on other sites

I'm not sure what you mean here. Can you give me an example?

 

Certainly! If you define a macro like this :-

 

MACRO Test(aValue)
%aValue% SHL 3
ENDM

 

Notice the spaces before the words MACRO and ENDM the assembler generates the following :-

 

MACRO Test(aValue)
castle.asm:44: ERROR - Unexpected MACRO or ENDM directive
castle.asm:44: ERROR - syntax error at/before token "Test" 
%aValue% SHL 3
castle.asm:45: ERROR - syntax error at/before character "%"
ENDM
castle.asm:46: ERROR - Unexpected MACRO or ENDM directive

 

If I remove the whitespace it assembles as per normal.

 

Looking back at my bug report post I should have same "Macro directives" instead of "Macro names". Sorry for the misdirection.

Link to comment
Share on other sites

I'm not sure what you mean here. Can you give me an example?

 

Certainly! If you define a macro like this :-

 

MACRO Test(aValue)
%aValue% SHL 3
ENDM

 

Notice the spaces before the words MACRO and ENDM the assembler generates the following :-

 

MACRO Test(aValue)
castle.asm:44: ERROR - Unexpected MACRO or ENDM directive
castle.asm:44: ERROR - syntax error at/before token "Test" 
%aValue% SHL 3
castle.asm:45: ERROR - syntax error at/before character "%"
ENDM
castle.asm:46: ERROR - Unexpected MACRO or ENDM directive

 

If I remove the whitespace it assembles as per normal.

 

Looking back at my bug report post I should have same "Macro directives" instead of "Macro names". Sorry for the misdirection.

 

By golly, it does work with whitespace before the MACRO/ENDM directives in Beta3. I don't think that was ever intended, but that said, I don't see why it ought to be disallowed. I think I know which change introduced this. Going through my SVN logs:

 

Use strpbrk instead of strtok to tokenize a string.  

strtok has the undesirable characteristic that it treats a string of
separators as a single separator.  While this is ok for whitespace,
it isn't ok for other syntactical elements.

The new code compresses whitespace to a single character and then
uses strpbrk on the result.  This fixes a bug where the following
macro invocation was considered to have 3 arguments instead of 2:

   macro(a, b), c

Previously, this was hacked around by putting a space between the ')'
and the ','.  Now as1600 / IMASM handle this correctly it appears.

 

I think this change may have also changed the tokenization behavior for the first token on the line, since the code that looks for "MACRO" and "ENDM" always looks for those in token #0, and had even in Beta3. I'll keep digging, and if I can restore the Beta3 behavior I will.

 

EDIT: Aha, the manpage for strtok brings enlightenment (emphasis mine):

A sequence of two or more contiguous delimiter characters in the parsed string is considered to be a single delimiter. Delimiter characters at the start or end of the string are ignored.

Edited by intvnut
Link to comment
Share on other sites

Ok, I think I've fixed both issues. The ASC, STRLEN and CHARDEF directives now correctly handle the new single-character single-quoted pseudo-string. The MACRO/ENDM directives allow whitespace before them again.

 

I've made OS X and Win32 builds.

 

 

I'm also updating the "Examples" directory and will upload that in a little bit as a separate download.

Link to comment
Share on other sites

:cool: Thanks for the fast bug fixes. Is there a way to do manual single stepping in the jzintv debugger without continually typing "s1"? Its driving me nuts :lol:. A single command for "execute this subroutine but don't tell me about it in the trace" would be good. The accumulated cycle time seems to be missing in the latest build too.
Link to comment
Share on other sites

:cool: Thanks for the fast bug fixes.

It's exciting to see someone using my stuff. I also hate it when my bugs hold anyone else up.

 

Is there a way to do manual single stepping in the jzintv debugger without continually typing "s1"? Its driving me nuts :lol:.

Yes. Just hit enter to single step. :D

 

Actually, more precisely from jzIntv's online help:

  [enter]     [enter] with no command is the same as "s1" or "t1",
              depending on whether "s" or "t" was used most recently

 

A single command for "execute this subroutine but don't tell me about it in the trace" would be good.

The "t" command, when executed while stopped at a JSR will tell jzIntv to "trace over" it. It even tries to detect arguments after the JSR. Example:

00FC 7000 0000 0000 01FE 103D 02f1 103D ------iq  JSR  R5,$101d                
> t
Returned from JSR at $103D.
00FC 4800 0000 0000 01FE 1041 02f1 1041 ------iq  MVII #$00fe,R0               
> u103d
   $103D:    0004 0110 001d         JSR  R5,$101d    
   $1040:    0048                   SLL  R0     
   $1041:    02b8 00fe              MVII #$00fe,R0    
   $1043:    02bc 0102              MVII #$0102,R4    

There's a 1 byte argument to the function at $101D stored immediately after the JSR at $1040. jzIntv will actually detect the CPU reads after the JSR and will move the trace-over point forward.

 

Trace-overs like this are ephemeral. I'm guessing you want to make them persistent? ie. "Always trace-over this function"?

 

The accumulated cycle time seems to be missing in the latest build too.

It's not gone, it's just that you're less likely to see it. I used to display all of the CPU's code fetches, but that's a lot of worthless noise. But, each of those fetches would also show the current time stamp.

 

I should probably show the timestamp elsewhere, or add a command to print the current time. What do you think of just putting the timestamp in the prompt? ie.

 

00FE 4800 0000 0000 0102 1041 02f1 1045 ------iq  JSR  R5,$1738                
0000012883 > 

or similar?

 

EDIT: Hold on, I do have a timestamp feature already and the ability to print out all the CPU reads and writes. From jzIntv's debugger online help:

  z           Toggle showing timestamps during 'step'
  x           Toggle showing CPU reads and writes during 'step'

Example:

0000 0000 0000 0000 0000 1003 0000 1026 --------  MVII #$02f1,R6               
> 
0000 0000 0000 0000 0000 1003 02f1 1028 ------i-  JSR  R5,$1a83                
> 
0000 0000 0000 0000 0000 102B 02f1 1A83 ------i-  PSHR R5                      
> 
0000 0000 0000 0000 0000 102B 02f2 1A84 --------  PSHR R0                      
> z
Now showing timestamps during step
> 
0000 0000 0000 0000 0000 102B 02f3 1A85 --------  PSHR R1                  53
> 
0000 0000 0000 0000 0000 102B 02f4 1A86 --------  MVII #$01f0,R4           62
> 
0000 0000 0000 0000 01F0 102B 02f4 1A88 ------iq  MVII #$000e,R0           70
> 
000E 0000 0000 0000 01F0 102B 02f4 1A8A ------iq  JSR  R5,$1738            78
> x
Now showing CPU reads/writes during step
> 
RD a=1A8A d=0004 CP-1610          (PC = $1A8A) t=78
RD a=1A8B d=0114 CP-1610          (PC = $1A8A) t=78
RD a=1A8C d=0338 CP-1610          (PC = $1A8A) t=78
000E 0000 0000 0000 01F0 1A8D 02f4 1738 ------iq  PSHR R5                  91
> 
RD a=1738 d=0275 CP-1610          (PC = $1738) t=91
RD a=1739 d=01ED CP-1610          (PC = $1738) t=91
WR a=02F4 d=1A8D CP-1610          (PC = $1738) t=91
000E 0000 0000 0000 01F0 1A8D 02f5 1739 -------q  CLRR R5                 100
> 
RD a=1739 d=01ED CP-1610          (PC = $1739) t=100
RD a=173A d=0270 CP-1610          (PC = $1739) t=100
000E 0000 0000 0000 01F0 0000 02f5 173A ---Z--iq  PSHR R0                 106
> 

Edited by intvnut
Link to comment
Share on other sites

I think I need to go and read the manual now :lol:. Nothing is really a problem or holding me up. I'm just trying to get to my goal of getting a game working.

 

I think the cycle time should be on a command on its own. I'm mainly interested in cycle times for runs of code or complete functions. I think that it would be too cluttered otherwise.

Link to comment
Share on other sites

I think I need to go and read the manual now :lol:. Nothing is really a problem or holding me up. I'm just trying to get to my goal of getting a game working.

 

I think the cycle time should be on a command on its own. I'm mainly interested in cycle times for runs of code or complete functions. I think that it would be too cluttered otherwise.

 

Yeah, just hit '?' in the debugger to get a command summary.

 

With the cycle toggle command ('Z') does that get you far enough? It puts the cycle numbers at the right.

 

 

Also, I have a bit of a hack I wrote. It mostly works, but I never considered it quite production worthy. You're welcome to give it a try, though. This program takes the "dump.hst" file that jzIntv's history logger writes along with the listing file from as1600, and attempts to put together a function-level and loop-level profile of the program. (It would probably do a better job if as1600 gave an exact listing of the PROC/ENDP locations.)

 

Here's an excerpt of what it gives from a recent run on Space Patrol:

Total profiled cycles:  40818306

Function                       | Range     | Cycles       | % of Tot    
ENGINE1                        | 53F9-5658 |      8115735 |  19.88%
RUNENG                         | 5FE8-6080 |      5849072 |  14.33%
WAIT_SONG1                     | BE3A-BE56 |      3006972 |   7.37%
UPSPP                          | 59B0-5ADE |      2921163 |   7.16%
UPMUX                          | 5E13-5E9B |      2644002 |   6.48%
UPBGP                          | 58BF-599D |      2099166 |   5.14%
DBHC                           | CDAB-CDB3 |      1666140 |   4.08%
UPSFX                          | BD13-BD3F |      1217325 |   2.98%
UPD_PSG                        | BF24-BF77 |      1135974 |   2.78%
CKGGB                          | 5B14-5C31 |      1074220 |   2.63%
CKBRC                          | 572F-5793 |      1007051 |   2.47%
UPSPA                          | 5E9C-5EEB |       922604 |   2.26%
MENUINP                        | CD71-CDA6 |       719445 |   1.76%
UPANI                          | 5CF8-5D19 |       669931 |   1.64%
....a bunch deleted....

Loop breakdown for each function over 1%

FUNCTION: ENGINE1                        | 53F9-5658 |      8115735

FUNCTION: RUNENG                         | 5FE8-6080 |      5849072
   LOOP: RUNENG.loop                    | 5FF2-6014 |      3109405 |  53.16%
   LOOP: RUNENG.loop                    | 5FF2-6040 |      5820198 |  99.51%
   LOOP: RUNENG.loop                    | 5FF2-6057 |      5845930 |  99.95%

FUNCTION: WAIT_SONG1                     | BE3A-BE56 |      3006972
   LOOP: WAIT_SONG1.spin                | BE3E-BE52 |      3006843 | 100.00%

FUNCTION: UPSPP                          | 59B0-5ADE |      2921163
   LOOP: UPSPP.l                        | 5A30-5A52 |      1405072 |  48.10%

FUNCTION: UPMUX                          | 5E13-5E9B |      2644002
   LOOP: UPMUX.gp1skip                  | 5E26-5E33 |       184107 |   6.96%
   LOOP: UPMUX.gp1mobl                  | 5E28-5E4A |       740096 |  27.99%
   LOOP: UPMUX.gp2skip                  | 5E5D-5E6A |       658104 |  24.89%
   LOOP: UPMUX.gp2mobl                  | 5E5F-5E81 |      1050887 |  39.75%

... a bunch more deleted ...

 

I've attached the C code for the profiler (renamed to .c.txt due to silly filter rules) in case you want to give it a whirl.

profile.c.txt

Edited by intvnut
Link to comment
Share on other sites

The symbol table exporter/importer in as1600/jzintv is a boon to productivity :D. Would it be possible to have it display :-

 

"label (absolute address in hex)" instead of its current form of :-

 

"absolute address in hex (label)"

 

In my mind if you have the symbol table loaded it should take priority over the absolute address.

 

Ideally I'd like to switch the absolute address display off with a command to reduce the clutter.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...