Z80 LED matrix Driver assembly source file
[Back]
; This code was written to test some LED matrix displays that were bought ; cheap at a radio rally. The cards have 240 LEDs in an 8x30 matrix driven ; by 8 row lines and two 4 bit decoders driving the 30 columns. (column 15 ; and column 31 = all LEDs off.) ; The displays were driven by the uPF IO/M Z80 PIO the address map of which ; is below. Change these for your own system. ; One interesting routine in this is rand_8 which generates a maximal length ; 8 bit pseudo random sequence. Read the comments to see how it works. ; uPF Address map MPortA: EQU 00h ;Main board 8255 port A MPortB: EQU 01h ;Main board 8255 port B MPortC: EQU 02h ;Main board 8255 port C MPPCNT: EQU 03h ;Main board 8255 control MCTC0: EQU 40h ;Main board CTC Channel 0 MCTC1: EQU 41h ;Main board CTC Channel 1 MCTC2: EQU 42h ;Main board CTC Channel 2 MCTC3: EQU 43h ;Main board CTC Channel 3 MPIODA: EQU 80h ;Main board PIO Port A Data MPIOCA: EQU 82h ;Main board PIO Port A Control MPIODB: EQU 81h ;Main board PIO Port B Data MPIOCB: EQU 83h ;Main board PIO Port B Control PIODA: EQU 68h ;IOM PIO Port A Data PIOCA: EQU 6Ah ;IOM PIO Port A Control PIODB: EQU 69h ;IOM PIO Port B Data PIOCB: EQU 6Bh ;IOM PIO Port B Control URTDA: EQU 60h ;IOM 8251 Data Port URTCNT: EQU 61h ;IOM 8251 Control Port CTC0: EQU 64h ;IOM CTC Channel 0 CTC1: EQU 65h ;IOM CTC Channel 1 CTC2: EQU 66h ;IOM CTC Channel 2 CTC3: EQU 67h ;IOM CTC Channel 3 DIPSW: EQU 6Ch ;IOM Dip SW PPortA: EQU 7Ch ;Programmer 8255 port A PPortB: EQU 7Dh ;Programmer 8255 port B PPortC: EQU 7Eh ;Programmer 8255 port C EPPCNT: EQU 7Fh ;Programmer 8255 control PPortD: EQU 70h ;Programmer Vpp select & control ORG 1800h LD A,#0Fh ; control word OUT (PIOCA),A ; port A o/p OUT (PIOCB),A ; port B o/p LD A,#0Fh ; set OFF address LD (pat_adr),A ; save it CALL byte_out ; out to display LD A,#0Ch ; set scans per call LD (s_count),A ; save it LD A,#29 ; scroll end byte LD (s_end),A ; save it LD A,#00h ; start from 0 LD (pat_adr),A ; save address LD (index),A ; save index byte LD (s_start),A ; clear scroll start LD (1+s_start),A ; clear scroll start (high byte) ; scrolling buffer main: LD HL,pattern ; point to bitmap ADD A,L ; add low LD L,A ; copy back LD A,#0 ; clear byte ADC A,H ; add high byte + carry LD H,A ; save back LD (pointer),HL ; save it CALL scan_disp ; go scan the display LD A,(index) ; get index INC A ; next LD (index),A ; save new JR NZ,main ; loop ; flash 'Reboot' 4 times slowly LD HL,d_buffer ; point to display buffer LD (pointer),HL ; save it LD HL,xpattern ; point to pattern LD DE,d_buffer ; point to buffer LD BC,30 ; 30 bytes to do LDIR ; copy block LD A,#80h ; scan count LD (s_count),A ; save it LD A,#04h ; flash count LD (d_count),A ; save it LD HL,d_buffer ; point to bitmap 'Reboot' CALL flash_it ; flash display LD A,#08h ; set scans per call LD (s_count),A ; save it LD A,#01h ; scroll direction (right) LD (s_dir),A ; save it CALL clear_dis ; scroll clear display ; scroll up test CALL b_clear ; clear buffer LD HL,p_up ; point to pattern LD DE,p_buffer ; point to buffer LD BC,30 ; 30 bytes to do LDIR ; copy block LD A,#18h ; set scans per call LD (s_count),A ; save it LD A,#40h ; scroll count LD (d_count),A ; save it LD A,#29 ; scroll end byte LD (s_end),A ; save it LD A,#0 ; start from 0 LD (s_start),A ; clear scroll start LD A,#01h ; scroll direction (up) LD (s_dir),A ; save it scroll_up: CALL scan_disp ; go scan the display CALL scroll ; go scroll display LD A,(d_count) ; get scroll count DEC A ; next LD (d_count),A ; save new JR NZ,scroll_up ; loop if not done ; scroll down test LD HL,p_down ; point to pattern LD DE,p_buffer ; point to buffer LD BC,30 ; 30 bytes to do LDIR ; copy block LD A,#40h ; scroll count LD (d_count),A ; save it LD A,#00h ; scroll direction (up) LD (s_dir),A ; save it scroll_dn: CALL scan_disp ; go scan the display CALL scroll ; go scroll display LD A,(d_count) ; get scroll count DEC A ; next LD (d_count),A ; save new JR NZ,scroll_dn ; loop if not done ; split scroll test LD HL,p_right ; point to pattern LD DE,p_buffer ; point to buffer LD BC,30 ; 30 bytes to do LDIR ; copy block LD A,#40h ; scroll count LD (d_count),A ; save it scroll_sp: CALL scan_disp ; go scan the display LD A,#14 ; scroll end byte LD (s_end),A ; save it LD A,#00h ; start from 0 LD (s_start),A ; clear scroll start LD (s_dir),A ; and scroll direction CALL scroll ; go scroll display LD A,#29 ; scroll end byte LD (s_end),A ; save it LD A,#15 ; start from 15 LD (s_start),A ; clear scroll start LD A,#01h ; scroll direction (up) LD (s_dir),A ; save it CALL scroll ; go scroll display LD A,(d_count) ; get scroll count DEC A ; next LD (d_count),A ; save new JR NZ,scroll_sp ; loop if not done ; scroll column at a time LD HL,p_numb ; point to pattern LD DE,p_buffer ; point to buffer LD BC,30 ; 30 bytes to do LDIR ; copy block LD A,#00h ; scroll direction (down) LD (s_dir),A ; save it CALL s_column ; scroll display 1 column at a time LD A,#80h ; scan count LD (s_count),A ; save it LD A,#04h ; flash count LD (d_count),A ; save it LD HL,d_buffer ; point to display buffer CALL flash_it ; flash display ; random # test ; don't just dump the word in, put it in p_buffer ..... LD HL,p_rand ; point to pattern LD DE,p_buffer ; point to buffer LD BC,30 ; 30 bytes to do LDIR ; copy block ; ..... and scroll it in LD A,#01h ; scroll direction (up) LD (s_dir),A ; save it CALL s_column ; scroll display 1 column at a time ; we now have 'Random' in the display LD A,#04h ; scans LD (b_count),A ; save it rand_loop: LD A,#04h ; set scans per call LD (s_count),A ; save it LD A,#F0h ; random count (240 LEDs to do) LD (d_count),A ; save it random: CALL rand_8 ; get random byte DEC A ; change 1-255 to 0-254 LD B,#0 ; clear B SRL A ; bit into carry RL B ; bit into B SRL A ; bit into carry RL B ; bit into B SRL A ; bit into carry RL B ; bit into B CP #30 ; compare with 30 JR NC,random ; skip if not within display ; A now has column #, B has bit # LD HL,d_buffer ; point to display ADD A,L ; add to pointer low LD L,A ; copy back to pointer LD A,#0 ; clear A ADC A,H ; add carry to H LD H,A ; back to pointer INC B ; bit is now 1-8 SCF ; set carry LD A,#0 ; clear A set_bit: RLA ; move bit DJNZ set_bit ; loop until in position XOR (HL) ; xor in byte LD (HL),A ; save into buffer CALL scan_disp ; go scan the display LD A,(d_count) ; get scan count DEC A ; next LD (d_count),A ; save new JR NZ,random ; loop if not done ; flash 'Random' 4 times slowly LD A,#80h ; scan count LD (s_count),A ; save it LD A,#04h ; flash count LD (d_count),A ; save it LD HL,d_buffer ; point to display buffer CALL flash_it ; flash display LD A,(b_count) ; get scroll count DEC A ; next LD (b_count),A ; save new JR NZ,rand_loop ; loop if not done LD A,#08h ; set scans per call LD (s_count),A ; save it LD A,#00h ; scroll direction (left) LD (s_dir),A ; save it CALL clear_dis ; scroll clear display LD A,(index) ; get index JP main ; loop ; end of main loop .. now the subroutines ; scroll clear display in direction (s_dir) ; left/right 0/1 clear_dis: LD A,#30 ; column count LD (b_count),A ; save it clear_lp: LD HL,(pointer) ; pointer to buffer LD BC,29 ; 29 bytes to do LD A,(s_dir) ; get direction BIT 0,A ; test direction bit JR Z,clear_l ; do scroll clear left ; this bit scrolls the display 1 column right LD DE,29 ; offset to buffer end ADD HL,DE ; add to pointer LD D,H ; copy high byte LD E,L ; copy low byte DEC HL ; -1 LDDR ; copy block JR clr_end ; go do end loop ; this bit scrolls the display 1 column left clear_l: LD D,H ; copy high byte LD E,L ; copy low byte INC HL ; +1 LDIR ; copy block clr_end: XOR A ; clear A LD (HL),A ; clear end byte CALL scan_disp ; go scan the display LD A,(b_count) ; get column count DEC A ; next LD (b_count),A ; save new JR NZ,clear_lp ; loop if not done RET ; scroll display down 1 column at a time in direction (s_dir) s_column: LD A,#04h ; scans LD (s_count),A ; save it LD A,#30 ; column count LD (b_count),A ; save it scroll_col: LD A,#08 ; bit count LD (d_count),A ; save it scroll_bit: CALL scan_disp ; go scan the display LD A,(b_count) ; get column count NEG ; make -ve ADD A,#30 ; add 30 (range 0-29) LD (s_end),A ; save as end LD (s_start),A ; save as start CALL scroll ; go scroll display LD A,(d_count) ; get bit count DEC A ; next LD (d_count),A ; save new JR NZ,scroll_bit ; loop if not done LD A,(b_count) ; get column count DEC A ; next LD (b_count),A ; save new JR NZ,scroll_col ; loop if not done RET ; done ; scroll display in direction (s_dir) from (s_start) to (s_end) inclusive. ; no error checking! scroll: LD HL,d_buffer ; point to display buffer LD DE,(s_start) ; get start # ADD HL,DE ; add to pointer LD DE,30 ; get bytes to p_buffer buffer EX DE,HL ; exchange pointer and distance ADD HL,DE ; add to give p_buffer pointer in HL LD A,(s_start) ; get start byte # LD B,A ; copy it LD A,(s_end) ; get end byte # INC A ; +1 to give total bytes to do SUB B ; subtract start gives bytes to do LD B,A ; copy to loop counter LD A,(s_dir) ; get direction byte BIT 0,A ; test up/down bit JR NZ,rotate_u ; do up if bit = 1 ; rotates B bytes one bit downward. uses d_buffer and p_buffer rotate_d: LD A,(HL) ; get byte from pattern SRL A ; rotate LD A,(DE) ; get byte from display RRA ; rotate carry in LD (DE),A ; save it back LD A,(HL) ; get byte from pattern back RRA ; rotate carry in LD (HL),A ; save back INC HL ; increment source INC DE ; increment destination DJNZ rotate_d ; loop if not done RET ; done ; rotates B bytes one bit upward. uses d_buffer and p_buffer rotate_u: LD A,(HL) ; get byte from pattern SLA A ; rotate carry out LD A,(DE) ; get byte from display RLA ; rotate carry in LD (DE),A ; save it LD A,(HL) ; get byte from pattern back RLA ; rotate carry in LD (HL),A ; save back INC HL ; increment source INC DE ; increment destination DJNZ rotate_u ; loop if not done RET ; done ; clears the pattern buffer b_clear: LD HL,d_buffer ; pointer to buffer LD D,H ; copy high byte LD E,L ; copy low byte INC DE ; +1 XOR A ; clear A LD (HL),A ; clear first byte LD BC,59 ; 59 bytes to do LDIR ; clear block RET ; done ; flash display. ; HL points to the massage to flash ; (d_count) is the number of flashes ; (s_count) is the number of scans per flash flash_it: LD (f_point),HL ; save flash pattern pointer flash: LD (pointer),HL ; save it CALL scan_disp ; go scan the display LD HL,ypattern ; point to bitmap ' ' LD (pointer),HL ; save it CALL scan_disp ; go scan the display LD HL,(f_point) ; point to flash pattern LD A,(d_count) ; get display count DEC A ; next LD (d_count),A ; save new JR NZ,flash ; loop if not done LD (pointer),HL ; save it CALL scan_disp ; go scan the display RET ; done ; scans the display (s_count) times using the bitamp at (pointer) scan_disp: LD A,(s_count) ; get count LD B,A ; copy to loop counter scan_p: LD HL,(pointer) ; set pointer to bitmap scan_it: CALL byte_out ; out to display INC HL ; next bitmap byte LD A,(pat_adr) ; get address INC A ; + 1 LD (pat_adr),A ; save it AND #0Fh ; mask low address CP #0Fh ; compare with OFF address JR NZ,scan_it ; skip if not xFh LD A,(pat_adr) ; get address INC A ; + 1 AND #1Fh ; mask address bits LD (pat_adr),A ; save it JR NZ,scan_it ; skip reset pointer if not at start DJNZ scan_p ; do it all again RET ; done ; outputs byte from (HL) to display column (pat_adr). ; the display is blanked before the byte is sent by setting the address ; to 0Fh which turns off all the column drivers. byte_out: LD A,#0Fh ; OFF address OUT (PIODB),A ; display off LD A,(HL) ; get byte OUT (PIODA),A ; byte to display LD A,(pat_adr) ; get address OUT (PIODB),A ; display on RET ; returns pseudo random 8 bit number in A. Only affects A. ; (r_seed) is the byte from which the number is generated and MUST be ; initialised to a non zero value or this function will always return ; zero. rand_8: LD A,(r_seed) ; get seed AND #B8h ; mask non feedback bits SCF ; set carry JP PO,no_clr ; skip clear if odd CCF ; complement carry (clear it) no_clr: LD A,(r_seed) ; get seed back RLA ; rotate carry into byte LD (r_seed),A ; save back for next prn RET ; done r_seed: DB 1 ; prng seed byte (must not be zero) s_start: DS 2 ; scroll start byte (0-29) s_end: DS 1 ; scroll end byte (0-29) s_dir: DS 1 ; scroll direction (0/1 = down/up) b_count: DS 1 ; display buffer size d_count: DS 1 ; display flash count s_count: DS 1 ; scans per call to scan_disp pat_adr: DS 1 ; pattern address index: DS 2 ; index offset pointer: DS 2 ; pointer into bitmap f_point: DS 2 ; pointer to flash pattern pattern: DB 00h,00h,00h,00h,00h,00h ;' ' DB 00h,00h,00h,00h,00h,00h ;' ' DB 00h,00h,00h,00h,00h,00h ;' ' DB 00h,00h,00h,00h,00h,00h ;' ' DB 00h,00h,00h,00h,00h,00h ;' ' DB 64h,92h,92h,92h,4Ch,00h ;'S' DB 30h,0Ah,0Ah,0Ah,3Ch,00h ;'y' DB 12h,2Ah,2Ah,2Ah,04h,00h ;'s' DB 20h,FCh,22h,02h,04h,00h ;'t' DB 1Ch,2Ah,2Ah,2Ah,18h,00h ;'e' DB 3Eh,20h,18h,20h,1Eh,00h ;'m' DB 00h,00h,00h,00h,00h,00h ;' ' DB 3Eh,10h,20h,20h,10h,00h ;'r' DB 1Ch,2Ah,2Ah,2Ah,18h,00h ;'e' DB 04h,2Ah,2Ah,2Ah,3Eh,00h ;'a' DB 1Ch,22h,22h,12h,FEh,00h ;'d' DB 30h,0Ah,0Ah,0Ah,3Ch,00h ;'y' DB 06h,06h,00h,00h,00h,00h ;'.' DB 00h,00h,00h,00h,00h,00h ;' ' DB FEh,FEh,06h,06h,06h,06h,00h ;'L' DB FEh,FEh,D6h,D6h,D6h,C6h,00h ;'E' DB FEh,FEh,C6h,C6h,7Ch,38h,00h ;'D' DB 00h,00h,00h,00h,00h,00h ;' ' DB 3Eh,20h,18h,20h,1Eh,00h ;'m' DB 3Ch,02h,02h,04h,3Eh,00h ;'u' DB 00h,82h,FEh,02h,00h,00h ;'l' DB 20h,FCh,22h,02h,04h,00h ;'t' DB 00h,22h,BEh,02h,00h,00h ;'i' DB 3Eh,28h,28h,28h,10h,00h ;'p' DB 00h,82h,FEh,02h,00h,00h ;'l' DB 1Ch,2Ah,2Ah,2Ah,18h,00h ;'e' DB 22h,14h,08h,14h,22h,00h ;'x' DB 1Ch,2Ah,2Ah,2Ah,18h,00h ;'e' DB 1Ch,22h,22h,12h,FEh,00h ;'d' DB 00h,00h,00h,00h,00h,00h ;' ' DB 3Eh,20h,18h,20h,1Eh,00h ;'m' DB 04h,2Ah,2Ah,2Ah,3Eh,00h ;'a' DB 20h,FCh,22h,02h,04h,00h ;'t' DB 3Eh,10h,20h,20h,10h,00h ;'r' DB 00h,22h,BEh,02h,00h,00h ;'i' DB 22h,14h,08h,14h,22h,00h ;'x' DB 06h,06h,00h,00h,00h,00h ;'.' ypattern: DB 00h,00h,00h,00h,00h,00h ;' ' DB 00h,00h,00h,00h,00h,00h ;' ' DB 00h,00h,00h,00h,00h,00h ;' ' DB 00h,00h,00h,00h,00h,00h ;' ' DB 00h,00h,00h,00h,00h,00h ;' ' DB 00h ; pad byte(s) p_right: DB 64h,92h,92h,92h,4Ch,00h ;'S' DB 3Eh,28h,28h,28h,10h,00h ;'p' DB 00h,82h,FEh,02h,00h,00h ;'l' DB 00h,22h,BEh,02h,00h,00h ;'i' DB 20h,FCh,22h,02h,04h,00h ;'t' xpattern: DB 00h,FEh,98h,94h,62h ;'R' DB 00h,1Ch,2Ah,2Ah,18h ;'e' DB 00h,FEh,12h,22h,1Ch ;'b' DB 00h,1Ch,22h,22h,1Ch ;'o' DB 00h,1Ch,22h,22h,1Ch ;'o' DB 20h,FCh,22h,04h,00h ;'t' p_up: DB 20h,40h,FEh,40h,20h ;' ' DB 00h,00h,00h,00h,00h ;' ' DB 00h,FCh,02h,02h,FCh ;'U' DB 00h,FEh,90h,90h,60h ;'P' DB 00h,00h,00h,00h,00h ;' ' DB 20h,40h,FEh,40h,20h ;' ' p_down: DB 08h,04h,FEh,04h,08h ;' ' DB 00h,FEh,82h,82h,7Ch ;'D' DB 00h,1Ch,22h,1Ch,00h ;'o' DB 3Ch,02h,0Ch,02h,3Ch ;'w' DB 00h,1Eh,20h,1Eh,00h ;'n' DB 08h,04h,FEh,04h,08h ;' ' p_rand: DB 00h,7Eh,58h,54h,22h ;'R' DB 04h,2Ah,2Ah,1Eh,00h ;'a' DB 0Eh,10h,0Eh,00h ;'n' DB 0Ch,12h,0Ah,7Eh,00h ;'d' DB 0Ch,12h,12h,0Ch,00h ;'o' DB 1Eh,10h,0Ch,10h,0Eh,00h ;'m' p_numb: DB 00h,22h,7Eh,02h,00h ;'1' DB 1Ch,2Ah,4Ah,04h,00h ;'6' DB 18h,28h,7Eh,08h,00h ;'4' DB 20h,52h,54h,38h,00h ;'9' DB 74h,52h,52h,4Ch,00h ;'5' DB 2Ch,52h,52h,2Ch,00h ;'8' d_buffer: DS 30 ; display part p_buffer: DS 30 ; +30 bytes for effects END