Display tricks to stretch the 8 CC limitation in 2x20 char LCD displays

Don't give up on the standard LCD 2x16 or 2x20 displays. About twenty years ago, I designed a set of LCD screens for a line of home theatre processors; however, through careful design, I was able to make it appear that there were more than eight custom chars. I have attached some of these below. The display driver automatically updated the CGRAM using custom fonts, which made coding the screen much easier! I flagged the custom font characters in a shadow register the same size as the string written to the LCD. The custom fonts included true descenders for lowercase, small caps, reverse caps and reverse small caps, bold, plus special custom char categories. The 'big nums' used eight custom chars to put up two large nums, followed by a normal ASCII decimal point and tenth, plus dB, e.g., 12.4 dB, where the 12 is two rows high. The big numbers volume readout (easy to read at a distance) and the 5.1-channel VU meter were the most dynamic of these screens.

Screenshot from 2024-05-29 17-40-17
This looks like 9 custom characters are used. 6 for the symbol with 7 circles and 3 for the dts logo. How did you achieve that?

This one too:
Screenshot from 2024-05-29 17-53-06

Screenshot from 2024-05-29 17-46-00
This looks like way more than 8 custom characters!

The rest, I can understand how 8 CC was enough.

EDIT: No, I see it now. The 7 circles symbol is always symetrical, so only 4 CCs are needed. Very good!

1 Like

Just to get it right. You have to store the bitmaps for the new characters in flash? You did not trick the display itself to use other characters?

Replying to PaulRB
Yes, that line is way more than 8! However, the complete set of 'big nums' is only for reference, as is the complete list of bar heights for the 6 dB resolution 5.1-channel VU meter. Everything else I showed is actual screen patterns. I am somewhat limited in my examples because I don't have a working example of the hardware. Indeed, as you have mentioned, symmetry and repetition are the keys to making pictures out of smaller graphical elements. None of the displays shown exceed eight custom characters. Indeed, the actual 'big nums' display, while you are changing the volume (using remote control vol up/down buttons), pops up a special screen with just the big nums volume, and when you stop changing the volume, the previous screen is restored. The process is fast enough that these screen popups and restores seem instantaneous. The big nums volume screen has something like this centered on the LCD: -26.5 dB
The 26 should be large (4 custom characters per digit), and the -, ., 5, d, and B are normal ASCII.

The custom characters driver automates writing a custom character bit map.

"Jay|"
"B L"  (where B means use Bold custom font for J, L lowercase descender font for y)

This is displayed as Jay

If the required custom character is already in the CGRAM, the driver uses it without further ado. If it isn't present, it searches for a CGRAM location not currently displayed and rewrites that custom character with the required one. To aid in debugging and testing various screens, the driver flags the following conditions, writing '*' for CGRAM overflow (maximum custom characters = 8); '@' for Font not found; '#' for Custom character not found in font, by writing these flag chars onto the LCD screen so that you can see the problem. The screenwriting driver does all of the housekeeping. Essentially, you have to give it the screen position (0-39 for a 2x20 LCD) and a pointer to the string.

The 6-channel VU meter uses a Bar font containing eight custom character positions in the CGRAM, with 16 variations (and no bar for zero signal) loaded dynamically, as needed. It can do this without any visual glitches while updating several times a second, converting the highest audio waveform amplitude reported by the DSP chip since the last query sent to the DSP, then converting to dB by scanning the resulting 16-bit value for the highest bit position The bars are two chars wide, with the inter-character gap hidden by arranging for a gap in the center of each bar custom char. E.g., character Bar3 (-30 dB) of this font contains the following binary pattern: %00000 %00000 %00000 %00000 %00000 %11011 %11011 %11011. This is displayed in the top row, while a Bar8 (consisting of %11011 repeated eight times) is displayed in the bottom row. A zero channel signal is displayed using an ASCII space char (no custom char required). I have a page that explains this in a little more detail, which I could post here if you are interested in seeing it.

1 Like

Replying to hmeijdam:
In my case, the bit maps (and program code) were stored in a 64 KiB EPROM. There are no CGRAM tricks that I know of, but the difference between manually manipulating CGRAM on the fly and having an automaton (a software driver) automate the process is night and day.

New edit:
Please let me know if anyone is interested in converting my 68HC11 assembly source to C for Arduino, which I would relish as my intro. into Arduino world. :slight_smile:

