• Please review our updated Terms and Rules here

Cromemco dazzler replica project

Just a note to point out I still have two sets of boards available if anyone wants them

They would need the minor errors found correcting and they are not ENIG plated and dont have hard gold fingers but as has now been shown, they do work ! (Mr Null, what a hero in taking on the challenge)

I know I posted one set to Verault but not sure if I sent / promised another set to someone else so please drop me a PM.
 
Hi,

I think we need to see a single page (PDF?) of all the fixes, and then another (PDF?) of the testing.

The idea of a board that needs to be studied in several dozen if not 100 or so pages of the forum isn't very inviting.


.
 
Corrections are all updated on Github but they were.

IC 43 pins 10,13 & 14 - connect to adjacent rail - fixed
IC 39 pin 14 - connect to adjacent rail - fixed
IC 23 pin 10 - connect to adjacent rail - fixed
IC 33 pin 10 - connect to adjacent rail - fixed

Cosmetics
R11 should read 30K
Two R23's !
IC 44 should be a 7474
IC52 should be a 7408

Test procedure is in the Dazzler manual.

But the changes to the Memory card will depend on what your using.
 
Z80 Assembly question:

I have a variable defined as:
Code:
BODYX:  DEFS    255        ;PREVIOUS SNAKE X POSITIONS

This will hold a list of the previous body X positions (i have another one for Y values)

Now, I want to place something at a specific offset within this memory space, say BODYX + 5.

My A register already holds the value 5. H holds the value that I want to write to the offset space in memory.

Then I have this code:

Code:
LD DE, BODYX        ; Load DE with the base address of BODYX
ADD A, E            ; Add the offset (stored in A) to the low byte (E) of DE
LD E, A             ; Move the result back to E (carry should be handled)
; Now DE points to the calculated address (BODYX + offset)
LD A, H             ; Load A with the value from H
LD (DE), A          ; Store the value in A at the calculated address (DE)

I don't know if this is right or not, but it seems ok..

However, when assembling I get the following error:
Code:
CROMEMCO Z80 Macro Assembler version 03.10
0127' 320000      0232          LD (DE), A          ; Store the value in A at the calculated address (DE)
*** syntax error ***

Can I not refer to a memory location from the value in a register? If not, how do I do this?
 
First is your use of DE as a pointer to memory.

Your code is erroneous I am afraid. It should be something like:

ADD A,E
LD E,A
LD A,D
ADC A,0
LD D,A

The reason is that your code only works if BODYX is aligned on a 256 byte page boundary.

You say (in a comment) that the carry should be handled. Unfortunately it isn't. You are doing 8 bit arithmetic and not 16 bit. So the carry flag (if any) from the addition is set; but you are responsible for adding the carry into register D.

You can (for brevity) tell the assembler to put BODYX on a page boundary. I leave that as an exercise for the reader! Clue: use ORG and the current location counter of the assembler and a bit of simple maths.

If you do that, you can very much simplify your code to:

LD D,BODYX / 256.
LD E,A

; Now use DE as a memory pointer.

The "LD (DE),A" instruction is valid. I can't see (for the moment) why there is a syntax error there.

Dave
 
Last edited:
Now, I want to place something at a specific offset within this memory space, say BODYX + 5.
If you only want to do specific constant offsets, rather than computed offsets, you can LD IX,BODYX and use (IX+nn) with a one-byte offset. I'm pretty sure that's a positive (0-255) offset only.

Another way could be:
Code:
LD HL,BODYX
LD D,0
LD E,A ; if the offset was in A rather than E
ADD HL,DE ; you could also use BC instead of DE
LD (HL),something
Generally HL is your first choice as a memory pointer, and DE or BC is your second choice because it's more versatile with LD (HL),x allowing any register or immediate, and the 16-byte add.

However, when assembling I get the following error:
Code:
CROMEMCO Z80 Macro Assembler version 03.10
0127' 320000      0232          LD (DE), A          ; Store the value in A at the calculated address (DE)
*** syntax error ***
That really should work. Try it without the blank after the comma. The reason is that older assemblers often have rather simple parsers, and may not like blanks in the operand field. (I think these are often the ones that allow comments with a semicolon.) So good style with assembly language avoids blanks after the comma, unlike C which prefers blanks after a comma.

