Yazwh0_BitMagic_Legacy/Libraries/Vera/VideoMemory.csasm

namespace Vera;

public enum AddressStep
{
    None,
    Step_1,
    Step_2,
    Step_4,
    Step_8,
    Step_16,
    Step_32,
    Step_64,
    Step_128,
    Step_256,
    Step_512,
    Step_40,
    Step_80,
    Step_160,
    Step_320,
    Step_460
}

public static class VideoMemory
{
    private static byte _zpWorkAddress = 0;
    private static bool _copyWorkAddressSet = false;

    public static void SetCopyZpWordAddress(byte zpWordAddress)
    {
        if (!_copyWorkAddressSet) 
        {
            _zpWorkAddress = zpWordAddress;      
            _copyWorkAddressSet = true;
        }
        else if (_zpWorkAddress != zpWordAddress)
        {
            throw new Exception("Copy called with an zp address that is not the same as a previous call.");
        }
    }

    public static void SetAddress(int address, AddressStep step = AddressStep.None)
    {
        var upper = ((address & 0xff0000) >> 16) + ((int)step << 4);
        var high = (address & 0xff00) >> 8;
        var low = (address & 0xff);

        if (upper != 0)
        {
            lda #@(upper)
            sta ADDRx_H
        } else {
            stz ADDRx_H
        }

        if (high != 0)
        {
            lda #@(high)
            sta ADDRx_M
        } else {
            stz ADDRx_M
        }
        
        if (low != 0)
        {
            lda #@(low)
            sta ADDRx_L
        } else {
            stz ADDRx_L
        }
    }


    public static void Copy(object source, int dest, int count, byte zpWordAddress)
    {
        SetCopyZpWordAddress(zpWordAddress);
        
        Copy(source, dest, count);
    }

    public static void Copy(object source, int dest, int count)
    {
        ; Copy from '@(source)' to $@(dest.ToString("X4")), @(count) bytes.
        if (!_copyWorkAddressSet)
        {
            throw new Exception("Copy called but zpWordAddress is not set.");
        }

        int addressPart = (dest & 0xf0000) >> 16;
        lda #@(addressPart + 0x10)
        sta ADDRx_H

        addressPart = (dest & 0xff00) >> 8; 

        if (addressPart == 0)
        {
            stz ADDRx_M
        } 
        else 
        {
            lda #@(addressPart)
            sta ADDRx_M
        }

        addressPart = dest & 0xff;

        if (addressPart == 0)
        {
            stz ADDRx_L
        } 
        else 
        {
            lda #@(addressPart)
            sta ADDRx_L
        }

        lda #<@(source)
        sta @(_zpWorkAddress)
        lda #>@(source)
        sta @(_zpWorkAddress)+1

        ldx #>@(count)
        ldy #<@(count)
        sty :CopyProcScope:CopyToVram:end_check+1

        jsr :CopyProcScope:CopyToVram
    }

    public static void CopyProc(byte zpWordAddress)
    {
        SetCopyZpWordAddress(zpWordAddress);
        
        CopyProc();
    }

    public static void CopyProc()
    {
        if (!_copyWorkAddressSet)
        {
            throw new Exception("CopyProc called but zpWordAddress is not set.");
        }
        .scope CopyProcScope
        .proc CopyToVram

        ;sty end_check+1
        ldy #0

        .loop:
        lda (@(_zpWorkAddress)), y
        sta DATA0
        iny
        bne loop

        inc @(_zpWorkAddress)+1
        dex
        bne loop

        .remain_loop:
        lda (@(_zpWorkAddress)), y
        sta DATA0
        iny
        .end_check:
        cpy #$aa ; gets updated
        bne remain_loop

        rts
        .endproc
        .endscope
    }

    // todo: fix this. edge cases don't work.
    public static void ClearVram(int startingAddress, int length)
    {
        if (length != 0) 
        {
            VideoMemory.SetAddress(startingAddress, AddressStep.Step_1);

            ldy #>@(length)+1
            ldx #<@(length)+1
            jsr videomemory:clear
        }
    }

    public static void ClearVramProc()
    {
        .scope videomemory
        .proc clear

        .loop:
        stz DATA0

        dex
        bne loop
        dey
        beq done
        ldx #$ff
        bra loop

        .done:
        rts
        .endproc 
        .endscope
    }
}