To clarify my code fragment, the 17-byte stack frame pushed onto the stack is allocated to the 8- and 16-bit local variables for the SCREENS subroutine. The equ lines (equ stands for equate) allow me to access variables in the stack frame without remembering the numerical offset.
E.g., with index register x already pointed to the stack frame, instead of writing
staa 13,x
to store a byte in the CGRAM byte pointer, I can write
staa CGRAM,x
which makes the assembler code almost readable and much easier to debug!
(This HLL-like method of storing local subroutine variables on the stack is detailed in the book "Single- and Multiple-Chip Microcomputer Interfacing" by G. J. Lipovski)

;****************************************
;*
;*  LCD Control Routines source file
;*
;*
;* Summary of subroutines:
;*
;*    screen_position      A=position (0-39)
;*    Section_clear        A=start position (0-39), B=end position (0-39), A < B
;*    LCD_config           Configure the LCD mode.
;*    LCD_init             Initialize display manager and clears screen.
;*    LCD_init0            Initialize display manager only.
;*    start_blinking       Uses blink positions in 'blink_posns'
;*    SCREENS              A=position (0-39), Y=pointer to formatted string.
;*    screen_out           Y=pointer to formatted 40-character string.
;*                         (Note: screen_out calls SCREENS twice, and also LCD_init.)
;*
;* Error characters:
;*
;*    *   ---   CGRAM overload (more than 8 custom characters)
;*    #   ---   Custom character not found.
;*    @   ---   Font not found.
;*
;*
;****************************************

<big snip>

;************************************************************************
;*
;*  SCREEN MANAGER
;*
;*  This routine loads relevant custom characters and displays a string.
;*  The N-character string must end with a '|' marker followed by N
;*  spaces or font characters.
;*
;*  Example string:
;*                      'Large LCD display|'
;*                      '   L  RRR    L  L'
;*
;*  There can be up to 40 display characters before the '|' character,
;*  with the corresponding font characters following, which is convenient
;*  when constructing a display string on the fly (e.g., the VU meter):
;* 
;*             'This very long string takes two lines!|'
;*             '        L    L      L                 '
;*
;*  Error messages:  '*'  CGRAM overflow! (maximum custom chars = 8)
;*                   '@'  Font not found!
;*                   '#'  Custom character not found in font!
;*
;*  Use as:      ldaa  #screen_posn (0-39)
;*               ldy   #string_ptr
;*               jsr   SCREENS
;*
;***

SCREENS:

;* Local variables allocated on the stack:
chars_ptr:      equ     0             ;Pointer to screen-data string
attrb_ptr:      equ     2             ;Pointer into attributes line
CC_id:          equ     4             ;Custom Char identifier (e.g. 'gL')
font_beg:       equ     6             ;Font beginning pointer
font_end:       equ     8             ;Font end pointer

len:            equ     10            ;String length
len2:           equ     11            ;String length copy
CC_space:       equ     12            ;Character number (0-7)
CGRAM:          equ     13            ;CGRAM pointer
curr_posn:      equ     14            ;Current position on screen
end_posn:       equ     15            ;String end position on screen
start_posn:     equ     16            ;String start position on screen
        
        pshx
        pshy
        tsx
        psha                           ;Save screen start position
        xgdx
        subd    #17                    ;Reserve stack space
        xgdx
        txs                            ;x points to local variables

        sty     chars_ptr,x            ;Store screen data pointer


;* Determine length of string to be output and set positions

        ldaa    start_posn,x           ;Get screen start position.
        staa    curr_posn,x            ;Initialize current position
        staa    end_posn,x             ; and end position
        clr     len,x
        clr     len2,x
string_len:
        inc     len,x                  ;Increment length of string
        inc     len2,x
        inc     end_posn,x             ;Increment end position
        iny                            ;Get new data
        ldaa    0,y
        cmpa    #'|'                   ;Check for end of string character
        bne     string_len

        ldaa    start_posn,x
        jsr     screen_position        ;Go to correct place on screen

;* Scan the section of the screen to be written over
;*   for custom characters and modify CC_table.

scan_image:
        ldy     #screen_image          ;Pointer into screen image
        ldab    start_posn,x
        aby                            ;Add start position
        ldab    len2,x
        decb
        aby                            ;Go to end of string
        ldab    0,y                    ;Get screen image character
        cmpb    #8
        blo     CC_table_update        ;Custom character?