(Either that or somehow it thinks DE is a label, not a register name, which is not sane.)
 
You can (for brevity) tell the assembler to put BODYX on a page boundary. I leave that as an exercise for the reader! Clue: use ORG and the current location counter of the assembler and a bit of simple maths.
Ok so if I'm understanding this correctly, I'm supposed to be able to use an expression with the ORG command, where $ is the current counter.

So this is what I came up with (with some help from google on the math formula) - although it's not working, (see further down):
Code:
ORG (($+255)/256)*256    ;PUT THESE VARIABLES IN THE NEXT 256-byte page boundary1
BODYX:  DEFS    256        ;PREVIOUS SNAKE X POSITIONS
BODYY:  DEFS    256        ;PREVIOUS SNAKE X POSITIONS

That really should work. Try it without the blank after the comma. The reason is that older assemblers often have rather simple parsers, and may not like blanks in the operand field. (I think these are often the ones that allow comments with a semicolon.) So good style with assembly language avoids blanks after the comma, unlike C which prefers blanks after a comma.
Thanks I took out the space (and one after) but I am still getting the syntax error. I made sure there are no tabs or anything too.. I don't get it.

So, this is where I'm at..

It's still giving me syntax errors on those 2 original lines:
Code:
014F' ED430000    0251          LD (DE),H
*** syntax error ***

0156' ED430000    0255          LD (DE),L
*** syntax error ***

I tried updating my code to the simplified version Dave offered above, but it's throwing a expression error on those. I assume the period at the end of the line was intentional, but this was an attempt without. With the period, I got an *** argument error ***.
Code:
014C' 1602        0249          LD D,BODYX/256
*** expression error ***

0153' 1603        0253          LD D,BODYY/256
*** expression error ***

And finally, I'm also getting a syntax error with my attempt of an ORG directive:
Code:
0396  ORG (($+255)/256)*256
*** syntax error ***
 
It's still giving me syntax errors on those 2 original lines:
Code:
014F' ED430000    0251          LD (DE),H
*** syntax error ***

0156' ED430000    0255          LD (DE),L
*** syntax error ***
Nope, you can only LD (DE),A / LD A,(DE). I still don't know why it wouldn't let you do that.

Code:
014C' 1602        0249          LD D,BODYX/256
*** expression error ***

0153' 1603        0253          LD D,BODYY/256
*** expression error ***
It might not do division. This is why I prefer to just use my own assembler and copy the result over.

And finally, I'm also getting a syntax error with my attempt of an ORG directive:
Code:
0396  ORG (($+255)/256)*256
*** syntax error ***
Yep, it's looking like it might not support much in the way of math.
In a modern assembler you could probably just do "ALIGN 256".
 
Nope, you can only LD (DE),A / LD A,(DE). I still don't know why it wouldn't let you do that.


It might not do division. This is why I prefer to just use my own assembler and copy the result over.


Yep, it's looking like it might not support much in the way of math.
In a modern assembler you could probably just do "ALIGN 256".

I thought I'd try simplifying it by doing just one operation on a line:

Code:
ORG $ + 255
ORG $ / 256
ORG $ * 256
BODYX:  DEFS    256        ;PREVIOUS SNAKE X POSITIONS
BODYY:  DEFS    256        ;PREVIOUS SNAKE X POSITIONS

different error, but nope:
Code:
0263' 000000      0397  ORG $ + 255
*** multiple definition ***

0266' 000000      0398  ORG $ / 256
*** multiple definition ***

0269' 000000      0399  ORG $ * 256
*** multiple definition ***

I realize this might not be ideal, but can I manually just stick these out somewhere higher-up in memory like 3000H & #3100H?
 
Last edited:
Nope, you can only LD (DE),A / LD A,(DE). I still don't know why it wouldn't let you do that.

Ok that's right.. I changed the code to:

Code:
LD A,H
LD (DE),A

And now those lines assembled, not sure what it didn't like the first time.
 
Going back to Dave's initial suggested revisions and completing it like:

Code:
    LD DE,BODYX
    ADD A,E
    LD E,A
    LD A,D
    ADC A,0
    LD D,A
    LD A,H
    LD (DE),A

