Loops/While: Difference between revisions
Content added Content deleted
(→{{header|ANSI BASIC}}: Added a solution.) |
(Added Z80 Assembly version) |
||
Line 3,738: | Line 3,738: | ||
]; |
]; |
||
]</syntaxhighlight> |
]</syntaxhighlight> |
||
=={{header|Z80 Assembly}}== |
|||
{{works with|CP/M 3.1|YAZE-AG-2.51.2 Z80 emulator}} |
|||
{{works with|ZSM4 macro assembler|YAZE-AG-2.51.2 Z80 emulator}} |
|||
Use the /S8 switch on the ZSM4 assembler for 8 significant characters for labels and names |
|||
<syntaxhighlight lang="z80"> |
|||
; |
|||
; 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 |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
E>whilelp |
|||
1024 |
|||
512 |
|||
256 |
|||
128 |
|||
64 |
|||
32 |
|||
16 |
|||
8 |
|||
4 |
|||
2 |
|||
1 |
|||
</pre> |
|||
=={{header|Zig}}== |
=={{header|Zig}}== |