inc_image_ptrs:
        dec     len2,x                 ;Update pointer
        bne     scan_image             ;Check if all done
        bra     write_string

CC_table_update:
        ldy     #CC_table_num          ;Point into CC occurrences
        aby                              
        dec     0,y                    ;Decrement occurrences
        bra     inc_image_ptrs

write_string:
        ldy     chars_ptr,x
        ldab    len,x
        incb
        aby
        sty     attrb_ptr,x            ;Set pointer into attributes line

write_loop:
        ldy     attrb_ptr,x            ;Get attribute in B
        ldab    0,y
        ldy     chars_ptr,x            ;Get character in A
        ldaa    0,y

        cmpa    #'|'                   ;Check for end of string
        beq     end_of_string
        cmpb    #$20                   ;Check if custom or not
        bne     do_custom
write_char:
        staa    CC_space,x             ;Save character

        pshx
        ldx     #regbase
        bclr    tmsk1,x,#%10001000     ;Disable OC1,OC5 interrupts
                                       ;while output is going on to avoid blink
                                       ;conflicts
        pulx

<another big snip>

The following equates may help this make sense:

;************************************************
;*
;*     Global Constants Include File
;*
;************************************************

       include "c:\1work\eos-i\build.i"


;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
;%%%%%%%%%%%%%%%%%%                                      %%%%%%%%%%%%%%%%%%
;%%%%%%%%%%%%%%%%%    G L O B A L    C O N S T A N T S    %%%%%%%%%%%%%%%%%
;%%%%%%%%%%%%%%%%%%                                      %%%%%%%%%%%%%%%%%%
;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

ram_end:         equ $03ff ;Top of 68HC11F1 RAM
ram_start:       equ $0000
regbase:         equ $1000

good for you

Encouraged by the number of people creating some character LCD version of Arduino space invaders (e.g., on YouTube), I am posting the following. I created it for the same 2x20 LCD mentioned, both as a test of the automatic LCD screen font loading discussed in this thread and as an easter egg paying tribute to the team who designed the home theater system alluded to in my post. I wrote using the same 68HC11 assembler mentioned. However, having just ordered an Arduino Uno kit, I plan to port some of these things to it. The entire Space Invaders animation sequence lasts about a minute, of which I have uploaded only three frames (from a VFD version which unlike the LCD screens I posted previously, was limited to 5x7 characters). Feel free to request a copy from me via email if you want a copy of the video. The entire sequence used 46 custom characters, but only eight at a time. I did not have time to code the explosion, but the custom characters are included. The original animation also has sound that is faithful to the original arcade game soundtrack.




Here are those custom characters:

;******************************************************************
;******************************************************************

Invader_out:

        dc.b     '0'
        dc.b     %01110
        dc.b     %10101
        dc.b     %01110
        dc.b     %10001
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000

        dc.b     '1'
        dc.b     %00000
        dc.b     %01110
        dc.b     %10101
        dc.b     %01110
        dc.b     %10001
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000

        dc.b     '2'
        dc.b     %00000
        dc.b     %00000
        dc.b     %01110
        dc.b     %10101
        dc.b     %01110
        dc.b     %10001
        dc.b     %00000
        dc.b     %00000

        dc.b     '3'
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %01110
        dc.b     %10101
        dc.b     %01110
        dc.b     %10001
        dc.b     %00000

        dc.b     '4'
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %01110
        dc.b     %10101
        dc.b     %01110
        dc.b     %10001

      
End_Invader_out:

;******************************************************************
;******************************************************************

Invader_in:

        dc.b     '0'
        dc.b     %01110
        dc.b     %10101
        dc.b     %01110
        dc.b     %01010
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000

        dc.b     '1'
        dc.b     %00000
        dc.b     %01110
        dc.b     %10101
        dc.b     %01110
        dc.b     %01010
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000

        dc.b     '2'
        dc.b     %00000
        dc.b     %00000
        dc.b     %01110
        dc.b     %10101
        dc.b     %01110
        dc.b     %01010
        dc.b     %00000
        dc.b     %00000
        
        dc.b     '3'
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %01110
        dc.b     %10101
        dc.b     %01110
        dc.b     %01010
        dc.b     %00000
        
        dc.b     '4'
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %01110
        dc.b     %10101
        dc.b     %01110
        dc.b     %01010
      
