6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Fri Mar 29, 2024 2:31 pm

All times are UTC




Post new topic Reply to topic  [ 14 posts ] 
Author Message
PostPosted: Thu Apr 01, 2010 8:31 pm 
Offline
User avatar

Joined: Thu Mar 11, 2004 7:42 am
Posts: 362
Version 1.0 of The One Page Assembler, a symbolic cross assembler written in 256 bytes of 6502 assembly, has been released! You need only supply the input routine. (Merely waiting for a keypress and returning it the accumulator will suffice.) It can assemble itself from its own source code! Considering its size, the source code it assembles is surprisingly readable. For example, the ubiquitous Hello World program:

Code:
10 @ 'BPL'
20 @ 'JSR'
60 @ 'RTS'
A2 @ 'LDX #'
B5 @ 'LDA ,X'
CA @ 'DEX'

F000 @ 'OUTPUT' (a system-dependent address, of course)

1000 @

    'LDX #',11,
00* 'LDA ,X','STRING',,
    'JSR','OUTPUT'
    'DEX',
    'BPL',00=<
    'RTS'
'STRING';;
    (DB) 21,64,6C,72,6F,47,20,69,48,20,20,21,6D,6F,4D,20,69,48


Features of The One Page Assembler:

  • Single pass assembly
  • In-place: object code stored directly in memory
  • "Global" (define once) and "local" (redefinable) labels
  • Multiple origins
  • Nestable commenting
  • Free-form source code
  • Fault-tolerant: unrecognized commands are ignored (consequently it doesn't bother you with any pesky error messages)


In comparison, the original Apple II Mini-Assembler weighs in at an outrageously (considering it is neither symbolic nor has nestable comments) bloated 317 bytes! ($F500 to $F63C.) This 317 bytes does not include the addressing mode character tables (e.g. ,X), nor the disassembly routines and tables from the Apple II Monitor that it makes use of (e.g. the tables of instruction names, e.g. ADC), and as such the Mini-Assembler is one of the most directly comparable assemblers.

Available separately are opcode tables for the NMOS 6502 and SWEET16 interpreter, a relocation utility for installing it on virtually any typical 6502-based system (and many exotic systems as well), a utility for processing a new "hex" file format more flexible than the Intel .hex and Motorola S-record formats, and a couple dozen bytes of bootstrapping code for installing all of these wonderful and exciting new utilities!

Install it now! You know you want to!

http://www.lowkey.comuf.com/


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Fri Apr 02, 2010 6:57 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10760
Location: England
You almost had me fooling myself by releasing on that date, but I have to say this is masterful. Nice one!

Unfortunately I can't quite get it to work...

Cheers
Ed


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sat Apr 03, 2010 1:51 am 
Offline

Joined: Sat Jan 04, 2003 10:03 pm
Posts: 1706
This is perhaps one of the most compact and specialized forms of Forth I've ever seen.


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sat Apr 03, 2010 8:43 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10760
Location: England
BigEd wrote:
Unfortunately I can't quite get it to work...


Aha! I'd completely missed the point of the relocation table, which is why my efforts to use the relocater were going wrong.

Then, I think there are a couple of bugs in the demo, corrected below. And it doesn't work well if it overwrites itself!

Here's an approx transcript of a demo session on a TUBE emulator:
Code:
$ ./bbc-emu basic.bbc
> REM input the bootstrap
>!&4000=&02550216
>!&4004=&00810690
>!&4008=&40A900F6
>!&400C=&E0200295
>!&4010=&9021C9FF
>!&4014=&60E9D0F9
>X%=&70
>!X%=&5000
> REM load the hex loader
>CALL &400A

<h<|,h=x9)<xx(lx<xxh|m=y|m=xxhx98-l(=yx8l(98,x<x<xx99)l(m,xx|(i=
x9(||(8|lm=x|y<xm,<x|(x-m,<ylhx)x9<8|(9xlm=x|m<xlm=y|m<ylhy989,h
x99x|h9|8y,xx9,9l(=l(9xxx9i(|hil(y9}lx<|=l=x-l=yxx|(h)<m=x8h|hh|
,8=y<i<x<x<x<x!
>>
>
> REM patch the output routine
>!&5016=&C9FFE020
> REM load the assembler
>!X%=&0FFA
>CALL &5000
(10,04,30,02,00,01,D8,A9,00,85,79,20,1F,10,A2,00,8A,C9,29,F0,01,E8,20,E0,FF,C9,
28,F0,F8,CA,D0,F1,C9,5F,D0,E7,60,C9,3B,D0,0F,A5,78,81,76,E6,76,D0,02,E6,77,A5,
75,85,78,60,C9,3E,D0,08,18,A5,74,E5,76,81,76,60,C9,27,D0,59,A0,FF,20,E0,FF,C9,
20,90,F9,C8,49,27,91,72,D0,F2,A9,00,85,7A,A9,12,85,7B,A6,79,F0,27,A0,FF,18,C8,
B1,7A,B0,06,51,72,C9,01,B1,7A,D0,F3,C8,B1,7A,85,76,C8,B1,7A,90,30,38,98,65,7A,
85,7A,90,02,E6,7B,CA,D0,D9,A0,FF,C8,B1,72,91,7A,D0,F9,E6,79,C8,A5,74,91,7A,C8,
A5,75,91,7A,60,A4,76,C9,3D,D0,0E,B1,70,85,76,C8,B1,70,85,77,A5,74,85,78,60,C9,
2A,D0,0A,A5,74,91,70,C8,A5,75,91,70,60,C9,2C,F0,09,C9,3C,D0,13,18,98,E5,74,A8,
98,81,74,A5,77,85,76,E6,74,D0,02,E6,75,60,C9,40,D0,06,84,74,A4,77,84,75,49,30,
C9,0A,90,0A,09,20,69,88,C9,FA,90,0F,29,0F,A2,04,06,76,26,77,CA,D0,F9,05,76,85,
76,60,Q)
>
Syntax error
>
> REM call the assembler and give it the demo program
>CALL &1000
10 @ 'BPL'
20 @ 'JSR'
60 @ 'RTS'
A2 @ 'LDX #'
B5 @ 'LDA ,X'
BD @ 'LDA W,X'
CA @ 'DEX'

FFEE @ 'OUTPUT' (a system-dependent address, of course)

7000 @ ( was 1000 @ )

    'LDX #',11,
00* 'LDA W,X','STRING',,
    'STX'
    'JSR','OUTPUT' ,, (added double comma)
    'DEX',
    'BPL',00=<
    'RTS' , (added comma)
'STRING';;
    (DB) 21,64,6C,72,6F,47,20,69,48,20,20,21,6D,6F,4D,20,69,48
_
>>
>
>CALL &7000
 i Mom!  Hi Gorld!>
>


I hand-relocated the assembler, and compacted the hex for pasting purposes. The version above uses zero page from &70 and calls &FFE0 for 'get'

(BBC BASIC has a built-in assembler so it's an odd place to be trying this out)

The spurious error is from the closing ) after the Q. I'm going to ignore the initial H going missing, and regard this as success!

Edit: oops, I didn't initialise the pointers LABELS and SYMBUF - will only work if I'm lucky.

(The TUBE emulator is by Michael Firth. The program doesn't work on a real Beeb, but I haven't yet figured out why. The assembler works!)


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Wed Apr 07, 2010 2:27 am 
Offline
User avatar

Joined: Thu Mar 11, 2004 7:42 am
Posts: 362
The missing H is my fault, not yours. The Hello World program has 6 bugs:

  • B5 should be BD (I intended to use the LDA rather than LDAW for the BD opcode, since zp,x was not (supposed to be) used, but either way is fine)
  • missing ,, after 'OUTPUT'
  • missing , after 'RTS'
  • 47 should be 57
  • missing , after (the second) 48
  • missing _ at the end


Sorry for all the bugs. I forgot to test the Hello World program (and the checksum example on the "hex" file page, but that one seems to be working). I was in a big hurry to make my (self-imposed) release date, and the documentation suffered badly due to being written at the eleventh hour, and it's little more than a first draft. (I found a serious bug in the relocator on the evening of the 31st, so I wound up changing the relocation data format when I had planned to be writing documenatation.) In fact, I was seriously considering waiting until next year to release. The way this thing has gone, I probably should have released on Friday the 13th. :)

The One Page Assembler is whimsical, of course, but the other three utilities have a serious intent. They are part of my still-unfinished 6502 Forth project, and came about when I was considering how to distribute the result(s). Consequently, I'll need to get the bootstrap, "hex" file, and relocator documenatation into a more complete state before I release anything Forth-related (one of these days, one of these days, ...). (I also plan to convert the assembler syntax to Forth. I hadn't intended to release any of the three utilities prior to the Forth release, but they were complete at least software-wise, and The One Page Assembler kinda forced their release.) I've got some notes of my own, but any suggested improvements or clarifications to the documentation are more than welcome.

If you decide to try the corrected Hello World on a real Beeb and it still doesn't work, let me know. I'd like to make sure there aren't any bugs in the three serious utilities, or fix any bug(s) that do exist.

By the way, just for reference, here is how the relocator can be used to produce your hand-relocated result.

Code:
     lda #table
     sta 0,x
     lda #>table
     sta 1,x
     lda #helloworld_header
     sta 2,x
     lda #>helloworld_header
     sta 3,x
     jsr relocator
     rts
table
     dw $1000,$1000
     dw $0200,$FFE0
     dw $0000,$0070


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Fri Apr 09, 2010 4:18 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10760
Location: England
dclxvi wrote:
Sorry for all the bugs

Not at all, and thanks for the whimsical offering. It's been very interesting to try to understand the code. Impressive mileage out of only two index registers. I really liked the way you zero-terminate the symbol names, and also the nested comment handler.

dclxvi wrote:
...try the corrected Hello World on a real Beeb

I'm glad to say the corrected program now runs on the Beeb, if I warm reset before running it. (Doubtless I have carelessly clobbered some important memory locations, which was breaking the OSWRCH output routine.)

dclxvi wrote:
By the way, just for reference, here is how the relocator can be used to produce your hand-relocated result.

Thanks!

Good luck with the Forth project: I look forward to that. (I meandered to The Evolution of Forth which describes microFORTH as having a 1k nucleus, versus 4k for Forth proper)

Cheers
Ed


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Thu Apr 15, 2010 2:10 am 
Offline
User avatar

Joined: Thu Mar 11, 2004 7:42 am
Posts: 362
BigEd wrote:
I really liked the way you zero-terminate the symbol names, and also the nested comment handler.


The zero termination was one of the last optimizations. (Before that it used apostrophe terminated strings. It might of been one of the only languages/applications to use apostrophe terminated strings - I don't know of any others - but it wasn't to be.) I think it saved two bytes. The second to last version of The One Page Assembler was 260 bytes, and I didn't want to resort to the desperation optimizations I'd come up with. Fortunately, I didn't have to.

The nested comment handler, originated with the "hex" file utilities and had been stored in a temporary (zero page) variable rather than a register. After getting the first version of The One Page Assembler working, it dawned on me that I could keep the comment depth in a register, since none of the other commands/characters would be executed if it were non-zero. Which meant rewriting both "hex" file utilities to include this optimization, regenerating the bootstrap data, etc. It really is a wonder I got any documentation done.

BigEd wrote:
Good luck with the Forth project: I look forward to that.


I should point that my various Forth projects have been unfinished for a long time (as you can tell from the age of some of the posts in the Forth sub-forum). I've explored a number of different avenues, so I've sort of been moving a circle without ever really getting anything complete. But it's getting near top of my list of projects to finish, so I'm running out of excuses. :)


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sun Apr 17, 2011 1:05 pm 
Offline

Joined: Mon Mar 02, 2009 7:27 pm
Posts: 3258
Location: NC, USA
I'm primarily a hardware guy even though I know 6502 assembly. When I get into "software" mode, I can understand things abit more so forgive my ignorance, but I have a few questions about this assembler...

First, can it be used in a similar fashion to Micromon?


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sun Apr 17, 2011 7:06 pm 
Offline

Joined: Sun Apr 10, 2011 8:29 am
Posts: 597
Location: Norway/Japan
dclxvi wrote:
The zero termination was one of the last optimizations. (Before that it used apostrophe terminated strings. It might of been one of the only languages/applications to use apostrophe terminated strings - I don't know of any others - [..]

The Norsk Data SINTRAN III 16-bit operating system used apostrophe-terminated strings in those places where a string had a fixed maximum length and the actual string was shorter. This was limited to the system call interface though - the various languages used other mechanisms for their internal strings (descriptors for ND Fortran, zero-terminated for C etc.). Like you I haven't seen any languages use apostrophe-terminated strings internally.

Running 'strings' on a binary image of the SINTRAN-III OS shows that e.g. the internal list of error codes were also apostrophe-terminated:
..
BATCH SYSTEM ERROR'
ILLEGAL PARAMETER IN CLOCK'
ILLEGAL PARAMETER IN ABSET'
ILLEGAL PARAMETER IN UPDAT'
ILLEGAL TIME PARAMETER'

-Tor


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Wed Apr 20, 2011 2:34 am 
Offline
User avatar

Joined: Thu Mar 11, 2004 7:42 am
Posts: 362
ElEctric_EyE wrote:
I have a few questions about this assembler...


Feel free to ask as many as you like. The One Page Assembler was a fun project to do, and I don't mind supporting it. I'm sure there's a lot of room for improvement with the documentation, given how little time I spent on it.

ElEctric_EyE wrote:
First, can it be used in a similar fashion to Micromon?


I'm not all that familiar with Micromon, but I found a little information from a quick web search. Ed and I both used redirected/pasted text, but with a "wait for a keypress" input routine, The One Page Assembler can be used interactively. It's awfully primitive though.

All commands execute immediately, so as long as you don't make any typing errors, you'll be fine. :) Seriously, some things are easy to redo (e.g. the @ command), but the one biggie that isn't is global labels. If you mess up between the apostrophes, you can terminate the bad label-in-progress with an apostrophe, but you'll waste a global label (and there are only 255 global labels available, so you can't mess this up too often), and the incorrect label will now be defined (with a value you likely didn't intend, so it can't be used later). However, it should take fewer than a dozen bytes to add a "discard the last global label" command (all that has to happen is decrementing the global label count (if it's not already zero)).

Other commands you might want to add are a "decrement the address" command (to undo a comma command, rather than using @ for that purpose), and a "dump a page of memory" command, since The One Page Assembler doesn't produce any output (it just stores bytes). Actually, with just the "hex digit", @, comma, and dump commands, you'd have a tiny, primitive, but usable monitor.

Tor wrote:
The Norsk Data SINTRAN III 16-bit operating system used apostrophe-terminated strings in those places where a string had a fixed maximum length and the actual string was shorter.


Interesting. I wasn't aware of that OS. I wonder why it used that character. (In The One Page Assembler it was simply an artifact of the parsing routine where apostrophe was the string end delimeter.)


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Wed Apr 20, 2011 4:03 pm 
Offline

Joined: Mon Mar 02, 2009 7:27 pm
Posts: 3258
Location: NC, USA
dclxvi wrote:
...All commands execute immediately, so as long as you don't make any typing errors, you'll be fine...


That is interesting. So if I typed in an LDA $location first, it would execute that immmediately. Then if I type STA $location2, it would execute that as well?

...With Micromon/Supermon you would type in your assembly. Then have to type a command to begin execution.

I am intrigued by the shortness of your coding.
In my system I am very close to adding a keyboard. Your 1page assembler would be in ROM, and initially I would like to use it to read and write values to peripherals. One is a write to only graphic display, and another is a read/write Flash memory.
Would the fact that your program would be present in ROM pose a problem? or should it reside in RAM?
I know you mentioned at the beginning that it self-assembles which would imply to me it needs to reside in RAM...
Also what exactly does it mean it can assemble itself?

Thanks for fielding my questions!


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Thu Apr 21, 2011 2:19 am 
Offline
User avatar

Joined: Thu Mar 11, 2004 7:42 am
Posts: 362
ElEctric_EyE wrote:
That is interesting. So if I typed in an LDA $location first, it would execute that immmediately. Then if I type STA $location2, it would execute that as well?


That's not what I was trying to say. The assembler doesn't execute the code it assembler, it just stores it in memory. What I meant is that if you type a line like:

1234 @ 56,

then the @ command will get executed when you type @, not when you press the Enter key, so, e.g., pressing backspace after the @ has no effect. I'd always been in the habit of typing something, using backspace to make corrections, giving it a quick check...looks good, so press Enter (often followed by "oops, no it wasn't right" :)). I'd hadn't realized how much of a habit that was until I wrote The One Page Assembler. A bit of an adjustment for me there.

Actually, it's possible (and fairly simple) for the input routine to be written in such a way that you only pass characters back to the assembler after Enter has been pressed. The input routine would be: (a) is the buffer empty? (b) if not, return the next character in the buffer, and remove it from the buffer (c) otherwise, get a line of input, store it in the buffer, and return (and then remove) the first character of the buffer. But it's not as simple as a "wait for a keypress" input routine.

(Incidentally, the "Old monitor ROM" in the original Apple II had a fairly short single step command that executed code in a way sort of like you describe (the code had to be somewhere in memory already, though). A few instructions it handled specially (BRK, JMP, JSR, RTI, RTS, branches, and maybe some others that I don't remember offhand), but for the rest it loaded the registers, copied the next instruction to the zero page, executed that, then saved the registers.)

ElEctric_EyE wrote:
Would the fact that your program would be present in ROM pose a problem? or should it reside in RAM?


The assembler itself can be run from RAM or ROM; it uses dozen or so zero page locations which must be in RAM.

I'd recommend in RAM at first so you can make sure you've got it working. You might want to experiment with it in RAM for a while to help you decide if you want to add any commands/feature for convenience.

ElEctric_EyE wrote:
Also what exactly does it mean it can assemble itself?


What that means is that source code of The One Page Assembler can be assembled by The One Page Assembler. (Very chicken-and-egg.)

For example, to install EhBASIC you assemble it with whatever assembler Lee used (or translate it for the assembler of your choice). Once you've got it installed, and you decide you want to make a change (add a another keyword, or remove a feature you're not using), you'd update the source code, re-assemble it with the assembler, the re-install it. (A similar situation exists for FIG-Forth).

Now imagine the same sort of situation with The One Page Assembler. You've installed it but you want to add a feature. You update the source, and re-assemble the updated source code with...The One Page Assembler you've already installed. So once you've installed The One Page Assembler you don't need to keep any of the tools you used to install it, even if you want to make changes to it. By contrast, with EhBASIC/FIG-Forth you don't the need an assembler to run them, but you do need an assembler to rebuild the code if you make changes to the language itself.


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Thu Apr 21, 2011 6:28 pm 
Offline

Joined: Mon Mar 02, 2009 7:27 pm
Posts: 3258
Location: NC, USA
Ok, thanks for the explanation and for sharing your work...

I need something to start with in my project so I don't have to keep "burning ROMs". In my case the FPGA config PROM. I would rather put some kind of assembler (yours or Micromon-64, maybe both) in the ROM. Then, make necessary software changes/upgrades and save the temp changes to Flash memory. Right now I have no disk I/O yet... That way on powerup the original assembler would be there, but the option would also be to copy over from the Flash previously stored info.

Thing is, your software would be easy to input and test @256bytes?!
Micromon will take time to hack...


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Fri Apr 22, 2011 8:47 pm 
Offline
User avatar

Joined: Thu Mar 11, 2004 7:42 am
Posts: 362
What I was getting at was that, if you are going to use The One Page Assembler, you may want to get familar with it first -- rather than trying to get a new system up and running and learn a new tool at the same time. The 256 byte length means there aren't a lot features and the tool pretty much assumes you know what you are doing. Also note that the 256 byte length does not include the 6502 instruction table (e.g. the 00 @ 'BRK' etc. part -- it is a cross-assembler, after all).

One possibility is to try it out on the 6502 simulator/emulator of your choice. That way you're not trying to bite off so much all at once, and you can see if it meets your needs.

I've generally found it easier to try/debug code from RAM, but YMMV of course. One thing you could store it ROM, copy it to RAM at power up and run it from there.

I've only skimmed the thread you linked, but when you're ready to try it (on your system or on an simulator), if you let me know the memory map (at least where RAM, ROM, and the I/O routines are located), I can help you adapt it, or give you some tips. If want to send some of the "not ready for prime time" code (e.g. I/O routines) you've got via email/pm that's fine too.


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 14 posts ] 

All times are UTC


Who is online

Users browsing this forum: No registered users and 6 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: