; TEST1.180 This is the Z180 CPU test program for the S100Computers Z180 SBC Project. ; Assemble and SLR's Z180ASM Assembler ; ; The code is intended to run starting at location 0000H and ignores ; all page zero stuff. Interrupts are kept disabled. The resultant ; binary is intended to be programmed into a ROM that is addressed at ; CPU address 0000H on reset. The code assumes the CPU has been reset ; and makes no attempt to "reset" Z180 registers back to defaults. ; ; The code assumes a physical memory layout of 1MB. The first 512KB ; is assumed to be ROM and the second 512KB is assumed to be RAM. The ; code configures the MMU to place the highest 32KB of physical RAM ; into the top 32KB of CPU address space. ; ; Input XTAL clock is expected to be 18.432 MHz. ; The CPU is configured to run at the most conservative settings. ; PHI at 1/2 XTAL input, +3 memory wait states, and +3 I/O wait states. ; ; Serial port baud rate divisor is programmed to 480, so the baud rate ; will be PHI / 480, so nominally 19,200 baud. See below for CNTLB ; value to use for 57,600 baud. Line characteristics are set to ; 8 data bits, 1 stop bit, no parity. ; ; The initialization code relocates the internal Z180 CPU registers ; to start at C0H. This is not a requirement of the code, but I ; thought it might be helpful for testing external devices that are ; more likely to be located at the lower I/O addresses. ; ; The submit file (xxx0.sub) contains:- ; R TEST1.180 ; SLR180 TEST1 FH ; HEXCOM TEST1 ; W TEST1.HEX ; W TEST1.LST ; W TEST1.COM ; ; To assemble under windows... ; Load Altair.EXE in the Windows CMD box ; do cpm3 ; I: ; ; Use the "r" SIMH command to move SLR180.COM file across to the I: Drive ; Also "r" the XXX0.SUB file across to the I: drive (one time only) ; Note I already have these files on the Altair I: drive you are using here. ; ; Then.... ; I:>Submit XXX1.SUB ; ; Ignore the SLR assembler error about the load address being less than 100H ; ; The .HEX file will have a start address of F000H. It must reside in the ROM starting ; at 0H. With the Wellon VP 290 Programmer:- ; ; To Buffer Address (HEX) ; For File Address (Hex) 0000 <------ ; File Size (Hex) 8000 ; Intel ; Clear buffer options 0 ; To Buffer Mode Normal ; From File Mode Normal ; ; ; z180_base equ 0C0h ; mon_start equ 0F000h ; z180_cntla0 equ z180_base + 00H ; asci0 control a z180_cntla1 equ z180_base + 01H ; asci1 control a z180_cntlb0 equ z180_base + 02H ; asci0 control b z180_cntlb1 equ z180_base + 03H ; asci1 control b z180_stat0 equ z180_base + 04H ; asci0 status z180_stat1 equ z180_base + 05H ; asci1 status z180_tdr0 equ z180_base + 06H ; asci0 transmit z180_tdr1 equ z180_base + 07H ; asci1 transmit z180_rdr0 equ z180_base + 08H ; asci0 receive z180_rdr1 equ z180_base + 09H ; asci1 receive z180_cntr equ z180_base + 0aH ; csi/o control z180_trdr equ z180_base + 0bH ; csi/o transmit/receive z180_tmdr0l equ z180_base + 0cH ; timer 0 data lo z180_tmdr0h equ z180_base + 0dH ; timer 0 data hi z180_rldr0l equ z180_base + 0eH ; timer 0 reload lo z180_rldr0h equ z180_base + 0fH ; timer 0 reload hi z180_tcr equ z180_base + 10H ; timer control ; z180_asext0 equ z180_base + 12H ; asci0 extension control (z8s180) z180_asext1 equ z180_base + 13H ; asci1 extension control (z8s180) ; z180_tmdr1l equ z180_base + 14H ; timer 1 data lo z180_tmdr1h equ z180_base + 15H ; timer 1 data hi z180_rldr1l equ z180_base + 16H ; timer 1 reload lo z180_rldr1h equ z180_base + 17H ; timer 1 reload hi z180_frc equ z180_base + 18H ; free running counter z180_astc0l equ z180_base + 1aH ; asci0 time constant lo (z8s180) z180_astc0h equ z180_base + 1bH ; asci0 time constant hi (z8s180) z180_astc1l equ z180_base + 1cH ; asci1 time constant lo (z8s180) z180_astc1h equ z180_base + 1dH ; asci1 time constant hi (z8s180) z180_cmr equ z180_base + 1eH ; clock multiplier (latest z8s180) z180_ccr equ z180_base + 1fH ; cpu control (z8s180) ; z180_sar0l equ z180_base + 20H ; dma0 source addr lo z180_sar0h equ z180_base + 21H ; dma0 source addr hi z180_sar0b equ z180_base + 22H ; dma0 source addr bank z180_dar0l equ z180_base + 23H ; dma0 dest addr lo z180_dar0h equ z180_base + 24H ; dma0 dest addr hi z180_dar0b equ z180_base + 25H ; dma0 dest addr bank z180_bcr0l equ z180_base + 26H ; dma0 byte count lo z180_bcr0h equ z180_base + 27H ; dma0 byte count hi z180_mar1l equ z180_base + 28H ; dma1 memory addr lo z180_mar1h equ z180_base + 29H ; dma1 memory addr hi z180_mar1b equ z180_base + 2aH ; dma1 memory addr bank z180_iar1l equ z180_base + 2bH ; dma1 i/o addr lo z180_iar1h equ z180_base + 2cH ; dma1 i/o addr hi z180_iar1b equ z180_base + 2dH ; dma1 i/o addr bank (z8s180) z180_bcr1l equ z180_base + 2eH ; dma1 byte count lo z180_bcr1h equ z180_base + 2fH ; dma1 byte count hi z180_dstat equ z180_base + 30H ; dma status z180_dmode equ z180_base + 31H ; dma mode z180_dcntl equ z180_base + 32H ; dma/wait control z180_il equ z180_base + 33H ; interrupt vector load z180_itc equ z180_base + 34H ; int/trap control ; z180_rcr equ z180_base + 36H ; refresh control ; z180_cbr equ z180_base + 38H ; mmu common base register z180_bbr equ z180_base + 39H ; mmu bank base register z180_cbar equ z180_base + 3aH ; mmu common/bank area register ; z180_omcr equ z180_base + 3eH ; operation mode control z180_icr equ z180_base + 3fH ; i/o control register ; ; CLOCK_VALUE_CMR equ 00H ;For setting CPU clock speed CLOCK_VALUE_CCR equ 00H ; " " CNTLB0_VALUE equ 00H ; For setting final baud rate from clock ; ; (CMR) (CCR) (CNTLB0) Baud PHI ;These settings need to be checked! ; 00H 80H 20H 38,400 18.432 MHz ;Using 18.432MHz Oscillator ; 00H 00H 20H 19,200 9.2 MHz ;Using 18.432MHz Oscillator ; 00H 80H 00H 115,200 18.432 MHz ;Using 18.432MHz Oscillator ; ; 00H 00H 00H 57,600 9.2 MHz ;Setting for S100 Bus Interface ; ; ; ;======================================================================= ; BOOTSTRAP ; ; This code is begins at 0000H and is expected to get control upon ; CPU reset. It does minimal Z180 CPU initialization, then copy ; the monitor code to upper CPU RAM (F000H) and jump to it. ;======================================================================= ; ; Initialize Z180 ; ORG 0H ; This code does *not* handle interrupts. Disable them and leave ; them disabled. im 1 di ; ; Set base for CPU I/O registers. ; Do not use z180_icr equate from z180.inc because the ICR ; is not yet at the running location. At reset, the Z180 ; register base I/O address is zero, so initially, ICR is ; at 3FH. ld a,z180_base out0 (3Fh),a ; at reset, icr is at 3FH ; ; Disable refresh (not really required) xor a out0 (z180_rcr),a ; ; Set default CPU clock multipliers (xtal / 2) ; ; It has been reported that CMR needs to be set prior to CCR ; when using an input frequency that is XTAL / 2. ; I never experienced a problem related to order, but just ; for good measure, CMR is set prior to CCR below. ; https://www.retrobrewcomputers.org/forum/index.php?t=msg&th=316&#msg_5045 LD A,CLOCK_VALUE_CMR ; zero out0 (z180_cmr),a ; set CMR LD A,CLOCK_VALUE_CCR ; 80H out0 (z180_ccr),a ; set CCR ; ; set default wait states (super conservative for now) ld a,0F0h ; +3 mem waits, +3 i/o waits out0 (z180_dcntl),a ; ; Setup Z180 MMU assuming physical memory is ; 512KB ROM / 512KB RAM. CPU address spaces is split at ; 32KB/32KB. The upper 32KB is mapped to the top 32KB of ; physical memory (RAM). The lower 32KB is left mapped to ; the lowest 32KB of memory (ROM), so we continue to run ; from ROM at this point. ld a,80h out0 (z180_cbar),a ; setup for 32k/32k bank config ld a,(1024 - 64) >> 2 out0 (z180_cbr),a ; common base = last (top) bank ; ; We have RAM now in upper 32K now. Initialize the stack ; at top of CPU memory. ld sp,0EFF0h ; now put stack at top of mem ; ; Confirm RAM is working by pushing a value on the stack, then ; popping the value to a different register and writing it to the ; diagnostic LEDs ; Copy monitor to RAM at F000H ; ld hl,mon_img ld de,mon_start ld bc,mon_len ldir ; ; Jump to monitor!!! ; jp mon_start ; ;======================================================================= ; MONITOR ; ; This is the system moitor code that is intended to run from RAM at ; F000H. ;======================================================================= ; mon_img equ $ ; start of monitor image ; .phase mon_start ; running location for monitor ; ; Configure the ASCI0 port ; ; CNTLB is programmed to achieve a divisor of CPU clock / 480. For ; a CPU with an 18.432 MHz clock running at half speed as programmed ; above, this will be 9216000 / 480 = 19200 baud. ; ; Alternative CNTLB values are: ; 20H: divisor = 480, 19200 baud ; 00H: divisor = 160, 57600 baud ; ld a,64h ; xmit enable, rcv enable, 8 data bits, no parity out0 (z180_cntla0),a ld a,CNTLB0_VALUE ; PS=/30, DR=/16, SS=/1, divisor=480, baud=19200 out0 (z180_cntlb0),a ld a,60h ; dcd0 disable, cts0 disable out0 (z180_asext0),a ; ; Delay after ASCI setup (should not be needed) ; ld b,0 djnz $ djnz $ ; ; Select first bank of RAM into lower 32K of CPU memory. This is ; where we abandon the physical ROM and switch to all RAM in the ; CPU address space. ; ld a,10h ; first physical bank of RAM call bnksel ; do it ; ; *** Add code as desired to setup Z180 zero page *** ; ; At this point RAM is mapped to entire CPU address space. This is ; the right place to setup lower RAM, if needed. However, nothing ; needs to be done for the remainder of this sample code. ; ; *** Add code here *** ; ; ; Say "Hello" ; ld hl,str_hello call prtstr ; ; Echo input to output ; echo: call getchr ; get a character cp 27 ; escape? jr z,echoz ; done if so call prtchr ; else print char jr echo ; and loop echoz: ; ; Say "Goodbye" ; ld hl,str_goodbye call prtstr ; exit: halt ; ;======================================================================= ; SUPPORT PROCEDURES ; ; Simple CALLable routines for serial port input/output bank selection ; of lower 32K CPU space. ;======================================================================= ; ; Print a null-terminated string at HL to the primary ; serial port. ; prtstr: ld a,(hl) ; get next char or a ; end of string? ret z ; if so, done call prtchr ; print the char inc hl ; bump to next char in string jr prtstr ; loop till done ; ; Print a single character in A to the primary serial port. ; Will wait for serial output port to be ready. ; prtchr: push af ; save char prtchr1: call outstat ; get output status jr z,prtchr1 ; loop till ready pop af ; restore char out0 (z180_tdr0),a ; send it ret ; done ; ; Check status of ASCI serial output. Returns non-zero (ZF cleared) ; if output register ready ; outstat: in0 a,(z180_stat0) ; get status and 02h ; bit 1 set means ready ret ; done ; ; Get a single character from the primary serial port and return ; it in A. Will wait for serial input port to have a char ready. ; getchr: call instat ; get input status jr z,getchr ; loop till char ready in0 a,(z180_rdr0) ; get it ret ; done ; ; Check status of ASCI serial input. Returns non-zero (ZF cleared) ; if the input port has a character available. Note that Z180 ASCI ; will stall (stop reception) if a line error is detected. In order ; to ensure continuous reception, it is necessary to check for and ; clear line errors. ; instat: in0 a,(z180_stat0) ; read stat reg push af ; save status and 70h ; parity, framing, or overrun error? jr z,instat1 ; jump ahead if no errors ; ; clear error(s) or nothing further can be received!!! in0 a,(z180_cntla0) ; read CNTLA res 3,a ; clear efr (error flag reset) out0 (z180_cntla0),a ; write updated CNTLA ; instat1: pop af ; restore status value and 80h ; data ready? ret ; done ; ; Simply bank selection routine. Enter with A indicating the ; 32K bank of memory to select into the lower 32K of CPU space. ; For example, 00H selects first bank of ROM, 10H selects first ; bank of RAM. Register A is trashed! ; ; We want 32K banks, but Z180 uses 4K increments. So, we need to ; scale the input from 32K chunks to 4K chunks. ; bnksel: rlca ; Scale input from rlca ; ... 32K chunk addressing to rlca ; ... 4K chunk addressing out0 (z180_bbr),a ; write to bank base reg ret ; ; Data ; str_hello db 13,10,"Hello",13,10,0 str_goodbye db 13,10,"Goodbye",13,10,0 ; mon_len equ ($ - mon_start) ; .dephase end