End_Invader_in:

;******************************************************************
;******************************************************************

Laser:

Laser0: dc.b     'a'
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %00100
        dc.b     %01110
        dc.b     %11111
        dc.b     %11111
     
Laser1: dc.b     'b'
        dc.b     %00000
        dc.b     %00000
        dc.b     %00100
        dc.b     %00100
        dc.b     %00100
        dc.b     %01110
        dc.b     %11111
        dc.b     %11111
      
Laser2: dc.b     'c'
        dc.b     %00100
        dc.b     %00100
        dc.b     %00100
        dc.b     %00000
        dc.b     %00100
        dc.b     %01110
        dc.b     %11111
        dc.b     %11111

Laser3: dc.b     'd'
        dc.b     %00100
        dc.b     %00100
        dc.b     %00000
        dc.b     %00000
        dc.b     %00100
        dc.b     %01110
        dc.b     %11111
        dc.b     %11111

Laser4: dc.b     'e'
        dc.b     %00100
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %00100
        dc.b     %01110
        dc.b     %11111
        dc.b     %11111


Explo1: dc.b     'p'
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %01110
        dc.b     %01110
        dc.b     %00000
      
Explo2: dc.b     'q'
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %10101
        dc.b     %01110
        dc.b     %01110
        dc.b     %10101
      
Explo3: dc.b     'r'
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %10001
        dc.b     %00100
        dc.b     %00100
        dc.b     %10001
      
Explo4: dc.b     's'
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %10001
        dc.b     %00000
        dc.b     %00000
        dc.b     %10001
      
Explo5: dc.b     't'
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000

Shield1:dc.b     'y'
        dc.b     %01111
        dc.b     %11111
        dc.b     %11100
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000

Shield2:dc.b     'z'
        dc.b     %11110
        dc.b     %11111
        dc.b     %00111
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000

End_Laser:

;******************************************************************
;******************************************************************

UFO:

M1a:    dc.b     'A'
        dc.b     %00000
        dc.b     %00011 
        dc.b     %00111 
        dc.b     %01101 
        dc.b     %11111 
        dc.b     %00111 
        dc.b     %00010 
        dc.b     %00000 

M1b:    dc.b     'B'
        dc.b     %11110 
        dc.b     %11111 
        dc.b     %11111 
        dc.b     %01101 
        dc.b     %11111 
        dc.b     %01100 
        dc.b     %00000 
        dc.b     %00000

M1c:    dc.b     'C'
        dc.b     %00000
        dc.b     %10000
        dc.b     %11000
        dc.b     %01100
        dc.b     %11110
        dc.b     %11000
        dc.b     %10000
        dc.b     %00000

M1d:    dc.b     'D'
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000


M2a:    dc.b     'I'   
        dc.b     %00000 
        dc.b     %00001
        dc.b     %00011
        dc.b     %00101
        dc.b     %01111
        dc.b     %00011
        dc.b     %00001
        dc.b     %00000 

M2b:    dc.b     'J'
        dc.b     %01111
        dc.b     %11111 
        dc.b     %11111 
        dc.b     %01101 
        dc.b     %11111 
        dc.b     %00110 
        dc.b     %00000 
        dc.b     %00000 

M2c:    dc.b     'K'
        dc.b     %00000
        dc.b     %11000
        dc.b     %11100
        dc.b     %01110 
        dc.b     %11111 
        dc.b     %11100  
        dc.b     %01000 
        dc.b     %00000  

M2d:    dc.b     'L'
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000


M3a:    dc.b     'Q'
        dc.b     %00000
        dc.b     %00000 
        dc.b     %00001 
        dc.b     %00011 
        dc.b     %00111 
        dc.b     %00001 
        dc.b     %00000 
        dc.b     %00000 

M3b:    dc.b     'R'
        dc.b     %00111
        dc.b     %11111 
        dc.b     %11111 
        dc.b     %01101 
        dc.b     %11111 
        dc.b     %10011 
        dc.b     %00000
        dc.b     %00000

M3c:    dc.b     'S'
        dc.b     %00000
        dc.b     %11100
        dc.b     %11110
        dc.b     %01101
        dc.b     %11111
        dc.b     %01110
        dc.b     %00100
        dc.b     %00000

