Jump to content
IGNORED

Millfork


jum

Recommended Posts

Has anyone tried using Millfork language to compile something for the Lynx?

 

A "middle-level programming language targeting 6502-based, 8080-based and Z80-based microcomputers."

 

https://github.com/KarolS/millfork

 

Readme says that it can target Atari Lynx and Atari 8-bit.  (although primarily for C64).

 

Edited by jum
  • Like 2
  • Thanks 1
Link to comment
Share on other sites

46 minutes ago, jum said:

Has anyone tried using Millfork language to compile something for the Lynx?

 

A "middle-level programming language targeting 6502-based, 8080-based and Z80-based microcomputers."

 

https://github.com/KarolS/millfork

 

Readme says that it can target Atari Lynx and Atari 8-bit.  (although primarily for C64).

 

Funny.

There is a Lynx example.

Link to comment
Share on other sites

21 minutes ago, 42bs said:

Copy that. Need to look at the generated code though.

I've already done cursory inspection of generic example on atari 8, and it seems to be quite reasonable.

 

Regarding competition I doubt that you can do any worse than cc65 though ;)

 

EDIT:

I've attached a disassembly of the main loop of that generic example (excluding printing etc).

fizzbuzz.txt

Edited by laoo
  • Like 1
Link to comment
Share on other sites

9 minutes ago, laoo said:

I've already done cursory inspection of generic example on atari 8, and it seems to be quite reasonable.

 

Regarding competition I doubt that you can do any worse than cc65 though ;)

 

EDIT:

I've attached the disassembly main loop of that generic example (excluding printing etc)

fizzbuzz.txt 4.02 kB · 2 downloads

You can set "-s" to get directly assembly output (yes, just playing with it also).

Tried the Lynx example and it works!

  • Like 1
Link to comment
Share on other sites

For millfork the same rules apply as for cc65: Check the code and help the compiler to optimize:
 

byte t
sbyte c
	c = 31
	do {
		palette_ram_green[t]=palette[c]
		c -= 1
	} while ( c >= 0 )

	asm { nop }
	for t,31,downto,0 {
		palette_ram_green[t]=palette[t]
	}
	asm { nop }

Both versions do the same, but resulting code is different (NOPs inserted as separator only):

    NOP
    LDY #$1F
.do__00010:
    LDA palette.array, Y
    STA $FDA0, X
    DEY
    BPL .do__00010
    NOP
    LDX #$20
.do__00013:
    DEX
    LDA palette.array, X
    STA $FDA0, X
    TXA
    BNE .do__00013
    NOP

But not bad anyway.

  • Like 1
Link to comment
Share on other sites

Would be nice to see a comparison with cc65 generated ASM. If I get a chance tonight I will do same code in cc65 and upload the asm generated.

 

Also would need to expand the lynx lib/module for Millfork.

Edited by jum
Link to comment
Share on other sites

OK here's the cc65 code and ASM output for comparison. Note I used the cc65 "-Or" optimisation flag to enable register variables:

 

cc65 code:

 

void main()
{
    uchar i;
    
    asm("nop");
    i = 31;
    do {
        MIKEY.palette[i] = work_palette[i];    
        i -= 1;
    } while ( i >= 0 );

    asm("nop");
    for (i = 0; i < 32; i++)
        MIKEY.palette[i] = work_palette[i];    
    asm("nop");
}


 

Assembler output (.s file):

; ---------------------------------------------------------------
; void __near__ main (void)
; ---------------------------------------------------------------

.segment    "CODE"

.proc    _main: near

.segment    "CODE"

    jsr     decsp1
    nop
    lda     #$1F
L0060:    sta     (sp)
    ldx     #$FD
    lda     #$A0
    clc
    adc     (sp)
    bcc     L0044
    inx
