/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 *
 * Copyright (C) 2008 by Jens Arnold
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/

#define CLOCK_MASK     0x20000000
#define DATA_MASK      0x04000000
#define GPIO_OUT_ADDR  0x80000004

#define CS_MASK        0x00010000
#define RS_MASK        0x00001000
#define GPIO1_OUT_ADDR 0x800000b4

    .extern cpu_frequency       /* Global variable from system.c */

    .section    .icode,"ax",@progbits

    /* Output 8 bits to the LCD. Instruction order is devised to maximize the
     * delay between changing the data line and the CLK L->H transition, which
     * makes the LCD controller sample DATA.
     * Requires CLK = 1 on entry.
     *
     * Custom calling convention:
     *   %a0 - GPIO_OUT_ADDR
     *   %d3 - data byte
     *   %d6 - DATA_MASK
     *   %d7 - CLOCK_MASK
     * Clobbers:
     *   %d0..%d3 
     */
.write_byte:
    move.w  %sr, %d2
    move.w  #0x2700, %sr

    move.l  (%a0), %d0          /* Get current state of data port */
    move.l  %d0, %d1
    and.l   %d6, %d1            /* Check current state of data line */
    beq.s   1f                  /*   and set it as previous-state bit */
    bset    #8, %d3
1:
    move.l  %d3, %d1            /* Compute the 'bit derivative', i.e. a value */
    lsr.l   #1, %d1             /*   with 1's where the data changes from the */
    eor.l   %d1, %d3            /*   previous state, and 0's where it doesn't */
    swap    %d3                 /* Shift data to upper byte */
    lsl.l   #8, %d3

    move.l  %d0, %d1            /* precalculate opposite state of clock line */
    eor.l   %d7, %d1

    lsl.l   #1, %d3             /* Shift out MSB */
    bcc.s   1f
    eor.l   %d6, %d0            /* 1: Flip data bit */
    eor.l   %d6, %d1
1:
    move.l  %d1, (%a0)          /* Output new state and set CLK = 0*/
    bra.w   .wr_bit7


    /* Output 16 bits to the LCD. Instruction order is devised to maximize the
     * delay between changing the data line and the CLK L->H transition, which
     * makes the LCD controller sample DATA.
     * Requires CLK = 1 on entry.
     *
     * Custom calling convention:
     *   %a0 - GPIO_OUT_ADDR
     *   %d3 - data word
     *   %d6 - DATA_MASK
     *   %d7 - CLOCK_MASK
     * Clobbers:
     *   %d0..%d3 
     */
.write_word:
    move.w  %sr, %d2
    move.w  #0x2700, %sr

    move.l  (%a0), %d0          /* Get current state of data port */
    move.l  %d0, %d1
    and.l   %d6, %d1            /* Check current state of data line */
    beq.s   1f                  /*   and set it as previous-state bit */
    bset    #16, %d3
1:
    move.l  %d3, %d1            /* Compute the 'bit derivative', i.e. a value */
    lsr.l   #1, %d1             /*   with 1's where the data changes from the */
    eor.l   %d1, %d3            /*   previous state, and 0's where it doesn't */
    swap    %d3                 /* Shift data to upper word */

    move.l  %d0, %d1            /* precalculate opposite state of clock line */
    eor.l   %d7, %d1

    lsl.l   #1, %d3             /* Shift out MSB */
    bcc.s   1f
    eor.l   %d6, %d0            /* 1: Flip data bit */
    eor.l   %d6, %d1
1:
    move.l  %d1, (%a0)          /* Output new state and set CLK = 0*/

.macro bit_out
    move.l  %d0, (%a0)          /* Set CLK = 1 */
    lsl.l   #1, %d3
    bcc.s   1f
    eor.l   %d6, %d0
    eor.l   %d6, %d1
1:
    move.l  %d1, (%a0)
.endm

    nop
    nop
    bit_out
    nop
    nop
    bit_out
    nop
    nop
    bit_out
    nop
    nop
    bit_out
    nop
    nop
    bit_out
    nop
    nop
    bit_out
    nop
    nop
    bit_out
    nop
    nop
    bit_out

    nop
.wr_bit7:
    nop
    bit_out
    nop
    nop
    bit_out
    nop
    nop
    bit_out
    nop
    nop
    bit_out
    nop
    nop
    bit_out
    nop
    nop
    bit_out
    nop
    nop
    bit_out
    nop
    nop

    move.l  %d0, (%a0)          /* Set CLK = 1 */
    move.w  %d2, %sr
    rts


    /* Output 16 bits to the LCD as fast as possible. Use only at < 60MHz.
     *
     * Custom calling convention:
     *   %a0 - GPIO_OUT_ADDR
     *   %d3 - data word
     *   %d6 - DATA_MASK
     *   %d7 - CLOCK_MASK
     * Clobbers:
     *   %d0..%d3 
     */
.write_word_fast:
    move.w  %sr, %d2            /* Get current interrupt level */
    move.w  #0x2700, %sr        /* Disable interrupts */

    move.l  (%a0), %d0          /* Get current state of data port */
    move.l  %d0, %d1
    and.l   %d6, %d1            /* Check current state of data line */
    beq.s   1f                  /*   and set it as previous-state bit */
    bset    #16, %d3
1:
    move.l  %d3, %d1            /* Compute the 'bit derivative', i.e. a value */
    lsr.l   #1, %d1             /*   with 1's where the data changes from the */
    eor.l   %d1, %d3            /*   previous state, and 0's where it doesn't */
    swap    %d3                 /* Shift data to upper byte */

    move.l  %d0, %d1            /* precalculate opposite state of clock line */
    eor.l   %d7, %d1
    
.macro bit_out_fast
    lsl.l   #1,%d3              /* Shift out MSB */
    bcc.s   1f                  
    eor.l   %d6, %d0            /* 1: Flip data bit */
    eor.l   %d6, %d1            /*   for both clock states */
1:
    move.l  %d1, (%a0)          /* Output new state and set CLK = 0*/
    move.l  %d0, (%a0)          /* set CLK = 1 */
.endm

    bit_out_fast
    bit_out_fast
    bit_out_fast
    bit_out_fast
    bit_out_fast
    bit_out_fast
    bit_out_fast
    bit_out_fast
    bit_out_fast
    bit_out_fast
    bit_out_fast
    bit_out_fast
    bit_out_fast
    bit_out_fast
    bit_out_fast
    bit_out_fast

    move.w  %d2, %sr            /* Restore interrupt level */
    rts


    .global lcd_write_command
    .type   lcd_write_command, @function

lcd_write_command:
    lea.l   (-4*4, %sp), %sp
    movem.l %d2-%d3/%d6-%d7, (%sp)
    
    move.l  (4*4+4, %sp), %d3   /* cmd */

    lea.l   GPIO_OUT_ADDR, %a0
    lea.l   GPIO1_OUT_ADDR, %a1
    move.l  #DATA_MASK, %d6
    move.l  #CLOCK_MASK, %d7

    move.l  #~(RS_MASK+CS_MASK), %d0
    and.l   %d0, (%a1)

    bsr.w   .write_byte

    move.l  #CS_MASK, %d0
    or.l    %d0, (%a1)
    
    movem.l (%sp), %d2-%d3/%d6-%d7
    lea.l   (4*4, %sp), %sp
    rts


    .global lcd_write_command_e
    .type   lcd_write_command_e, @function

lcd_write_command_e:
    lea.l   (-4*4, %sp), %sp
    movem.l %d2-%d3/%d6-%d7, (%sp)
    
    movem.l (4*4+4, %sp), %d2-%d3   /* cmd, data */

    lea.l   GPIO_OUT_ADDR, %a0
    lea.l   GPIO1_OUT_ADDR, %a1
    move.l  #DATA_MASK, %d6
    move.l  #CLOCK_MASK, %d7

    move.l  #~(RS_MASK+CS_MASK), %d0
    and.l   %d0, (%a1)

    lsl.l   #8, %d2
    or.l    %d2, %d3
    bsr.w   .write_word

    move.l  #CS_MASK, %d0
    or.l    %d0, (%a1)
    
    movem.l (%sp), %d2-%d3/%d6-%d7
    lea.l   (4*4, %sp), %sp
    rts


    .global lcd_write_data
    .type   lcd_write_data, @function

lcd_write_data:
    lea.l   (-7*4, %sp), %sp
    movem.l %d2-%d4/%d6-%d7/%a2-%a3, (%sp)
    
    move.l  (7*4+4, %sp), %a2   /* p_words */
    move.l  (7*4+8, %sp), %d4   /* count */

    lea.l   GPIO_OUT_ADDR, %a0
    lea.l   GPIO1_OUT_ADDR, %a1
    move.l  #DATA_MASK, %d6
    move.l  #CLOCK_MASK, %d7

    lea.l   .write_word, %a3
    move.l  cpu_frequency, %d0
    cmp.l   #60000000, %d0
    bhi.b   1f
    lea.l   .write_word_fast, %a3
1:

    move.l  #RS_MASK, %d0
    or.l    %d0, (%a1)
    move.l  #~CS_MASK, %d0
    and.l   %d0, (%a1)

.wd_loop:                
    clr.l   %d3
    move.w  (%a2)+, %d3
    jsr     (%a3)
    subq.l  #1, %d4
    bne.s   .wd_loop

    move.l  #CS_MASK, %d0
    or.l    %d0, (%a1)
    
    movem.l (%sp), %d2-%d4/%d6-%d7/%a2-%a3
    lea.l   (7*4, %sp), %sp
    rts

    