M3d:    dc.b     'T'
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000


M4a:    dc.b     'a'
        dc.b     %00000 
        dc.b     %00000 
        dc.b     %00000 
        dc.b     %00001 
        dc.b     %00011 
        dc.b     %00000 
        dc.b     %00000 
        dc.b     %00000 
      
M4b:    dc.b     'b'
        dc.b     %00011 
        dc.b     %11111 
        dc.b     %11111 
        dc.b     %01101 
        dc.b     %11111 
        dc.b     %11001 
        dc.b     %10000 
        dc.b     %00000 

M4c:    dc.b     'c'
        dc.b     %10000 
        dc.b     %11110 
        dc.b     %11111 
        dc.b     %01101 
        dc.b     %11111 
        dc.b     %00111 
        dc.b     %00010 
        dc.b     %00000 

M4d:    dc.b     'd'
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %10000
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000


M5a:    dc.b     'i'
        dc.b     %00000 
        dc.b     %00000
        dc.b     %00000 
        dc.b     %00000 
        dc.b     %00001 
        dc.b     %00000 
        dc.b     %00000 
        dc.b     %00000 

M5b:    dc.b     'j'
        dc.b     %00001 
        dc.b     %01111 
        dc.b     %11111 
        dc.b     %01101 
        dc.b     %11111 
        dc.b     %11100 
        dc.b     %01000 
        dc.b     %00000 

M5c:    dc.b     'k'
        dc.b     %11000 
        dc.b     %11111 
        dc.b     %11111 
        dc.b     %01101 
        dc.b     %11111 
        dc.b     %10011 
        dc.b     %00001 
        dc.b     %00000 

M5d:    dc.b     'l'
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000
        dc.b     %10000
        dc.b     %11000
        dc.b     %00000
        dc.b     %00000
        dc.b     %00000


M6a:    dc.b     'q'
        dc.b     %00000 
        dc.b     %00000 
        dc.b     %00000 
        dc.b     %00000 
        dc.b     %00000 
        dc.b     %00000 
        dc.b     %00000 
        dc.b     %00000

M6b:    dc.b     'r'
        dc.b     %00000 
        dc.b     %00111 
        dc.b     %01111 
        dc.b     %11101 
        dc.b     %11111 
        dc.b     %01110 
        dc.b     %00100
        dc.b     %00000

M6c:    dc.b     's'
        dc.b     %11100 
        dc.b     %11111 
        dc.b     %11111 
        dc.b     %01101 
        dc.b     %11111 
        dc.b     %11001 
        dc.b     %00000 
        dc.b     %00000 

M6d:    dc.b     't'
        dc.b     %00000 
        dc.b     %00000 
        dc.b     %10000 
        dc.b     %01000 
        dc.b     %11100 
        dc.b     %10000 
        dc.b     %00000 
        dc.b     %00000

End_UFO:

Here is the entire 'Invaders' code mentioned previously (in the posting with three sample screen shots). Using a demo inspired by the Space Invaders arcade game demonstrates the simplicity of using an automated CGRAM custom font system to code character-based graphics (compared to the complexity that handling 46 custom characters would impose without automation). While this is written in 68HC11 assembler, the screen definition strings (i.e., the data structures) and font loading should be easy to follow. These parts will probably look the same in my port to the Arduino Uno.

;***************************************************************
;*      Inv.s
;*
;* Possible Improvements:
;*
;* Use local variable delay routine.
;* Allow invaders to be destroyed with the laser.
;* Accelerate the invaders with each invader destroyed.
;* Left-right movement of laser together with manual firing.
;* Keep the existing demo mode intact, but also have a playable mode.
;* Increment the score by 25s and 100s, as appropriate.
;* Decrement the number of spare laser bases with each one destroyed.
;*
;***************************************************************

        CLIST OFF       ;Only list assembled conditionals.
        MLIST OFF       ;Don't expand macros.

inv_flag:  equ     0

          XDEF  Invade

        include "c:\1work\eos-i\xrefs.i"
        include "c:\1work\eos-i\equates.i"
        include "c:\1work\eos-i\macros.i"


Inv_vbl:     Section 79

