flat assembler
Message board for the users of flat assembler.

Index > Main > Simple51 as a learning exercise

Author
Thread Post new topic Reply to topic
Hugh-Aguilar



Joined: 26 May 2020
Posts: 34
Hugh-Aguilar
Has the syntax of FASM changed? Macros seem to be different now.
I looked at your recent documentation on this website and it seems to not correspond with what I learned a few years ago.

I'm writing an 8051 assembler in FASM as a learning exercise.
I still have that Forth in the works, but am holding off on further work there until I have learned more about FASM programming, especially user-interface programming.

I call my assembler Simple51 because it is a pretty simple assembler.
It is single-pass, so it doesn't optimize forward-jump opcode sizes, and all labels have to be defined prior to being referenced. I'm not ambitious enough to write a multi-pass assembler that does optimization --- that kind of analytic compilation will be done in the Forth compiler --- it is an overkill in an 8051 assembler (I'm expecting pretty simple 8051 programs such as are normally done with a PLC).

Simple51 does not have macros. Maybe later I will write a pre-processor to provide macros, but this is not needed for basic 8051 programming. I allow more than one instruction per line, so if macros are being used solely for reducing the length of the 8051 source code, I have that covered already. I have EQU that helps a lot with the SFRs and general readability --- these have to resolve to a number though --- they don't do text-replacement.

I want to learn how a real-time OS works, so my idea is to write one for the 8051. I'm not quite as ambitious as Ville, that I would write an OS for the 64-bit x86 --- 8-bit processors are really more at my level.
So, first a simple 8051 assembler and then a simple OS --- have any of you done this already for any 8-bit or 16-bit micro-controller? --- has FASM ever been used for micro-controller programming, or only desktop-computer programming?

_________________
When all else fails, write the source.
Post 04 Oct 2020, 17:39
View user's profile Send private message Reply with quote
Melissa



Joined: 12 Apr 2012
Posts: 82
Melissa
I beleive you have 8051 assembler as include file for new fasmg...
Post 04 Oct 2020, 20:25
View user's profile Send private message Reply with quote
Hugh-Aguilar



Joined: 26 May 2020
Posts: 34
Hugh-Aguilar
Melissa wrote:
I beleive you have 8051 assembler as include file for new fasmg...

Well, I'm writing my own as a learning exercise. Smile
I rarely look at other people's code. I can't learn anything that way! Nobody will ever accuse me of being a maintenance programmer.
Technology will never advance if everybody just copies existing code. This is stagnation. This is why the Middle Ages lasted for many centuries.

For example, none of the assemblers I have written in the past (for the 65c02 and the MiniForth) restricted the user to one instruction per line.
It is bizarre that all of the other assemblers in the world (including FASM) have this pointless and annoying restriction.
Some CISC processors (the IBM370 and the VAX) allow an instruction to have one, two or three operands, so the assembler can't easily tell when the instruction is complete, which is why the assembler has the restriction of one instruction per line. That is ancient technology! Shocked In the 21st century, I would hope that we could stop copying 1960s assemblers that were written for long-obsolete processors and have corresponding limitations.

Of course, a good argument can be made that if I did copy other people's code, I would not diverge so much from the standard way of doing things.

This is how I do control-flow:
Code:
In a single-pass assembler you can't have forward jumps to arbitrary labels.
Control-flow is done with code blocks that are bracketed with { and } like in C syntax. Code blocks can be nested.
A code block has to be within a 2KB segment, not spanning the boundary between 2KB segments.
Note that I use the term "segment" to mean 2KB and "page" to mean 256 bytes, which is non-standard terminology.
In a code block, the following work:

IF A                            conditionally jumps forward on accumulator zero to past code block

IF C                            conditionally jumps forward on carry clear to past code block

IF bit                          conditionally jumps forward on bit-variable clear to past code block

IF= operand lit_number          conditionally jumps forward on not equal to past code block (CJNE)
                                the operand is A, @R0, @R1, Rx or a zero-page address

FOR Rx                          decrements the Rx register and jumps to the code block beginning if Rx is non-zero (DJNZ)

<                               unconditionally jumps to the code block beginning

<<                              unconditionally jumps to the parent code block beginning

<<<                             unconditionally jumps to the grandparent code block beginning

>                               unconditionally jumps to past the code block

>>                              unconditionally jumps to past the parent code block

>>>                             unconditionally jumps to past the grandparent code block

Note that IF and FOR are 8-bit relative. If they fail for destination out-of-range, then the user has to fix his code.
I encourage Forth-style factoring of functions into sub-functions, so code-blocks should be pretty small most of the time.
The > >> >>> are all 11-bit absolute, so they won't fail. The < << <<< optimize as 8-bit relative or 11-bit absolute.
I don't support 8-bit relative unconditional forward jumps --- this makes for a slight inefficiency in the code.

In the following, xxx yyy and zzz mean some code:
Forth has:  IF xxx THEN                                 For bitflags, I have:   { IF bbb xxx }
Forth has:  0= IF xxx THEN                              For bitflags, I have:   { IF /bbb xxx }
Forth has:  IF xxx ELSE xxx THEN                        For bitflags, I have:   { { IF bbb xxx >> } yyy }
Forth has:  IF xxx ELSE IF yyy ELSE zzz THEN THEN       For bitflags, I have:   { { IF bbb xxx >> } { IF bbb yyy >> } zzz }
CALL optimizes as either ACALL or LCALL internally. The label has to be prior to the reference in the source-code.
If you write your own OS, you should put each task in a segment so CALL will use ACALL as much as possible.
The OS system functions would be at the bottom of code memory and require CALL to assemble an LCALL to reach them.
Note that each task has its own vocabulary, so code label names can clash between tasks without a problem.
Data labels, EQUs and system functions are in the common vocabulary.
For semaphores, I have this:

GRAB bit label                  conditionally jumps backward on bit-variable set to label, and clears the bit (this is JBC)

GRAB is used in multi-tasker dispatching. The tasks are in order of priority and use GRAB to grab a semaphore and gain control.
The dispatcher is an ISR on a heartbeat timer. If none of the tasks gain control, then the system goes back to sleep.
The ISRs tied to I/O will push/pull a datum to/from a buffer, then set the associated semaphore for any task waiting on this.
The semaphore is a bit-variable that set means: "ready for the task to do grab control."
For input, the set semaphore means there is input data available in the buffer for the task to start using.
For output, the set semaphore means that there is room in the buffer for the task to output more data.
For example, for a UART input, the set semaphore means that a whole message has been sent from the desktop computer.
For a UART output, the set semaphore means that there is room in the buffer for a whole message to be sent to the desktop computer.
Typically an I/O buffer would be one page (256 bytes) in external memory.
    

It is quite possible that everybody will hate Simple51 because it is non-standard, so I will be the only user. I'm okay with this.
This is my usual situation! Wink

To the best of my knowledge, only two other people at Testra (John Hart and Steve Brault) ever learned how to use my assembler for the MiniForth. There has been a parade of super-duper maintenance programmers at Testra, all of whom claimed that they were better programmers than I am and that they would learn my assembler in one day and begin improving upon it --- all of them failed --- maintenance programmers aren't real programmers at all, which is why they focus on becoming experts in other people's software.

_________________
When all else fails, write the source.
Post 05 Oct 2020, 05:02
View user's profile Send private message Reply with quote
Melissa



Joined: 12 Apr 2012
Posts: 82
Melissa
Well, it is really difficult to start code from scratch these days in company Wink
That is unless you do alone...
Post 06 Oct 2020, 01:59
View user's profile Send private message Reply with quote
Hugh-Aguilar



Joined: 26 May 2020
Posts: 34
Hugh-Aguilar
Melissa wrote:
Well, it is really difficult to start code from scratch these days in company Wink
That is unless you do alone...

I think your "in company" means in a job at a company, and "do alone" means a program such as my Simple51 done as a fun project at home.

At Testra I had to start MFX from scratch because nobody had ever done anything like that before. Shocked
The MiniForth (built on a Lattice isp1048 PLD) was a VLIW processor. Each opcode contained 5 fields, each containing an instruction. All 5 instructions would execute concurrently in a single clock cycle. My assembler allowed the programmer to write his code as if the instructions executed sequentially in the order that they appeared in his source-code. My assembler would internally rearrange the instructions so they could be packed into opcodes as much as possible. The goal was to minimize the number of NOP instructions inserted. I didn't always pack every opcode with 5 instructions, but I did get quite a lot of parallelization. Instructions with dependencies on other instructions have to execute after those other instructions, not concurrently. It is okay to read a register and write to the register concurrently though, so long as the write is after the read in the source-code. This was out-of-order execution, similar to what the Pentium was doing, except that the Pentium does the rearranging at run-time whereas my MFX assembler was doing the rearranging at compile-time. The Pentium could only parallelize two instructions (in the U and V pipe), whereas the MiniForth could parallelize five instructions, so the business of rearranging was more complicated in the MiniForth.
I'm very proud of the MFX assembler. This is my best work! Smile
I'm not aware of anybody else who has done this. Tomasz Grysztar is an elite assembly-language programmer, so maybe he knows what VLIW is. What I have found though (on https://opencores.org/ for example), is that people who claim to know what VLIW is, are faking it --- they are using the term VLIW as a synonym for "super-duper" --- they don't understand how concurrent execution of instructions is possible, or what the limitations are.

I did a lot in MFX to make the assembly-language programmer's job as easy as possible. The rearranging of instructions is all done internally. The assembly-language programmer can write his code as if the instructions were executed sequentially, without even knowing about the parallelization and out-of-order execution under the hood. Despite this ease, nobody has ever succeeded at learning MFX assembly-language. It is still more difficult than mainstream processors such as the MSP430. There are no jump or branch instructions other than NXT. There is no instruction for addition. The 16-bit addition is a function done with a 4-bit half-adder and XOR. This is a very low-level assembly-language that is pretty mind-blowing for anybody who is only familiar with mainstream processors such as the 6502 or 8086.
Another reason why nobody has succeeded is a lack of motivation. There is no future in becoming an expert on something esoteric. Nobody outside of Testra will ever do any MFX programming, so working for Testra is a dead-end job (and the pay is low).

I can make Simple51 open-source because it is pretty simple. Allowing multiple instructions per line has always been in Forth assemblers. The control-flow structures, have been in Forth assemblers since the 1980s. I was originally going to use Forth syntax, such as BEGIN WHILE REPEAT and IF ELSE THEN, but decided to invent a new syntax instead. I have curly brackets around code blocks, that look similar to C, but they are not really the same as C because the IF is inside of the code-block rather than in front of the code block. My < and > are pretty comparable to C's CONTINUE and BREAK commands. I have << <<< >> >>> that are for the parent and grandparent code-block, which C does not have. Forth doesn't have that either. Still though, this is a pretty simple assembler.

BTW: I figured out how to optimize > >> and >>> (the unconditional forward jumps) to be 8-bit relative, 11-bit absolute or 16-bit long. I can allot 3 bytes to the jump. If it can be done with 8-bit relative or 11-bit absolute (two-byte instructions) then I assemble a NOP in the third byte. This bloats out the code somewhat. This does not hurt efficiency however, because the NOP never executes. This trick with the NOP was done in the 1980s in 6809 Forth assemblers --- I had just forgotten about the trick.

The 8051 is 1980's technology. If the Kolibiri people steal Simple51 and put their own copyright on it (as they did with Menuet), all they have is an 8051 assembler. I can retarget it to a more modern processor such as the MSP430 and make that closed-source so they can't steal it The MSP430 is popular because it has low power-consumption.

_________________
When all else fails, write the source.
Post 07 Oct 2020, 16:00
View user's profile Send private message Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
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 vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2020, Tomasz Grysztar. Also on GitHub, YouTube, Twitter.

Website powered by rwasa.