/*** The following functions are only needed for main LCDs ***/


    .global lcd_mono_data
    .type   lcd_mono_data, @function

lcd_mono_data:
    lea.l   (-7*4, %sp), %sp
    movem.l %d2-%d4/%d6-%d7/%a2-%a3, (%sp)
    
    move.l  (7*4+4, %sp), %a2   /* p_bytes */
    move.l  (7*4+8, %sp), %d4   /* count */

    lea.l   GPIO_OUT_ADDR, %a0
    lea.l   GPIO1_OUT_ADDR, %a1
    move.l  #DATA_MASK, %d6
    move.l  #CLOCK_MASK, %d7

    lea.l   .write_word, %a3
    move.l  cpu_frequency, %d0
    cmp.l   #60000000, %d0
    bhi.b   1f
    lea.l   .write_word_fast, %a3
1:

    move.l  #RS_MASK, %d0
    or.l    %d0, (%a1)
    move.l  #~CS_MASK, %d0
    and.l   %d0, (%a1)

.md_loop:
    clr.l   %d3
    move.b  (%a2)+, %d3
    move.l  %d3, %d2
    lsl.l   #8, %d2
    or.l    %d2, %d3
    jsr     (%a3)
    subq.l  #1, %d4
    bne.s   .md_loop

    move.l  #CS_MASK, %d0
    or.l    %d0, (%a1)

    movem.l (%sp), %d2-%d4/%d6-%d7/%a2-%a3
    lea.l   (7*4, %sp), %sp
    rts


    .global     lcd_grey_data
    .type       lcd_grey_data,@function

lcd_grey_data:
    lea.l   (-9*4, %sp), %sp
    movem.l %d2-%d7/%a2-%a4, (%sp)
    
    movem.l (9*4+4, %sp), %a2-%a4   /* values, phases, length */
    add.l   %a4, %a4
    lea.l   (%a3, %a4.l*4), %a4     /* end address */
    
    lea.l   GPIO_OUT_ADDR, %a0
    lea.l   GPIO1_OUT_ADDR, %a1
    move.l  #DATA_MASK, %d6
    move.l  #CLOCK_MASK, %d7

    move.l  #RS_MASK, %d0
    or.l    %d0, (%a1)
    move.l  #~CS_MASK, %d0
    and.l   %d0, (%a1)
    
    clr.l   %d5
    move.l  (%a3), %d4          /* fetch 4 pixel phases */
    bclr.l  #31, %d4            /* Z = !(p0 & 0x80); p0 &= ~0x80; */
    seq.b   %d5                 /* %d5 = ........................00000000 */
    lsl.l   #1, %d5             /* %d5 = .......................00000000. */
    bclr.l  #23, %d4            /* Z = !(p1 & 0x80); p1 &= ~0x80; */
    seq.b   %d5                 /* %d5 = .......................011111111 */
    lsl.l   #1, %d5             /* %d5 = ......................011111111. */
    bclr.l  #15, %d4            /* Z = !(p2 & 0x80); p2 &= ~0x80; */
    seq.b   %d5                 /* %d5 = ......................0122222222 */
    lsl.l   #1, %d5             /* %d5 = .....................0122222222. */
    bclr.l  #7, %d4             /* Z = !(p3 & 0x80); p3 &= ~0x80; */
    seq.b   %d5                 /* %d5 = .....................01233333333 */
    lsl.l   #1, %d5             /* %d5 = ....................01233333333. */
    add.l   (%a2)+, %d4         /* add 4 pixel values to the phases */
    move.l  %d4, (%a3)+         /* store new phases, advance pointer */

    move.l  (%a3), %d4          /* fetch 4 pixel phases */
    bclr.l  #31, %d4            /* Z = !(p0 & 0x80); p0 &= ~0x80; */
    seq.b   %d5                 /* %d5 = ....................012344444444 */
    lsl.l   #1, %d5             /* %d5 = ...................012344444444. */
    bclr.l  #23, %d4            /* Z = !(p1 & 0x80); p1 &= ~0x80; */
    seq.b   %d5                 /* %d5 = ...................0123455555555 */
    lsl.l   #1, %d5             /* %d5 = ..................0123455555555. */
    bclr.l  #15, %d4            /* Z = !(p2 & 0x80); p2 &= ~0x80; */
    seq.b   %d5                 /* %d5 = ..................01234566666666 */
    lsl.l   #1, %d5             /* %d5 = .................01234566666666. */
    bclr.l  #7, %d4             /* Z = !(p3 & 0x80); p3 &= ~0x80; */
    seq.b   %d5                 /* %d5 = .................012345677777777 */
    lsr.l   #7, %d5             /* %d5 = ........................01234567 */
    add.l   (%a2)+, %d4         /* add 4 pixel values to the phases */
    move.l  %d4, (%a3)+         /* store new phases, advance pointer */
    
    move.l  %d5, %d3
    lsl.l   #8, %d3
    or.l    %d5, %d3
    
    cmp.l   %a3, %a4
    bls.w   .gd_last