;%% Static buffer space in CPU RAM:
inv_line_out:   ds.b    23
inv_line_in:    ds.b    23
laser:          ds.b    3
explode:        ds.b    3
UFO:            ds.b    9


Invs:        Section 77


Invade: jsr     LCD_init

;%% Misc constants:
RH_side:                equ     13       ;Defines RHS of screen.
final_inv_position:     equ     24       ;Number of invader moves in each wave.
number_of_waves:        equ     2        ;Number of invasion waves.
High_score_frames:      equ     20       ;Determines duration of high score screen.

;%% Local variables dynamically allocated on stack:
LR_position:            equ     0        ;8-bit counter.
LR_flag:                equ     1        ;8-bit flag.
invaders:               equ     2        ;16-bit pointer.
run_counter:            equ     5        ;8-bit invader run counter
laser_position:         equ     6        ;8-bit position register
wave_counter:           equ     7        ;8-bit invasion wave counter

        tsx
        xgdx
        subd    #7          ;Allocate local variables on stack
        xgdx
        txs

splash_screen:
        jsr     splash
inv_init:
        jsr     LCD_init
        ldy     #Inv_screen
        jsr     screen_out
        delay   1500*10

        ldaa    #number_of_waves
        staa    wave_counter,x

new_wave:
        jsr     init_strgs
        ldy     #inv_line_out
        sty     invaders,x

        ldaa    #28
        staa    laser_position,x

        ldaa    #final_inv_position
        staa    run_counter,x

        ldaa    #1
        staa    LR_flag,x               ;Go right

        ldaa    #0                      ;Allows for 1 leading space on aliens.
        staa    LR_position,x           ;Start at LH end

;----------- MAIN LOOP ------------
inv_loop:
        ldaa    laser_position,x
        ldy     #laser
        jsr     SCREENS

        ldaa    LR_position,x
        ldy     invaders,x
        jsr     SCREENS

        ldaa    LR_flag,x
        cmpa    #1                      ;Determine direction...
        bne     .left

.right: inc     LR_position,x
        inc     LR_position,x
.left:  dec     LR_position,x
        ldaa    LR_position,x 

RH_check:
        cmpa    #6                      ;Reached RH end?
        bne     LH_check

        ldaa    #$ff                    ;Flag left move
        staa    LR_flag,x
        jsr     move_down
        bra     new_invs
                                        
LH_check:
        cmpa    #0                      ;Reached LH_end?
        bne     new_invs

        ldaa    #1                      ;Flag right move
        staa    LR_flag,x
        jsr     move_down
        
new_invs:
        ldy     invaders,x
        cpy     #inv_line_out
        bne     legs_out

legs_in:
        ldy     #inv_line_in
        bra     inv_line

legs_out:
        ldy     #inv_line_out
inv_line:
        sty     invaders,x
        jsr     tick  
        delay   800*10                  ;Initial speed is approx. 1.25 Hz.
        dec     run_counter,x           ;Have invaders reached final position?
        beq     final_stage             ;Yes...

fire_laser:
        ldy     #laser                  ;No, continue firing laser...
        ldaa    0,y
        cmpa    #'e'                    ;Last frame in animation sequence?
        blt     fire_next

reload_laser:
        ldaa    #'a'-1                  ;First frame in animation sequence.
        staa    0,y
fire_next:
        inc     0,y

        ldaa    0,y
        cmpa    #'b'                    ;Laser firing frame?
        bne     end_loop 

        jsr     chirp                   ;Sound for laser beam.
end_loop:
        jmp     inv_loop


;---------- FINAL STAGE ------------
final_stage:
        jsr     explode_laser

        delay   1000*10
        jsr     UFO_fly_by              ;Occurs at the end of each wave.

        dec     wave_counter,x
        beq     finals
        jmp     new_wave                ;Go get another invasion wave.


finals: delay   2000*10
        jsr     LCD_init

        ldaa    #High_score_frames
        staa    run_counter,x           ;Using as a loop counter here.

        ldy     #high_scores
        jsr     screen_out
        delay   400*10

score_loop:
        ldaa    #5                      ;"o" in Scores
        ldy     #high_scores_I
        jsr     SCREENS
        delay   400*10

        ldaa    #5                      ;"o" in Scores
        ldy     #high_scores_O
        jsr     SCREENS
        delay   400*10

        dec     run_counter,x
        bne     score_loop

        jsr     LCD_init
        delay   500*10
        jmp     splash_screen

