;======================================================================= ; FizzBuzz ; ; For RC2014's Bubble display module and CP/M ; Use ZAS HI-TECH SOFTWARE Z80 Macro Assembler that's part of HI-TECH C 3.09 ; ; zas -L -W140 fizzbuzz.as ; link -Z -C100H -Ofizzbuzz.com fizzbuzz.obj ; ;======================================================================= psect bss ORG 100H JP INIT ; Jump to entry point ;======================================================================= ; Config here MSG_FIZZ: DEFM 'Fizz' DEFB 0 MSG_BUZZ: DEFM 'Buzz' DEFB 0 DISPL_FIZZ: DEFB 113, 48, 91, 91 ; Fizz in display code DISPL_BUZZ: DEFB 124, 62, 91, 91 ; Buzz in display code DISPL_DIGS: DEFB 63, 6, 91, 79, 102, 109, 125, 7, 127, 111 ; Digits, 0 - 9 MSG_EOL: DEFB 13, 10, 0 ; COUNT3: DEFB 3 COUNT5: DEFB 5 FLAG: DEFB 0 ; 0 = print number, otherwise Fizz or Buzz found OFFSET: DEFB 0 ; For each digit is used by OUTing to DIGIT_SELECT_PORT ; and then rotated using RLCA for select next digit NUMBER: DEFB 0 ; Current number to display BDOS EQU 5 ; Address of BDOS entry ;======================================================================= ; Peripheral I/O adresses LEDS0 EQU 00H ; Digital I/O board #1 LEDS3 EQU 03H ; Digital I/O board #2 DIGIT_SELECT_PORT EQU 00 SEGMENT_PORT EQU 02 ;======================================================================= ; Entry point INIT: ; ------------------------------------------------------------ ; ------------------------------------------------------------ ; FizzBuzz LD B, 255 ; Loop from 255 to 1, used by DJNZ at end LD C, 1 ; Loop from 1 to 255 and test FizzBuzz conditions LOOP: XOR A ; LD A, 0 -> XOR A LD (FLAG), A ; Reset FLAG LD A, C ; Current number to A LD (NUMBER), A ; And to memory OUT (LEDS3), A ; Current number to LEDS3 PUSH BC LD A, 1 ; 1.st digit LD B, 8 ; Counter - 8 loops CLEAR: OUT(DIGIT_SELECT_PORT), A LD C, A LD A, 0 OUT(SEGMENT_PORT), A LD A, C RLCA CALL ACTIVATE DJNZ CLEAR POP BC ; ------------------------------------------------------------ ; Check for Fizz condition LD A, (COUNT3) DEC A JR NZ, NEXT3 PUSH BC LD HL, MSG_FIZZ ; MSG_FIZZ HL = address of string CALL SEND_STR_CON ; Send string in (HL) to console POP BC LD A, 0FH LD (FLAG), A ; Set FLAG to Fizz OUT (LEDS3), A ; Signalize Fizz LD A, 3 NEXT3: LD (COUNT3), A ; ------------------------------------------------------------ ; Check for Buzz condition LD A, (COUNT5) DEC A JR NZ, NEXT5 PUSH BC LD HL, MSG_BUZZ ; MSG_BUZZ HL = address of string CALL SEND_STR_CON ; Send string in (HL) to console POP BC LD A, (FLAG) OR 0F0H ; Set FLAG to Buzz LD (FLAG), A ; Set FLAG OUT (LEDS3), A ; Signalize Buzz LD A, 5 NEXT5: LD (COUNT5), A ; ------------------------------------------------------------ ; Test FLAG ; (i.e., if Fizz or Buzz has been printed) LD A, (FLAG) OR A ; CP 0 -> OR A, set CPU Flags JR NZ, FIZZBUZZ ; ------------------------------------------------------------ ; No Fizz or Buzz condition success, so print number CALL PRINT_NUM_CON FIZZBUZZ: PUSH BC LD HL, MSG_EOL ; MSG_EOL HL = address of string CALL SEND_STR_CON ; Send string in (HL) to console CALL BIG_DELAY ; Delay and update Bubble Display frequently POP BC IN A, (LEDS0) ; Read buttons on I/O board OR 0 ; Set Zero Flag JR NZ, EXIT ; If any button pressed, exit INC C ; Next number DJNZ LOOP JR INIT ; Main loop again EXIT: ; ------------------------------------------------------------ RET ;======================================================================= ; ; Routine to print NUMBER as ASCII decimal to console ; PRINT_NUM_CON: PUSH BC PUSH DE PUSH HL LD A, (NUMBER) CALL DTOA3D ; Split A register into B, D and E ; Print first digit in B LD A, B CP '0' ; Don't bother printing leading '0' JR Z, PRINT_NUMCON3 CALL CONOUT_A ; Send char in A to console PRINT_NUMCON3: ; Print second digit in D LD A, D ; Second digit will be tested for '0' CP '0' ; Don't bother printing leading '0' JR NZ, PRINT_DIGIT_NUMCON2 LD A, B ; First digit will be tested for '0' CP '0' ; Don't bother printing leading '0' JR Z, PRINT_NUMCON2 LD A, D ; Put back second digit to A PRINT_DIGIT_NUMCON2: CALL CONOUT_A ; Send char in A to console PRINT_NUMCON2: ; Print third digit in E LD A, E CALL CONOUT_A ; Send char in A to console POP HL POP DE POP BC RET ;======================================================================= ; ; Routine to print NUMBER as ASCII decimal to bubble display ; PRINT_NUM_DISPL: PUSH BC PUSH DE PUSH HL LD A, 1 ; 1.st digit LD (OFFSET), A ; Clear OFFSET LD A, (NUMBER) CALL DTOA3D ; Split A register into B, D and E ; Print first digit in B LD A, B CP '0' ; Don't bother printing leading '0' JR Z, PRINT_NUM_DISPL3 CALL A_TO_DISPL ; Put char in A to display PRINT_NUM_DISPL3: ; Print second digit in D LD A, D ; Second digit will be tested for '0' CP '0' ; Don't bother printing leading '0' JR NZ, PRINT_NUM_DISPL_DIGIT2 LD A, B ; First digit will be tested for '0' CP '0' ; Don't bother printing leading '0' JR Z, PRINT_NUM_DISPL2 LD A, D ; Put back second digit to A PRINT_NUM_DISPL_DIGIT2: CALL A_TO_DISPL ; Put char in A to display PRINT_NUM_DISPL2: ; Print third digit in E LD A, E CALL A_TO_DISPL ; Put char in A to display POP HL POP DE POP BC RET ;======================================================================= ; ; Binary to ASCII Decimal, in: A, out: BDE ; DTOA3D: LD B, '0' ; Starting from ASCII '0' DEC B ; Because we are inc'ing in the loop LD D, 100 ; Want base 100 please AND A ; Clear C Flag DTOA3DLOOP: INC B ; Increase the number of hundreads SUB D ; Take away one unit of hundread from A JR NC, DTOA3DLOOP ; If A still hasn't gone negative, do another ADD A, D ; Decreased it too much, put it back LD D, '0' ; Starting from ASCII '0' DEC D ; Because we are inc'ing in the loop LD E, 10 ; Want base 10 please AND A ; Clear C Flag DTOA2DLOOP: INC D ; Increase the number of tens SUB E ; Take away one unit of ten from A JR NC, DTOA2DLOOP ; If A still hasn't gone negative, do another ADD A, E ; Decreased it too much, put it back ADD A, '0' ; Convert to ASCII LD E, A ; Stick remainder in E RET ;======================================================================= BIG_DELAY: PUSH BC LD B, 015H BIG_DELAY1: CALL DELAY IN A, (LEDS0) ; Read buttons on I/O board OR 0 ; Set Zero Flag JR NZ, EXIT_DLY ; If any button pressed, exit DJNZ BIG_DELAY1 EXIT_DLY: POP BC RET ;======================================================================= ; DELAY: ; Routine to add a delay and update Bubble Display frequently ; PUSH BC LD B, 0FFH DELAY1: PUSH BC LD A, 1 ; 1.st digit LD (OFFSET), A ; Clear OFFSET LD A, (FLAG) AND 0FH CALL NZ, SAY_FIZZ ; Update Bubble Display - Fizz LD A, (FLAG) AND 0F0H CALL NZ, SAY_BUZZ ; Update Bubble Display - Buzz LD A, (FLAG) OR 0 ; Set Zero Flag CALL Z, PRINT_NUM_DISPL ; Update Bubble Display - actual number POP BC DJNZ DELAY1 POP BC RET ;======================================================================= SAY_FIZZ: LD A, 1 ; 1.st display segment LD HL, DISPL_FIZZ ; DISPL_FIZZ HL = address of string LD B, 4 FIZZ: OUT(DIGIT_SELECT_PORT), A LD C, A LD A, (HL) OUT(SEGMENT_PORT), A LD A, C RLCA ; Next display segment INC HL CALL ACTIVATE ; Relax ... DJNZ FIZZ ; Next char LD (OFFSET), A ; Save offset of display segment RET ;======================================================================= SAY_BUZZ: LD A, (OFFSET) ; Restore offset of display segment LD HL, DISPL_BUZZ ; DISPL_BUZZ HL = address of string LD B, 4 BUZZ: OUT(DIGIT_SELECT_PORT), A LD C, A LD A, (HL) OUT(SEGMENT_PORT), A LD A, C RLCA ; Next display segment INC HL CALL ACTIVATE ; Relax ... DJNZ BUZZ ; Next char RET ;======================================================================= ; ; Must be called after setting segments to SEGMENT_PORT ; Note that although the digits each have to be activated for a moment... ; ACTIVATE: PUSH AF PUSH BC LD B, 010H ACTIVATE1: NOP ; Do something useless DJNZ ACTIVATE1 XOR A ; LD A, 0 -> XOR A OUT(SEGMENT_PORT), A POP BC POP AF RET ;======================================================================= ; ; Send zero-terminated string in (HL) to console ; SEND_STR_CON: LD A, (HL) ; Read string char OR A ; Set CPU flags RET Z ; If char = 0 done CALL CONOUT_A ; Send char in A to console INC HL ; Point to next char JR SEND_STR_CON ; Do next char ;======================================================================= ; ; Send the character in A to the console ; CONOUT_A: PUSH AF PUSH BC PUSH DE PUSH HL LD E, A LD C, 2 ; BDOS function 2 (C_WRITE) - Console output CALL BDOS ; Call BDOS function - Entered with C=2, E=ASCII character POP HL POP DE POP BC POP AF RET ;======================================================================= ; ; Put the character 0 - 9 in A to display ; A_TO_DISPL: PUSH BC PUSH DE PUSH AF LD A, (OFFSET) ; Restore offset OUT(DIGIT_SELECT_PORT), A RLCA ; Next display segment LD (OFFSET), A POP AF SUB '0' ; ASCII to zero based index in DISPL_DIGS LD HL, DISPL_DIGS LD B, 0 LD C, A ADD HL, BC ; Compute addres of number in DISPL_DIGS LD A, (HL) ; Get number to A OUT(SEGMENT_PORT), A ; Display number CALL ACTIVATE ; Relax ... POP DE POP BC RET ;======================================================================= ;=======================================================================