L0044:    jsr     pushax
    ldy     #$02
    lda     (sp),y
    tay
    lda     _work_palette,y
    ldy     #$00
    jsr     staspidx
    lda     (sp)
    sec
    sbc     #$01
    bra     L0060
L005F:    lda     (sp)
    cmp     #$20
    bcs     L004E
    ldx     #$FD
    lda     #$A0
    clc
    adc     (sp)
    bcc     L0058
    inx
L0058:    jsr     pushax
    ldy     #$02
    lda     (sp),y
    tay
    lda     _work_palette,y
    ldy     #$00
    jsr     staspidx
    lda     (sp)
    ina
    sta     (sp)
    bra     L005F
L004E:    nop
    jmp     incsp1

.endproc

 

Way longer/slower (for this example), and very hard to follow! 

  • Thanks 2
Link to comment
Share on other sites

18 hours ago, jum said:

OK here's the cc65 code and ASM output for comparison. Note I used the cc65 "-Or" optimisation flag to enable register variables:

Are you sure it's an optimization option giving the best possible code? Because the output is terrible and I wonder how it can be even called an optimized code. I know cc65 is bad, but still...

 

Nevertheless the guy responsible for this project is responsive and the compiler is in active development. I think it's worth investing as it might be very good alternative.

Edited by laoo
Link to comment
Share on other sites

Really guys....

 

The correct way to write this code is:

 

#include <lynx.h>
#include <string.h>

unsigned char work_palette[32];

void main()
{
    asm("nop");
    memcpy(MIKEY.palette, work_palette, sizeof(work_palette));
    asm("nop");
}

Which translates to:

 

;
; asm("nop");
;
        nop
;
; memcpy(MIKEY.palette, work_palette, sizeof(work_palette));
;
        ldy     #$1F
L0023:  lda     _work_palette,y
        sta     $FDA0,y
        dey
        bpl     L0023
;
; asm("nop");
;
        nop
;
; }
;

The beauty of C is the use of standard library functions that make the work easier to understand and faster to execute.

Link to comment
Share on other sites

Imho it's not the case. This whole program is meaningless and "perfect" optimization should be something like

        ldx     #$1F
L0023:  stz     $FDA0,x
        dex
        bpl     L0023

as the global array according to standard should be zeroed.

 

The obvious meaning of this comparison is to... well... compare how the compiler handles the same code construct, in this instance two versions of loops. Not to find which standard function to use. 

 

 

Edited by laoo
  • Like 1
  • Thanks 1
Link to comment
Share on other sites

23 hours ago, jum said:

OK here's the cc65 code and ASM output for comparison. Note I used the cc65 "-Or" optimisation flag to enable register variables:

 

cc65 code:

 


void main()
{
    uchar i;
    
    asm("nop");
    i = 31;
    do {
        MIKEY.palette[i] = work_palette[i];    
        i -= 1;
    } while ( i >= 0 );

    asm("nop");
    for (i = 0; i < 32; i++)
        MIKEY.palette[i] = work_palette[i];    
    asm("nop");
}

 

If MIKEY is a struct for all Mikey registers, then the comparison is not fair. But nevertheless, for this code construct, Millfork is the better compiler.

 

BTW: Doesn't cc65 warn about the "i >= 0" comparison? Actually it should create an endless loop as 0 - 1 => 0xff which is >= 0 !

 

EDIT: Oh, it really does ?

Edited by 42bs
Link to comment
Share on other sites

On 10/19/2019 at 3:17 PM, karri said:

Really guys....

 

The correct way to write this code is: "memcpy"

 

Yes, that is true for this specific example, but only because you have optimised it, not the compiler.

 

It's not a good example, maybe a better example is something like:

 

for (i = 0; i < 32; i++)

        {

        B[i] = A[i] * 2 + 1;         // some processing is happening, not just a memcpy

        }

 

As said before, the point of the comparison is to see how the the 2 compilers handle the same code construct.

 

 

Link to comment
Share on other sites

  • 10 months later...

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...