;------------------------------------
;Note: The following return locks up the main EOS_com.s code.
Never:
        tsx
        xgdx
        addd    #7          ;Deallocate local stack variables.
        xgdx
        txs

        rts


;*******************************************************
;*
;* Move Down 
;*
;***
move_down:
        ldy     #inv_line_out
        ldaa    1,y
        cmpa    #4
        beq     move_end                ;If all the way down, do nothing

        ldab    #5                      ;Move 5 invaders down one row...
mve_loop:
        inc     1,y                     ;Do outies (skip leading space)
        inc     24,y                    ;Do innies (skip leading space)
        iny
        iny
        decb
        bne     mve_loop

move_end:
        rts


;*******************************************************
;*
;* Explode Laser
;*
;***
explode_laser:
        ldaa    laser_position,x        ;wipe out laser.
        tab
        jsr     Section_clear
        rts

;replace the above with this code:
        ldaa    #0
        ldab    #16   
        jsr     Section_clear           ;Clear top row of LCD except 000.

        delay   1000*10
        ldy     #laser+4                ;1st explosion frame.

explode_loop:
        ldaa    laser_position,x
        jsr     SCREENS

        ldaa    0,y
        cmpa    #'t'                    ;Last frame in animation sequence?
        beq     explode_exit

        inc     0,y
        delay   350*10                  ;Do explosion noise here later.
        bra     explode_loop

explode_exit:
        rts


;*******************************************************
;*
;* UFO Fly By 
;*
;***
UFO_fly_by:
        ldaa    #0
        ldab    #16 
        jsr     Section_clear           ;Clear top row of LCD except for 000.

        ldy     #laser                  ;Make sure main laser frame matches spares
        ldd     #'a|'                   ; during fly-by to conserve CGRAM.
        std     0,y
        ldaa    laser_position,x
        jsr     SCREENS
        
        ldaa    #0
        staa    LR_position,x

UFO_loop:
        jsr     chirp

        ldy     #UFO
        ldd     #'AB'
        std     0,y
        ldd     #'C '
        std     2,y
        stab    8,y                     ;Clear leading space font
        ldaa    #'U'
        staa    5,y                     ;Make trailing space UFO font
        ldaa    LR_position,x
        jsr     SCREENS
        delay   120*10

        jsr     chirp

        ldd     #'IJ'
        std     0,y
        ldd     #'KL'
        std     2,y
        ldaa    #'U'
        staa    8,y                     ;Make leading space UFO font
        ldaa    LR_position,x
        jsr     SCREENS
        delay   120*10

        jsr     chirp

        ldd     #'QR'
        std     0,y
        ldd     #'ST'
        std     2,y
        ldaa    LR_position,x
        jsr     SCREENS
        delay   120*10

        jsr     chirp

        ldd     #'ab'
        std     0,y
        ldd     #'cd'
        std     2,y
        ldaa    LR_position,x
        jsr     SCREENS
        delay   120*10

        jsr     chirp

        ldd     #'ij'
        std     0,y
        ldd     #'kl'
        std     2,y
        ldaa    LR_position,x
        jsr     SCREENS
        delay   120*10

        jsr     chirp

        ldd     #' r'
        std     0,y
        staa    5,y                     ;Clear trailing space
        ldd     #'st'
        std     2,y

        ldaa    LR_position,x
        jsr     SCREENS
        delay   120*10

        inc     LR_position,x
        ldaa    LR_position,x



        cmpa    #RH_side                ;Reached RH side?
        beq     UFO_exit
        jmp     UFO_loop

UFO_exit:
        ldaa    #0
        ldab    #16 
        jsr     Section_clear           ;Clear top row of LCD except for 000.
                                        ;(frees up some CGRAM for next wave).
        rts


;*******************************************************
;*
;* Sound when invaders move.
;*
;***
tick:   pshy
        pshx
        psha

        ldy     #2              ;Duration.
        ldaa    #100            ;Frequency.
        bra     .t1

;*******************************************************
;*
;* Sound when laser fires.
;*
;***
chirp:  pshy
        pshx
        psha
        ldy     #8              ;Chirp length.
.t1:    ldx     #regbase
	bset    porta,x,#%01000000
