; ; Divide et impera, Based on book by Ladislav Zajicek, 1986: ; Machine code Assembler Z80, page 11 ; ; This program divides number in interval 0-255 by number in interval 1-255 (devide by zero is not possible) ; In case that result is not whole number is necessary compute remainder. ; ; Another input condition is that that dividend can't be less than divisor. ; ; Dividend will have symbol 'ec', divisor is 'el'. ; ; Finally we must understadd that division is in reality substraction. So 31:5 is the same ; that 6x substraction 30-5 with remainder 1. ; .ORG 100H ; CP/M programs start address JP START ; Go to program start ; ; Variable storage space ; MSG_DIVIDEND .DB "Dividend: ", 0 MSG_DIVISOR .DB "Divisor: ", 0 MSG_RESULT .DB "Result: ", 0 MSG_EOL .DB 13, 10, 0 Stack1 .DW 0 ; Place to save old stack Sbot .DS 64 ; Temp stack for us to use ; ; Constants ; STOP .EQU $-1 ; Top of our stack BDOS .EQU 5 ; Address of BDOS entry ; ; CP/M BIOS stuff ; iobyte .EQU 03H ; Intel standard I/O definition byte const .EQU 0E606H ; 2 Console status conin .EQU 0E609H ; 3 Console INput conout .EQU 0E60CH ; 4 Console OUTput ; ; Peripheral I/O adresses ; LEDS .EQU 00H ; Digital I/O board PIO1_A_D .EQU 068H ; Z80 PIO #1 PIO1_A_C .EQU 06AH PIO1_B_D .EQU 069H PIO1_B_C .EQU 06BH PIO2_A_D .EQU 06CH ; Z80 PIO #2 PIO2_A_C .EQU 06EH PIO2_B_D .EQU 06DH PIO2_B_C .EQU 06FH PIO3_A_D .EQU 0F0H ; Z80 PIO #3 PIO3_A_C .EQU 0F2H PIO3_B_D .EQU 0F1H PIO3_B_C .EQU 0F3H PIO4_A_D .EQU 0F4H ; Z80 PIO #4 PIO4_A_C .EQU 0F6H PIO4_B_D .EQU 0F5H PIO4_B_C .EQU 0F7H ec .EQU 30 ; Dividend el .EQU 6 ; Divisor ; ; Start of code segment ; START: ; ------------------------------------------------------------ ; Starting sequence for CP/M programs LD HL, 0000H ; HL = 0 ADD HL, SP ; HL = SP LD (Stack1), HL ; Save original stack LD HL, STOP ; HL = address of new stack LD SP, HL ; Stack pointer = our stack ; ------------------------------------------------------------ ; Initialize Z80 PIO's LD A, 0FH ; Control word for PIO, set output mode OUT (PIO1_A_C), A ; PIO #1 port A OUT (PIO1_B_C), A ; PIO #1 port B OUT (PIO2_A_C), A ; PIO #2 port A OUT (PIO2_B_C), A ; PIO #2 port B OUT (PIO3_A_C), A ; PIO #3 port A OUT (PIO3_B_C), A ; PIO #3 port B OUT (PIO4_A_C), A ; PIO #4 port A OUT (PIO4_B_C), A ; PIO #4 port B ; ; Print dividend ; LD HL, MSG_DIVIDEND ; MSG_DIVIDEND HL = address of string CALL SEND_STR_CON ; Send string in (HL) to console LD C, ec ; Load dividend to C CALL PRINTC ; Print it LD HL, MSG_EOL ; MSG_EOL HL = address of string CALL SEND_STR_CON ; Send string in (HL) to console LD A, ec ; Load dividend to A OUT (PIO1_A_D), A ; Send dividend to PIO #1 port A ; ; Print divisor ; LD HL, MSG_DIVISOR ; MSG_DIVISOR HL = address of string CALL SEND_STR_CON ; Send string in (HL) to console LD C, el ; Load divisor to C CALL PRINTC ; Print it LD HL, MSG_EOL ; MSG_EOL HL = address of string CALL SEND_STR_CON ; Send string in (HL) to console LD A, el ; Load divisor to A OUT (PIO1_B_D), A ; Send divisor to PIO #1 port B ; ------------------------------------------------------------ ; Divide et impera LD C, 0 ; Reset C for storing result LD A, el ; Load divisor to Accumulator CP 00 ; Is divisor equals to zero ? JR Z, EXIT ; ... If yes go to EXIT LD B, A ; Move divisor to B LD A, ec ; Load divisor to A CP B ; Compare el with ec, is el > ec ? JR C, EXIT ; ec mustn't by less than el CP 00 ; Is ec == 0 ? If Yes - jump to address of ... JR Z, RESULT ; ... RESULT, C is nonzero SUBSTRACT: INC C ; Increment number of substraction SUB B ; Substract divisor from dividend JR Z, RESULT ; If all is done goto RESULT JR C, REMAINDER ; If result is negative, goto REMAINDER ... JR SUBSTRACT ; ... else continue with substracting ; REMAINDER: ; Remainder ; DEC C ; First correct "overflowed" result ADD A, B ; Find out remainder OUT (PIO2_B_D), A ; Send remainder to PIO #2 port B RESULT: ; ; Print result ; LD A, C LD C, LEDS OUT (C), A ; Send result to Didital I/O board OUT (PIO2_A_D), A ; Send result to PIO #2 port A LD C, A PUSH BC ; Save result in C LD HL, MSG_RESULT ; MSG_RESULT HL = address of string CALL SEND_STR_CON ; Send string in (HL) to console POP BC CALL PRINTC ; Print result in C LD HL, MSG_EOL ; MSG_EOL HL = address of string CALL SEND_STR_CON ; Send string in (HL) to console ; ------------------------------------------------------------ ; Finish and return to CP/M EXIT: LD HL, (Stack1) ; HL = entry stack address LD SP, HL ; SP = value on entry RET ;-------------------------------------------------------------------------------------- ;-------------------------------------------------------------------------------------- PRINTC: PUSH BC PUSH DE LD A, C 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, PRINTC3 LD C, A CALL conout ; Send char in C to console PRINTC3: ; 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_DIGIT2 LD A, B ; First digit will be tested for '0' CP '0' ; Don't bother printing leading '0' JR Z, PRINTC2 LD A, D ; Put back second digit to A PRINT_DIGIT2: LD C, A CALL conout ; Send char in C to console PRINTC2: LD A, E ; Print third digit in E LD C, A CALL conout ; Send char in C to console 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 ;-------------------------------------------------------------------------------------- ;-------------------------------------------------------------------------------------- ; DELAY: ; Routine to add a delay ; PUSH BC PUSH DE LD D, 0FFH DELAY1: LD B, 0FFH DELAY2: NOP DJNZ DELAY2 DEC D JR NZ, DELAY1 POP DE POP BC RET ;-------------------------------------------------------------------------------------- ; ; Send A to Digital I/O board and BC and DE to PIO's ; OUT_TO_LEDS: OUT (LEDS), A ; Send A to Digital I/O board PUSH AF LD A, B OUT (PIO1_A_D), A ; Send B to the PIO #1 port A LD A, C OUT (PIO1_B_D), A ; Send C to the PIO #1 port B LD A, D OUT (PIO2_A_D), A ; Send D to the PIO #2 port A LD A, E OUT (PIO2_B_D), A ; Send E to the PIO #2 port B 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 LD C, A CALL conout ; Send char in C to console INC HL ; Point to next char JR SEND_STR_CON ; Do next char ;-------------------------------------------------------------------------------------- .END