;; Program for the RUDI-1 Z80 computer with 8k ROM, 8k SRAM and ;; 16C550 UART ;; Ver 1.5 uart_base equ 0x80 ; base adress of UART data_reg equ uart_base ; UART data register int_enable equ uart_base+1 ; UART interrupt enable register int_status equ uart_base+2 ; UART interrupt status register line_control equ uart_base+3 ; UART line control register modem_control equ uart_base+4 ; UART modem control register line_status equ uart_base+5 ; UART line status register modem_status equ uart_base+6 ; UART modem status register scratchpad equ uart_base+7 ; UART scratchpad register dle equ 2**7 ; divisor latch enable bit in lcr stop_bit equ 2**2 ; stop bit in lcr DTR equ 0 ; DTR control bit in modem_control RTS equ 1 ; RTS control bit in modem_control OUT1 equ 2 ; OUT1 control bit in modem_control OUT2 equ 3 ; OUT2 control bit in modem_control CTS equ 4 ; CTS bit in modem_status DSR equ 5 ; DSR bit in modem_status data_ready equ 0 ; "Receive Data Ready" bit in line_status trans_empty equ 6 ; "transmitter shift register empty" bit in line_status divisor_lsb equ uart_base+0 ; UART divisor LSB divisor_msb equ uart_base+1 ; UART divisor MSB ram_base equ 2**15 ; RAM base ram_size equ 8*1024 ; RAM size stack_size equ 1024 ; 1k stack size ascii_lf equ 10 ; line feed ascii_cr equ 13 ; carriage return ascii_tab equ 8 ; tabulator ascii_spc equ 32 ; space int_vec equ 0x38 ; interrupt service routine base address nmi_vec equ 0x66 ; NMI interrupt vector input_buffer equ ram_base ; input buffer buffer_size equ 1024 ; input buffer size prog_base equ ram_base+buffer_size ; base address for programs org 0000H ; reset vector, init system im 1 ; interrupt mode 1 di ; disable interrupts LD SP,ram_base+ram_size-1 ; init stack pointer ; LD SP,ram_base+1023 LD A,dle ; DLE on OUT (line_control),A ; activate divisor registers LD A,0CH ; 9600 baud OUT (divisor_lsb),A ; LSB of divisor LD A,0 ; MSB of divisor OUT (divisor_msb),A LD A,#00000011 ; 8 data bits, no parity, one stop bit (8N1) OUT (line_control),A ;; end of system initialisation LD A,2**DTR+2**OUT1 ; set DTR OUT (modem_control),A ;; RAM test LD BC,80 LD DE,ram_base LD HL,greeting LDIR ; LD BC,ram_base LD BC,greeting CALL print_string LD AF,0xAB JP main org int_vec isr: RETI org nmi_vec nmi: LD A,2**DTR+2**OUT2 ; set DTR OUT (modem_control),A RETN greeting defm "RUDI-1 Z80 microcomputer (8k ROM (Ver. 1.6), 8k SRAM, 16C550 UART) ready." defb ascii_lf,ascii_lf,ascii_cr,0 ;; subroutine: print a string ;; Uses EX AF,AF' ;; expects the base address of a zero-terminated string in BC print_string: EX AF,AF' wait_CTS: IN A,(modem_status) ; wait for CTS BIT CTS,A JR Z,wait_CTS LD A,(BC) CP 0 ; terminating zero? JR Z,string_end OUT (data_reg),A ; output byte wait_end: IN A,(line_status) ; poll for end of transmission BIT trans_empty,A JR Z,wait_end INC BC JP wait_CTS string_end: EX AF,AF' RET ;; subroutine: print a character ;; Uses EX AF,AF' ;; expects the character to print in A print_char: EX AF,AF' ; save A wait_CTS2: IN A,(modem_status) ; wait for CTS BIT CTS,A JR Z,wait_CTS2 EX AF,AF' ; load A OUT (data_reg),A ; output byte EX AF,AF' ; save A wait_end2: IN A,(line_status) ; poll for end of transmission BIT trans_empty,A JR Z,wait_end2 EX AF,AF' ; load A RET ;; subroutine: polls for an input character ;; Uses EX AF,AF' ;; returns the received character in A input_char: IN A,(modem_status) ; set RTS bit BIT RTS,A OUT (modem_status),A poll_uart: IN A,(line_status) ; poll receive data ready-bit BIT data_ready,A JR Z,poll_uart IN A,(data_reg) EX AF,AF' IN A,(modem_status) ; reset RTS bit RES RTS,A OUT (modem_status),A EX AF,AF' RET ;; Input: HL = number to convert, DE = location of ASCII string ;; Output: ASCII string at (DE) Num2Dec: PUSH BC PUSH AF CALL Num2DecI POP AF POP BC RET Num2DecI: ld bc,-10000 call Num1 ld bc,-1000 call Num1 ld bc,-100 call Num1 ld c,-10 call Num1 ld c,-1 Num1: ld a,'0'-1 Num2: inc a add hl,bc jr c,Num2 sbc hl,bc ld (de),a inc de ret ;; Input: HL = number to convert, DE = location of ASCII string ;; Output: ASCII string at (DE) Num2Hex: PUSH AF ld a,h call Num13 ld a,h call Num23 ld a,l call Num13 ld a,l jr Num23 Num13: rra rra rra rra Num23: or 0xF0 daa add a,0xA0 adc a,0x40 ld (de),a inc de POP AF ret ;; Input: HL = number to convert Print_Hex: push af push de ld a,h call Num21 ld a,h call Num22 ld a,l call Num21 ld a,l jr Num24 Num21: rra rra rra rra Num22: or 0xF0 daa add a,0xA0 adc a,0x40 call print_char inc de ret Num24: or 0xF0 daa add a,0xA0 adc a,0x40 call print_char pop de pop af ret ;; Converts a zero-terminated string to an unsigned 8 bit integer ;; Param: HL - pointer to a zero-terminated string ;; Return: 8 bit unsigned integer in A atoi8u: PUSH BC LD C,0 ; clear C atoi8u_loop: LD A,(HL) CP 0 ; end-of-string? JR Z,atoi8u_end SUB '0' JR C,atoi8u_end ; below '0' CP 10 JR NC,atoi8u_end ; greater than '9' LD B,A ; B = new_digit LD A,C ; A = old_number SLA A SLA A ; A = 4*old_number ADD A,C ; A = 5*old_number SLA A ; A = 10*old_number ADD A,B ; A = 10*old_number+new_digit LD C,A INC HL JR atoi8u_loop atoi8u_end: LD A,C POP BC RET ;; Routine for entering a string ;; Parameter: HL - address of string in memory ;; Return: end-of-string in HL input_string: PUSH AF inputs_loop: CALL input_char CALL print_char CP ascii_cr JR Z,inputs_end LD (HL),A INC HL JR inputs_loop inputs_end: LD (HL),0 ; terminate string POP AF RET ;; Routine for entering a string with a maximum length ;; Parameter: HL - address of string in memory ;; BC - maximal number of bytes ;; Return: end-of-string in HL input_stringl: PUSH AF inputsl_loop: CALL input_char CALL print_char CP ascii_cr JR Z,inputsl_end LD (HL),A INC HL DEC BC JR NZ,inputsl_loop inputsl_end: LD (HL),0 ; terminate string POP AF RET ;; prints the content of the accumulator as a binary string print_binary: PUSH BC LD B,8 pb_loop: RLCA LD C,A AND 0x1 ADD A,0x30 CALL print_char LD A,C DJNZ pb_loop POP BC RET ;; memory dump - Input: HL = address of data dump_mem2: push AF push BC xor A rld LD B,2 Nibble: LD C,A daa add a,0xF0 adc a,0x40 call print_char ; prints ASCII character in A LD A,C rld DJNZ Nibble POP BC POP AF RET dump_mem: PUSH AF PUSH BC LD A,(HL) LD B,2 dmpmem_l0: RLCA RLCA RLCA RLCA LD C,A AND 0x0F ADD A,'0' CP 10+'0' JR C,dmpmem_out ADD A,'A'-'0'-10 dmpmem_out: CALL print_char LD A,C DJNZ dmpmem_l0 dmpmem_end: POP BC POP AF RET ;; CRC check, calculates standard CRC-CCITT bit-by-bit using polynomial 1021h. ;; Input: DE = address of input data, C = number of bytes to process ;; Output: HL = CRC Crc16: ld hl,0xFFFF Read: ld a,(de) inc de xor h ld h,a ld b,8 CrcByte: add hl,hl jr nc,Next ld a,h xor 10h ld h,a ld a,l xor 21h ld l,a Next: djnz CrcByte dec c jr nz,Read ret ;; converts an 8 bit unsigned hexadecimal value given as a zero terminated ;; ASCII string by HL into a binary number returned in C. ;; Parameter: HL - poiner to a zero-terminated string ;; Returns: C - converted number ;; B - error code, 0 on success atohex8u: PUSH AF LD C,0 LD B,2 ; counter for 2 nibbles atohex8u_nextc: LD A,(HL) INC HL CP 0 JR Z,atohex8u_end ; end-of-string marker CP ' ' ; skip space JR Z,atohex8u_nextc CP 9 ; skip tabs JR Z,atohex8u_nextc CP 'a' JR C,test_hex_uc SUB 'a'-10 CP 16 JR NC,atohex8u_end ; not in a-f, skip conversion JP atohex8u_svr test_hex_uc: CP 'A' JR C,test_dec SUB 'A'-10 CP 16 JR NC,atohex8u_end ; not in a-f, skip conversion JP atohex8u_svr test_dec: SUB '0' CP 10 JR NC,atohex8u_end ; not in 0-9, skip conversion atohex8u_svr: OR C LD C,A DEC B JR Z,atohex8u_end ; finished SLA C SLA C SLA C SLA C JP atohex8u_nextc ; next character atohex8u_end: POP AF RET ;; Routine for inputting a decimal number ;; main routine main: LD BC,monitor_string CALL print_string CALL input_char CALL print_char ; local echo LD B,A ; save input string LD A,ascii_lf CALL print_char LD A,ascii_cr CALL print_char LD A,B CP 0x71 ; 'q' entered? JR Z,end CP 0x72 ; 'r' entered? CALL Z,dump_reg CP 0x31 ; '1' entered? CALL Z,toggle_1 CP 0x32 CALL Z,toggle_2 CP 'l' CALL Z,load_prog CP 'g' CALL Z,prog_base CP 'a' CALL Z,memdump_all CP 'd' CALL Z,memdump_given CP 'e' CALL Z,edit_mem CP 'b' CALL Z,loadBinProg JP main end: HALT dump_reg: PUSH AF PUSH BC PUSH DE PUSH HL PUSH IX PUSH IY LD IX,11 ADD IX,SP LD B,6 ; 6 registers to go LD IY,reg_strings reg_loop: LD A,(IY+0) CALL print_char INC IY LD A,(IY+0) ; CALL print_char INC IY LD A,0x3D ; '=' CALL print_char LD H,(IX+0) DEC IX LD L,(IX+0) DEC IX CALL Print_Hex LD A,B AND 1 JR NZ,dr_nl LD A,ascii_spc CALL print_char LD A,ascii_spc CALL print_char JR reg_loop_end dr_nl: LD A,ascii_lf ; line feed CALL print_char LD A,ascii_cr CALL print_char ; carriage return reg_loop_end: DJNZ reg_loop ;; output modem_control LD BC,mcr_string CALL print_string LD A,(modem_control) CALL print_binary LD A,66 CALL print_char LD BC,newline CALL print_string ;; output modem_status LD BC,msr_string CALL print_string LD A,(modem_status) CALL print_binary LD A,66 CALL print_char LD BC,newline CALL print_string ;; output line_control LD BC,lcr_string CALL print_string LD A,(line_control) CALL print_binary LD A,66 CALL print_char LD BC,newline CALL print_string ;; output modem_status LD BC,lsr_string CALL print_string LD A,(line_status) CALL print_binary LD A,66 CALL print_char LD BC,newline CALL print_string POP IY POP IX POP HL POP DE POP BC POP AF RET mcr_string: defm "modem_control: " defb 0 msr_string: defm "modem_status: " defb 0 lcr_string: defm "line control: " defb 0 lsr_string: defm "line status: " defb 0 newline: defb ascii_lf,ascii_cr,0 tab: defb ascii_tab,0 toggle_1: PUSH AF PUSH BC IN A,(modem_control) XOR 2**OUT1 OUT (modem_control),A LD BC,switch_msg CALL print_string LD A,0x31 CALL print_char LD BC,newline CALL print_string POP BC POP AF RET toggle_2: PUSH AF PUSH BC IN A,(modem_control) XOR 2**OUT2 OUT (modem_control),A LD BC,switch_msg CALL print_string LD A,0x32 CALL print_char LD BC,newline CALL print_string POP BC POP AF RET load_prog: PUSH HL PUSH AF PUSH BC PUSH DE LD BC,lprg_info CALL print_string LD HL,prog_base lp_input: CALL Print_hex LD A,':' CALL print_char LD A,' ' CALL print_char PUSH HL LD HL,input_buffer LD DE,buffer_size CALL input_stringl LD HL,input_buffer CALL atohex8u POP HL LD A,B CP 0 JR NZ,lp_end LD (HL),C INC HL JR lp_input lp_end: POP DE POP BC POP AF POP HL RET lprg_info: defm "Enter the program byte for byte as hex code, enter a non-hex digit to stop." defb ascii_lf,ascii_cr,0 edit_mem: PUSH BC PUSH HL PUSH DE PUSH AF LD BC,em_address CALL print_string LD HL,input_buffer LD DE,buffer_size CALL input_stringl LD HL,input_buffer CALL atohex8u LD A,B CP 0 JR NZ,em_error LD D,C CALL atohex8u LD A,B CP 0 JR NZ,em_error LD E,C PUSH DE LD BC,em_value CALL print_string LD HL,input_buffer LD DE,buffer_size CALL input_stringl LD HL,input_buffer CALL atohex8u LD A,B CP 0 JR NZ,em_error POP HL LD (HL),C JR em_end em_error: LD BC,conv_error CALL print_string em_end: POP AF POP DE POP HL POP BC RET em_address: defm "Address: " defb 0 em_value: defm "Value: " defb 0 ;; dumps the memory segment starting at HL and a length given by DE memdump: PUSH AF PUSH BC start_line: CALL Print_Hex LD A,0x3A ; ':' CALL print_char LD B,8 start_symb: LD A,0x20 ; ' ' CALL print_char CALL dump_mem INC HL DEC DE LD A,D CP 0 JR NZ,dmp_l0 LD A,E CP 0 JR NZ,dmp_l0 JR dump_end dmp_l0: DJNZ start_symb LD A,ascii_lf CALL print_char LD A,ascii_cr CALL print_char JP start_line dump_end: LD BC,newline CALL print_string POP BC POP AF RET enter_code: PUSH AF PUSH BC PUSH HL LD BC,rq_numbytes CALL print_string LD HL,ram_base CALL input_string LD BC,ram_base CALL print_string LD HL,ram_base CALL atoi8u ec_loop: LD BC,rq_numbytes CALL print_string LD BC,newline CALL print_string DEC A JR NZ,ec_loop LD BC,newline CALL print_string POP HL POP BC POP AF RET memdump_all: PUSH HL PUSH DE LD HL,ram_base LD DE,ram_size-1 CALL memdump POP DE POP HL RET memdump_given: PUSH AF PUSH HL PUSH DE PUSH BC LD BC,rq_baseaddr CALL print_string LD BC,buffer_size LD HL,input_buffer CALL input_stringl LD HL,input_buffer CALL atohex8u LD A,B CP 0 JR NZ,mdmp_error LD D,C CALL atohex8u LD A,B CP 0 JR Z,mdmp_goon mdmp_error: LD BC,conv_error CALL print_string JR mdmp_end mdmp_goon: LD H,D LD L,C LD DE,64 CALL memdump mdmp_end: POP BC POP DE POP HL POP AF RET ;; Routine zum Laden eines Datenblocks in den Programmspeicher ;; zuerst wird die Anzahl der Bytes als 16-Bit-Präfix (oberes ;; Halbwort zuerst) danach die Datenbytes ;; Parameter: DE - Basisadresse des Zielbereichs load_bin: PUSH AF PUSH BC PUSH HL IN A,(modem_status) ; set RTS bit BIT RTS,A OUT (modem_status),A ppoll_uart1: IN A,(line_status) ; poll receive data ready-bit BIT data_ready,A JR Z,ppoll_uart1 IN A,(data_reg) LD H,A ; lade Anzahl an Zeichen - oberes Halbwort ppoll_uart2: IN A,(line_status) ; poll receive data ready-bit BIT data_ready,A JR Z,ppoll_uart2 IN A,(data_reg) LD L,A ; lade Anzahl an Zeichen - unteres Halbwort LD BC,1 SCF CCF ppoll_uart3: IN A,(line_status) ; poll receive data ready-bit BIT data_ready,A JR Z,ppoll_uart3 IN A,(data_reg) LD (DE),A INC DE SBC HL,BC JR NZ,ppoll_uart3 IN A,(modem_status) ; reset RTS bit RES RTS,A OUT (modem_status),A POP HL POP BC POP AF RET loadBinProg: PUSH DE PUSH BC LD BC,explain_loadb CALL print_string LD DE,prog_base CALL load_bin POP BC POP DE RET ;; Multiplikation zweier vorzeichenloser 8-Bit-Zahlen ;; Multiplikant: H, Multiplator: L ;; Produkt: HL mul8u8u: PUSH DE LD E,L LD D,0 LD L,0 sla h ; optimised 1st iteration jr nc,@+3 ld l,e add hl,hl ; unroll 7 times jr nc,@+3 ; ... add hl,de ; ... add hl,hl ; unroll 7 times jr nc,@+3 ; ... add hl,de ; ... add hl,hl ; unroll 7 times jr nc,@+3 ; ... add hl,de ; ... add hl,hl ; unroll 7 times jr nc,@+3 ; ... add hl,de ; ... add hl,hl ; unroll 7 times jr nc,@+3 ; ... add hl,de ; ... add hl,hl ; unroll 7 times jr nc,@+3 ; ... add hl,de ; ... add hl,hl ; unroll 7 times jr nc,@+3 ; ... add hl,de ; ... POP DE RET ;; Division between two 8 Bit unsigned integers ;; Input: D = Dividend , E = Divisor ;; Output: D = Quotient , A = Remainder div8u8u: ld a,0 sla d ; unroll 8 times rla ; ... cp e ; ... jr c,@+4 ; ... sub e ; ... inc d ; ... sla d ; unroll 8 times rla ; ... cp e ; ... jr c,@+4 ; ... sub e ; ... inc d ; ... sla d ; unroll 8 times rla ; ... cp e ; ... jr c,@+4 ; ... sub e ; ... inc d ; ... sla d ; unroll 8 times rla ; ... cp e ; ... jr c,@+4 ; ... sub e ; ... inc d ; ... sla d ; unroll 8 times rla ; ... cp e ; ... jr c,@+4 ; ... sub e ; ... inc d ; ... sla d ; unroll 8 times rla ; ... cp e ; ... jr c,@+4 ; ... sub e ; ... inc d ; ... sla d ; unroll 8 times rla ; ... cp e ; ... jr c,@+4 ; ... sub e ; ... inc d ; ... sla d ; unroll 8 times rla ; ... cp e ; ... jr c,@+4 ; ... sub e ; ... inc d ; ... ret ;; Pseudozufallszahlengenerator ;; Argument: A - Seed ;; Rückgabe: Zufallszahl in A Rand8: push bc ld b,a add a,a add a,a add a,b inc a ; another possibility is ADD A,7 ld (Rand8+1),a pop bc ret reg_strings: defm "AFBCDEHLIXIY" monitor_string: defm "*****************************************" defb ascii_lf,ascii_cr defm "* Z80 monitor program *" defb ascii_lf,ascii_cr defm "*****************************************" defb ascii_lf,ascii_cr defm "* q - halt cpu | r - dump registers *" defb ascii_lf,ascii_cr defm "* d - dump memory | e - edit memory *" defb ascii_lf,ascii_cr defm "* l - load program | g - run program *" defb ascii_lf,ascii_cr defm "* a - dump all | b - load binary *" defb ascii_lf,ascii_cr defm "* 1 - toggle OUT1 | 2 - toggle OUT2 *" defb ascii_lf,ascii_cr defm "*****************************************" defb ascii_lf,ascii_cr defm "?> " defb 0 rq_numbytes: defm "How many bytes? >" defb 0 conv_error: defm "Number conversion error!" defb ascii_lf,ascii_cr,0 rq_baseaddr: defm "Base address as 16 bit hexadecimal: " defb 0 switch_msg: defm "Switch OUT" defb 0 explain_loadb: defm "Format: first word (high byte first) gives number of bytes to follow" defb ascii_lf,ascii_cr,0