News:

Printed Amstrad Addict magazine announced, check it out here!

Main Menu

My introduction, and my first assembly language program

Started by the graveborn, 20:00, 28 December 13

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

the graveborn

Hi! I've been a lurker here for a few weeks, not wanting to post an introduction thread 'til I had some source code to show... I'm a student, and I usually program in C, but I'm trying to broaden my mind so I've decided to try -- emphasis on try -- to learn to program in Z80 assembly language, specifically the Arnor MAXAM dialect, specifically for the 6128+ (I played a little on my older cousin's when I was younger); I've posted below the source code to my first assembly language program (which just prints "Hello!" to the screen -- I have a lot to learn...) -- it assembles/runs without any warnings or errors, but I'd appreciate it if anybody could tell me if I've made any egregious errors!

Hello!.Z80:
; The name of the assembled executable
write "Hello!"

; After starting the 6128+

; print himem
; 42619

; So I've assumed that using addresses
; 42600..42606 (storage for the string
; "Hello!" plus terminator) is okay...?

; Register pair HL holds the address
; 42600, to be set to the first
; character in the string
ld hl, 42600

; The memory at the address held by
; HL is set to the value 72 -
; - corresponding to the letter 'H' -
; then the address held by HL is
; incremented ready for the next
; character
ld (hl), 72  ; 'H'
inc hl

ld (hl), 101 ; 'e'
inc hl
ld (hl), 108 ; 'l'
inc hl
ld (hl), 108 ; 'l'
inc hl
ld (hl), 111 ; 'o'
inc hl
ld (hl), 33  ; '!'
inc hl

; Lastly, the memory at the address
; held by HL is set to the value 0,
; terminating the string
ld (hl), 0

ld hl, 42600
.loop
  ld a, 0

  ; Compare the value at the address
  ; held by HL to 0; if it *is* 0,
  ; that's the string terminator
  cp a, (hl)
  jp z, forever

  ; Set A to the value at the address
  ; held by HL, and use a firmware
  ; function to print A to the screen
  ld a, (hl)
  call &bb5a
  inc hl
  jp loop

.forever
  jp forever
Current project: SDL2 joystick interrogator

redbox

Welcome and nice to see you're learning Z80, it can only make your coding generally better and more efficient.

Your code is fine and works, so it's a good attempt. Locating it beneath HIMEM is fine but generally code is placed somewhere in 'normal' RAM.

Here is an optimised version for you to play with:


org &4000

ld hl,message
.loop
ld a,(hl)
cp 0
ret z
call &bb5a
inc hl
jr loop

.message
defb "Hello World!",0


redbox

...and you can replace the CP 0 here with OR A.

For bonus points can you work out why...? ;)

TFM

Welcome in the forum and a happy 2014 full of CPCs.
TFM of FutureSoft
Also visit the CPC and Plus users favorite OS: FutureOS - The Revolution on CPC6128 and 6128Plus

the graveborn

@redbox: Thanks for the welcome :) Comparing your version to mine... I knew my version was bad; I didn't know how bad :( (I didn't know about the defb directive; and I didn't think enough to realise that labels could be loaded into registers.)

(And I think cp 0 could be replaced with or a as or a would essentially be a no-op but would still set the z flag if the result was 0...?)

@TFM: Thanks for the welcome :)

Presenting my first (vaguely :blush:) useful assembly language routine (to print to the screen an 8bit value as binary):

print8b.Z80:
.print8b

; B  - The value to be printed (binary
; representation)
; HL - The address to return to after
; printing B
; Corrupts A, C, D

ld c, &80
.p8b_loop
  ld a, b
  and a, c
  ld d, &30 ; Set d to '0'
  jp nz, p8b_nz
  .p8b_nz_return
  ld a, d
  call &bb5a
  srl c
  ld a, c
  cp a, 0
  jp nz, p8b_loop
  ld a, &62 ; Set a to 'b'
  call &bb5a
  jp (hl)

.p8b_nz
  ld d, &31 ; Set d to '1'
  jp p8b_nz_return