.gd_loop:
    move.w  %sr, %d2
    move.w  #0x2700, %sr

    move.l  (%a0), %d0          /* Get current state of data port */
    move.l  %d0, %d1
    and.l   %d6, %d1            /* Check current state of data line */
    beq.s   1f                  /*   and set it as previous-state bit */
    bset    #16, %d3
1:
    move.l  %d3, %d1            /* Compute the 'bit derivative', i.e. a value */
    lsr.l   #1, %d1             /*   with 1's where the data changes from the */
    eor.l   %d1, %d3            /*   previous state, and 0's where it doesn't */
    swap    %d3                 /* Shift data to upper word */

    move.l  %d0, %d1            /* precalculate opposite state of clock line */
    eor.l   %d7, %d1

    lsl.l   #1, %d3             /* Shift out MSB */
    bcc.s   1f
    eor.l   %d6, %d0            /* 1: Flip data bit */
    eor.l   %d6, %d1
1:
    move.l  %d1, (%a0)          /* Output new state and set CLK = 0*/

    move.l  (%a3), %d4          /* fetch 4 pixel phases */
    bit_out
    bclr.l  #31, %d4            /* Z = !(p0 & 0x80); p0 &= ~0x80; */
    seq.b   %d5                 /* %d5 = ........................00000000 */
    lsl.l   #1, %d5             /* %d5 = .......................00000000. */
    trapf
    trapf
    bit_out
    bclr.l  #23, %d4            /* Z = !(p1 & 0x80); p1 &= ~0x80; */
    seq.b   %d5                 /* %d5 = .......................011111111 */
    lsl.l   #1, %d5             /* %d5 = ......................011111111. */
    trapf
    trapf
    bit_out
    bclr.l  #15, %d4            /* Z = !(p2 & 0x80); p2 &= ~0x80; */
    seq.b   %d5                 /* %d5 = ......................0122222222 */
    lsl.l   #1, %d5             /* %d5 = .....................0122222222. */
    trapf
    trapf
    bit_out
    bclr.l  #7, %d4             /* Z = !(p3 & 0x80); p3 &= ~0x80; */
    seq.b   %d5                 /* %d5 = .....................01233333333 */
    lsl.l   #1, %d5             /* %d5 = ....................01233333333. */
    trapf
    trapf
    bit_out
    add.l   (%a2)+, %d4         /* add 4 pixel values to the phases */
    bit_out
    move.l  %d4, (%a3)+         /* store new phases, advance pointer */

    bit_out
    move.l  (%a3), %d4          /* fetch 4 pixel phases */
    bit_out
    bclr.l  #31, %d4            /* Z = !(p0 & 0x80); p0 &= ~0x80; */
    seq.b   %d5                 /* %d5 = ....................012344444444 */
    lsl.l   #1, %d5             /* %d5 = ...................012344444444. */
    trapf
    trapf
    bit_out
    bclr.l  #23, %d4            /* Z = !(p1 & 0x80); p1 &= ~0x80; */
    seq.b   %d5                 /* %d5 = ...................0123455555555 */
    lsl.l   #1, %d5             /* %d5 = ..................0123455555555. */
    trapf
    trapf
    bit_out
    bclr.l  #15, %d4            /* Z = !(p2 & 0x80); p2 &= ~0x80; */
    seq.b   %d5                 /* %d5 = ..................01234566666666 */
    lsl.l   #1, %d5             /* %d5 = .................01234566666666. */
    trapf
    trapf
    bit_out
    bclr.l  #7, %d4             /* Z = !(p3 & 0x80); p3 &= ~0x80; */
    seq.b   %d5                 /* %d5 = .................012345677777777 */
    lsr.l   #7, %d5             /* %d5 = ........................01234567 */
    trapf
    trapf
    bit_out
    add.l   (%a2)+, %d4         /* add 4 pixel values to the phases */
    bit_out
    move.l  %d4, (%a3)+         /* store new phases, advance pointer */

    bit_out
    nop
    nop
    bit_out
    move.l  %d5, %d3
    lsl.l   #8, %d3
    or.l    %d5, %d3
    nop

    move.l  %d0, (%a0)          /* Set CLK = 1 */
    move.w  %d2, %sr

    cmp.l   %a3, %a4
    bhi.w   .gd_loop

.gd_last:
    bsr.w   .write_word

    move.l  #CS_MASK, %d0
    or.l    %d0, (%a1)

    movem.l (%sp), %d2-%d7/%a2-%a4
    lea.l   (9*4, %sp), %sp
    rts
