147 lines
3.7 KiB
Z80 Assembly
147 lines
3.7 KiB
Z80 Assembly
;
|
|
; while loop, dividing 1024 repeatedly by 2, using Z80 assembly language
|
|
;
|
|
; Runs under CP/M 3.1 on YAZE-AG-2.51.2 Z80 emulator
|
|
; Assembled with zsm4 on same emulator/OS, uses macro capabilities of said assembler
|
|
; Created with vim under Windows
|
|
;
|
|
; Thanks to https://wikiti.brandonw.net for the idea for the conversion routine hl -> decimal ASCII
|
|
;
|
|
;
|
|
; 2023-05-19 Xorph
|
|
;
|
|
|
|
;
|
|
; Useful definitions
|
|
;
|
|
|
|
bdos equ 05h ; Call to CP/M BDOS function
|
|
strdel equ 6eh ; Set string delimiter
|
|
wrtstr equ 09h ; Write string to console
|
|
|
|
nul equ 00h ; ASCII control characters
|
|
cr equ 0dh
|
|
lf equ 0ah
|
|
|
|
cnull equ '0' ; ASCII character constants
|
|
|
|
;
|
|
; Macros for BDOS calls
|
|
;
|
|
|
|
setdel macro char ; Set string delimiter to char
|
|
ld c,strdel
|
|
ld e,char
|
|
call bdos
|
|
endm
|
|
|
|
print macro msg ; Output string to console
|
|
ld c,wrtstr
|
|
ld de,msg
|
|
call bdos
|
|
endm
|
|
|
|
newline macro ; Print newline
|
|
ld c,wrtstr
|
|
ld de,crlf
|
|
call bdos
|
|
endm
|
|
|
|
;
|
|
; =====================
|
|
; Start of main program
|
|
; =====================
|
|
;
|
|
|
|
cseg
|
|
|
|
setdel nul ; Set string delimiter to 00h
|
|
ld ix,value ; Register ix points to memory location of counter
|
|
|
|
while:
|
|
ld a,(ix) ; Z80 has no 16 bit compare, so we check the value byte by byte for 0
|
|
or a ; In contrast to other CPUs, loading a register does NOT set the flags
|
|
jr nz,docalc ; or-ing the accumulator with itself sets the flags and is faster than "cp 0"
|
|
ld a,(ix+1)
|
|
or a
|
|
jr z,endprog ; If both bytes are 0, end program - this jump could be optimized away
|
|
; and replaced with a direct "ret z", but we want to simulate a "real"
|
|
; while loop, so we continue (jump to) after the last loop statement
|
|
|
|
docalc:
|
|
ld hl,(value) ; Print the current value, followed by newline
|
|
ld iy,buffer ; Register iy points to memory location for current value as text for printout
|
|
call dispHL ; dispHL modifies iy, so it must be reset to the buffer on every iteration
|
|
|
|
print buffer
|
|
newline
|
|
|
|
srl (ix+1) ; Neither has the Z80 a 16 bit shift operation for dividing by 2...
|
|
rr (ix) ; Shift the MSB of value right and then rotate the LSB with carry to the right
|
|
|
|
jr while ; Next iteration
|
|
|
|
endprog:
|
|
ret ; Return to CP/M
|
|
|
|
;
|
|
; ===================
|
|
; End of main program
|
|
; ===================
|
|
;
|
|
|
|
;
|
|
; Helper routines - notice that the Z80 does not have a divide instruction
|
|
; Notice further that CP/M does not have any support for pretty-printing
|
|
; formatted numbers and stuff like that. So we have to do all this by hand...
|
|
;
|
|
|
|
;
|
|
; Converts the value (unsigned int) in register hl to its decimal representation
|
|
; Register iy has memory address of target for converted value
|
|
; String is terminated with nul character (\0)
|
|
;
|
|
|
|
dispHL:
|
|
ld b,1 ; Flag for leading '0'
|
|
irp x,<-10000,-1000,-100,-10,-1>
|
|
ld de,x ; Subtract powers of 10 and determine digit
|
|
call calcdig
|
|
endm
|
|
|
|
ld a,nul ; Terminate result string with nul
|
|
ld (iy+0),a
|
|
|
|
ret ; End of conversion routine
|
|
|
|
calcdig:
|
|
ld a,cnull-1 ; Determine the digit character
|
|
incrdig:
|
|
inc a ; Start with '0'
|
|
add hl,de ; As long as subtraction is possible, increment digit character
|
|
jr c,incrdig
|
|
|
|
sbc hl,de ; If negative, undo last subtraction and continue with remainder
|
|
cp cnull ; Check for leading '0', these are ignored
|
|
jr nz,adddig
|
|
bit 0,b ; Use bit instruction for check if flag set, register a contains digit
|
|
ret nz ; If '0' found and flag set, it is a leading '0' and we return
|
|
adddig:
|
|
ld b,0 ; Reset flag for leading '0', we are now outputting digits
|
|
ld (iy+0),a ; Store character in memory and set iy to next location
|
|
inc iy
|
|
|
|
ret ; End of conversion helper routine
|
|
|
|
;
|
|
; ================
|
|
; Data definitions
|
|
; ================
|
|
;
|
|
|
|
dseg
|
|
|
|
value: defw 1024d ; Starting value for loop, 16 bit little endian
|
|
crlf: defb cr,lf,nul ; Generic newline
|
|
buffer: defs 10 ; Buffer for conversion of number to text
|