print8b.tst (to test the above):
write "print8b"

ld b, 21
ld hl, goodbye
jp print8b
.goodbye
jp goodbye

read "print8b.z80"
Current project: SDL2 joystick interrogator

ralferoo

Nice work, you're getting there. Here are another couple of variants you might like to make sure you understand... (IMHO, a good way to learn is to understand something that seems magical before!)

BTW, I'm not sure why you'd want to use HL as a return address here. Obviously there are occasions where it's useful, but more often you'd want to keep HL available as it's the second or first most useful register depending on what you're trying to do... In all these examples, I'll assume you're calling this function and using RET to return.

So, a simple example with RL and ADC to demonstrate carry:

; entry: B=value to be printed
; exit: A=corrupted, C=0
print8b:
ld c,8
print8bloop:
ld a,#30
rl b     ; try to think about what happens to B each loop and at the end of the loop
adc a,0   ; and why this works
call &bb5a
dec c
jr nz,print8bloop
ld a,#62
jp &bb5a


a slight optimsation:


; entry: B=value to be printed
; exit: A=corrupted, C=0
print8b:
ld c,8
print8bloop:
ld a,#18
rlc b
rla            ; try to understand why this works too!
call &bb5a
dec c
jr nz,print8bloop
ld a,#62
jp &bb5a


and now:


; entry: B=value to be printed
; exit: A=corrupted, B=0
print8b:
scf ; notice this!
print8bloop:
ld a,#18
rl b   ; what happens to B here the first time through the loop and then the subsequent times
jr z,print8end  ; if you didn't want the b at the end, ret z could be used
rla
call &bb5a
and a ; think why this is needed
jr print8bloop
print8end:
ld a,#62
jp &bb5a


Happy learning! :)

fano


Welcome to the marvellous world of Z80 programming (and CPC Z80 world where JR is faster than JP  :P  )
Note CPC firmware does not use C termination for string but it sets the last character bit 7 as string terminator.

Quote from: the graveborn on 13:57, 29 December 13(And I think cp 0 could be replaced with or a as or a would essentially be a no-op but would still set the z flag if the result was 0...?)
It is 1µs and 1 byte instead of 2µs and 2 bytes for cp 0.What is the result if you OR (or AND) a bit with ifself ?
"NOP" is the perfect program : short , fast and (known) bug free

Follow Easter Egg products on Facebook !

the graveborn

@ralferoo: Thanks for those examples; I'm still working to understand them, but after seeing the first of those examples I realised that --

ld a, &80
add a, &80 ; a is now 00000000b
adc a, 0   ; a is now 00000001b


-- thanks! :)

Quote from: ralferoo on 14:33, 29 December 13BTW, I'm not sure why you'd want to use HL as a return address here.

Because I haven't (yet...) figured out how to use ret to return from a jp :blush:

And after writing that I figured it out -- I shouldn't have been using jp, but call :blush:

(I'm trying to learn from pretty much a listing of assembler mnemonics -- a little less than ideal ;) I still have no idea what the m, p, pe, or po flags are for...)
Current project: SDL2 joystick interrogator

the graveborn

@fano: Thanks for the welcome :)

I guessed that or instead of cp would be a time or memory saving, or redbox wouldn't have suggested it ;)

What I meant by no-op was that or a wouldn't change a, but would have the side-effect of setting z (and clearing nz) if a was 0... correct?
Current project: SDL2 joystick interrogator

fano


Exact  ;)

Quote from: the graveborn on 16:54, 29 December 13I still have no idea what the m, p, pe, or po flags are for...)
pe and po stands for parity even and parity odd (bit count parity in result), related to P/V (or P) flag.m and p stands for negative or positive result (high bit of the operation result), related to S flag.
"NOP" is the perfect program : short , fast and (known) bug free

Follow Easter Egg products on Facebook !

ralferoo

Everything Z80 you could possibly need will be at Thomas Scherrer Z80-Family Official Support Page (although obviously nothing that's CPC specific)

Powered by SMFPacks Menu Editor Mod