.t2:    deca
        bne     .t2  

        bclr    porta,x,#%01000000
        ldaa    #50             ;Chirp pitch.
.t3:    deca
        bne     .t3  

        dey
        bne     .t1    

        pula
        pulx
	puly

	rts                  ;** Return **
        

;*******************************************************
;*
;* Initialize string variables
;*
;*      inv_line_out:  ' 0 0 0 0 0 |'
;*                     ' O O O O O '
;*
;*      Inv_line_in:   ' 0 0 0 0 0 |'
;*                     ' I I I I I '
;*
;*      Laser:         'a|'
;*                     'P'
;*
;*      Explode:       'i|'
;*                     'P'
;*
;*      UFO:           'IJKL|', etc.
;*                     'UUUU'
;***
init_strgs:  
        ldy     #inv_line_out
        ldd     #' 0'
        std     0,y
        std     2,y
        std     4,y
        std     6,y
        std     8,y
        ldd     #' |'
        std     10,y
        ldd     #' O'
        std     12,y
        std     14,y
        std     16,y
        std     18,y
        std     20,y
        ldaa    #' '
        staa    22,y

        ldy     #inv_line_in
        ldd     #' 0'
        std     0,y
        std     2,y
        std     4,y
        std     6,y
        std     8,y
        ldd     #' |'
        std     10,y
        ldd     #' I'
        std     12,y
        std     14,y
        std     16,y
        std     18,y
        std     20,y
        ldaa    #' '
        staa    22,y

        ldy     #laser
        ldd     #'a|'
        std     0,y
        ldaa    #'P'
        staa    2,y

        ldy     #explode
        ldd     #'i|'
        std     0,y
        ldaa    #'P'
        staa    2,y

        ldy     #UFO
        ldd     #'IJ'
        std     0,y
        ldd     #'KL'
        std     2,y
        ldaa    #'|'
        staa    4,y
        ldd     #'UU'
        std     5,y
        std     7,y

        rts


;*******************************************************
;*
;* Put up splash screen, etc.
;*
;***
splash:
        ldy     #Inv_splash
        jsr     screen_out              ;(Includes LCD_init)
        delay   600*10

        ldaa    #25
        ldy     #score1
        jsr     SCREENS 
        jsr     tick
        delay   200*10

        ldaa    #25
        ldy     #score2
        jsr     SCREENS 
        jsr     tick
        delay   200*10

        ldaa    #25
        ldy     #score3
        jsr     SCREENS 
        jsr     tick
        delay   400*10

        ldaa    #34
        ldy     #score4
        jsr     SCREENS 
        jsr     tick
        delay   200*10

        ldaa    #34
        ldy     #score5
        jsr     SCREENS 
        jsr     tick
        delay   200*10

        ldaa    #34
        ldy     #score6
        jsr     SCREENS 
        jsr     tick
        delay   1500*10

        ldy     #Inv_get_ready
        jsr     screen_out              ;(Includes LCD_init)
        delay   3000*10
        rts


;-------------------------------------
Inv_splash:  
        dc.b     '   Space Invaders   |'   
        dc.b     '   BL    B          '
        dc.b     '   1-     ABC-      |'
        dc.b     '   I      UUU       '

Inv_get_ready:
        dc.b     '       GET       000|'   
        dc.b     '                    '
        dc.b     '      READY!     _aa|'
        dc.b     '                 SPP'

Inv_screen:  
        dc.b     '                 000|'   
        dc.b     '                    '
        dc.b     '  yz yz   yz yz  _aa|'
        dc.b     '  PP PP   PP PP  SPP'

high_scores:                  
        dc.b     'Hi-Sc3rers: AJR SCR |'              
        dc.b     'BB BBOBBBB          '
        dc.b     '            AR  JSH |'
        dc.b     '                    '


score1: dc.b     '2  |'       ;Position 25
        dc.b     '   '

score2: dc.b     '25 |'
        dc.b     '   '

score3: dc.b     '25 |'
        dc.b     '   '

score4: dc.b     '1  |'       ;Position 34
        dc.b     '   '

score5: dc.b     '10 |'
        dc.b     '   '

score6: dc.b     '100|'
        dc.b     '   '


high_scores_I:
        dc.b     '3|'              
        dc.b     'I'

high_scores_O:
        dc.b     '3|'              
        dc.b     'O'

;-------------------------------------

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.