I was able to get something to assemble and I think it's working, I may have a bug elsewhere in the code but I think that gets me what I needed.
 
I will check out the ASMB manual later today...

When writing assembler code, register usage should be considered right from the very start.

As Bruce says, HL is a much more 'versatile' register for accessing memory than any of the others are - so it would be my first choice for a memory pointer. In fact, it 'inherits' this flexibility from the Z80's ancestors where it was the only double register memory pointer. Using DE and BC as memory pointers are very limiting when compared to the flexibility of HL.

IX and IY are really only used with fixed offsets from a base. However, there are some 'backdoor' methods of using IX and IY in a similar manner to HL, but the code size is increased as a result.

Dave
 
Reading the manual is your friend...

There is the full gamut of mathematical operators (e.g. +, -, *, /). However, there may be some important syntax involved.

Note that the worst problem here is the use of '(' and ')' - as these are used to differentiate between (say) LD A,10 and LD A,(10) (i.e. an immediate value verses a memory address). You can (however) use '[' and ']' instead - and these should not cause problems. Again, the documentation is your friend.

You can use an absolute address to locate your variables - but this takes away from one of the major advantages of using an assembler - that of not bothering about memory addresses (unless you have to for other reasons). However, there is a learning curve to getting it right.

I *** think *** some of your errors may be due to using (for example) ORG as the first thing on the line WITHOUT a leading delimiter. This is one case where delimiters are important. I think the assembler is using ORG as a label not as a directive.

My preference is to use labels in column 1 and to use a couple of <TABS> to separate the opcode or directive then a couple of <TABS> to separate the operand(s) then a couple more <TABS> to separate the comment(s).

This aligns things nicely so the result is readable...

Dave
 
Thanks, I did look at the macro assembler manual and found the section about ORG.. it talked briefly about using expressions, but gave no expression examples. It didn't even mention the $ symbol. I didn't see the actual section dedicated to it will read through it. I guess I should just put the whole thing aside for some Sunday reading, that way I at least am familiar with all the sections.
:)

I am going through and updating all the code to the format you suggested, this looks much nicer. Will report back on the ORG command.
 
Last edited:
dang..
Code:
    ORG [[$+255]/256]*256
BODYX:        DEFS    256        ;PREVIOUS SNAKE X POSITIONS
BODYY:        DEFS    256        ;PREVIOUS SNAKE X POSITIONS

obviously still missing something:
Code:
      (0300)      0407          ORG [[$+255]/256]*256
*** expression error ***

So I tried separating out the operations again:
Code:
    ORG $+255
    ORG $/256
    ORG $*256
BODYX:        DEFS    256        ;PREVIOUS SNAKE X POSITIONS
BODYY:        DEFS    256        ;PREVIOUS SNAKE X POSITIONS

And just got this:
Code:
      (0003)      0410          ORG $/256
*** expression error ***

So it seems to have a problem with the divide for some reason?
 
We might have hit the issue with REL (relocatable) source files where there are limitations on certain expressions related to ORG and other directives.

Perhaps stick to not using ORG and doing the job properly...

You can also ORG the data at any address you actually like:

Code:
       ORG 04000H
BODYX: DS 256
BODYY: DS 256

However, you have to now make sure that your code does not crash through the data!

Look at the resulting listing file and make sure the end of the code does not overlap the beginning of the data. If it does, move the ORG statement and reassemble.

Also, you need to make sure the end of your data does not crash over CDOS.

If you define your data using DS directives, then it doesn't actually occupy any space in the file - but you have to assume it is random rubbish on start-up and initialise it yourself.

I will still think about how to use the DATA directive with the linker or to do it a better way. I would like to find out for myself...

Dave
 
Last edited:
Well in the past few days I got a few things working.. Random point placement, speed control, wall collision detection, and tracking body position - this was more difficult than I thought.. I've sure Dave would barf if he saw my code, but it works? I basically add the head to the end of the memory location, erase the tail, and shift the memory to the left by 1 byte. On each pass through the game loop I only draw the next head and erase the tail, since the Dazzler maintains the rest of the screen in memory. I'm sure there are more efficient ways to do this. Next up, body & point collision detection, and scoring.

 
Back
Top