Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Memory Management
#1
Why not just put your org lower, have your low non speed critical functions, use align to hop to 32768 and stick a defs 6912, 0 to skip your screen buffer zone?
Reply
#2
britlion Wrote:Why not just put your org lower, have your low non speed critical functions, use align to hop to 32768 and stick a defs 6912, 0 to skip your screen buffer zone?

That's an idea except that would make the compiler runtime low as well
so that is not an option. Or place the screen buffer at top and experiment
with the ORG so the program compiles right up to the screen buffer and
that would work with some programs but you would have no control over
what goes where and the runtime again would be at the low end of the
memory.

I rewrote the ATTR routine and it is as follows:
Code:
Declare sub SetAttr (ByVal Line as ubyte, ByVal Column as ubyte, ByVal NumberOfCells as uinteger)

Dim AttrAddress as uinteger = 22528
---
bla
----

Sub SetAttr (ByVal Line as ubyte, ByVal Column as ubyte, ByVal NumberOfCells as uinteger)

'Dim AttrAddress as uinteger = 22528
'Dim EndAttrAddress as uinteger
'Dim AttrColor as ubyte at 23693
'AttrAddress = AttrAddress + (cast(uinteger, Line) shl 5) + cast(uinteger, Column) ' Line * 32
'EndAttrAddress = AttrAddress + NumberOfCells

AttrAddress = AttrAddress
Asm
push hl
ld l, (ix+5)
ld h, 0
add hl, hl
add hl, hl
add hl, hl
add hl, hl
add hl, hl
ex de, hl
pop hl
add hl, de
push hl
ld l, (ix+7)
ld h, 0
ex de, hl
pop hl
add hl, de
ld c, (ix+8)
ld b, 0
ld a, (23693)
ld e, a
SetAttrCopy:
ld a, e
ld (hl), a
inc hl
dec bc
ld a, b
or c
jr nz, SetAttrCopy
End asm

'Do
'  Poke AttrAddress, AttrColor
'  AttrAddress = AttrAddress + 1
'Loop until AttrAddress = EndAttrAddress

End sub

NumberOfCells is treated as a ubyte in the ASM code instead of uinteger, it
saves me one indexing instruction, the BC register pair. The declareation
of AttrAddress is moved into global scobe instead of local as that saves in
indexing instuctions as well and they are slow.

If i rem out the global declaration of AttrAddress and the ASM section and
"AttrAddress = AttrAddress" and unrem the other parts then the resulting
code is 90 bytes larger with 22 indexing instructions instead of three and
of those 10 are in the Do-Loop section if I counted correctly.

I use AttrAddress = AttrAddress to make sure that it is the HL register pair
before the code moves on. The resulting code with O2 is:
ld hl, (_AttrAddress)
ld (_AttrAddress), hl

If the O3 level get's smarter isn't there a danger that is will be optimized out?
Does it matter for I did compile this without "AttrAddress = AttrAddress" and
it did work fine but that could have been luck for this is the first uinteger
variable declared in the global scobe and as such it could have been in HL
I don't know but I would never depend on such luck so the question does
matter.

Maybe someone can use this code.

Darkstar.
Reply
#3
Note: there is already both SetAttr and GetAttrAddr routines in <attr.bas>. Did you had a look to them? Maybe what you want was already there... I'm now checking this bug.
Reply
#4
boriel Wrote:Note: there is already both SetAttr and GetAttrAddr routines in <attr.bas>. Did you had a look to them? Maybe what you want was already there... I'm now checking this bug.

Thank you.

Yes, there was something there and here is the reworked version of the
sub.

Code:
Declare sub SetAttr (ByVal Line as ubyte, ByVal Column as ubyte, ByVal NumberOfCells as uinteger)

Sub SetAttr (ByVal Line as ubyte, ByVal Column as ubyte, ByVal NumberOfCells as uinteger)

Asm
ld e, (ix+7)
ld d, (ix+5)
ld h, 0                     ;  7 T-States
ld a, d                     ;  4 T-States
add a, a     ; a * 2        ;  4 T-States
add a, a     ; a * 4        ;  4 T-States
ld l, a      ; HL = A * 4   ;  4 T-States
add hl, hl   ; HL = A * 8   ; 15 T-States
add hl, hl   ; HL = A * 16  ; 15 T-States
add hl, hl   ; HL = A * 32  ; 15 T-States
ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone)
add hl, de
ld de, (SCREEN_ADDR)    ; Adds the screen address
add hl, de
;ld c, (ix+8)
;ld b, (ix+9)
;ld b, 0
ld b, (ix+8)
ld a, (23693)
;ld e, a
SetAttrCopy:
;ld (hl), e
ld (hl), a
inc hl
djnz SetAttrCopy
;dec bc
;ld a, b
;or c
;jr nz, SetAttrCopy
End asm

End sub

Nothing in the global scope, some of this is inline addition from attr.asm.
Reply
#5
Darkstar Wrote:...and the runtime again would be at the low end of the
memory.

Correction, the runtime is placed after the program so it would be in high
but with limited control over the routines, that is nothing but hope as in
what goes low and what goes high.
Reply
#6
It's my belief that they are compiled in order, so you should get that effect from

GOTO start

REM Routines that can go below 32768
FUNCTION a ()
END FUNCTION

SUB b
END SUB

align 32768
REM BUFFER
asm
defs 0,6912
end asm

REM High memory routines
FUNCTION c()
end function

SUB d
end sub

start:
Here's where we jump to immediately, so start of program.

[Note that the above code is example, rather than strictly correct!]

Not sure where the heap is put, though? I think it's after the code.
Reply
#7
This thread has derived to a completely new topic. Splitted and moved to WishList: memory management :wink:
Reply
#8
britlion Wrote:Not sure where the heap is put, though? I think it's after the code....It's my belief that they are compiled in order, so you should get that effect from..........

The heap is after the code and runtime.
With O1 and ORG 28000 this produces a TAP file of 6078 bytes and that I
find quite odd.
Code:
GOTO start

REM Routines that can go below 32768
FUNCTION a ()

Print "Hello_A";

Asm
dec a
dec a
dec a
End asm

END FUNCTION

SUB b

Print "Hello_B";

Asm
dec b
dec b
dec b
End asm

END SUB

'align 32768
REM BUFFER
asm
align 32768
defs 0,6912
end asm

REM High memory routines
FUNCTION c()

Print "Hello_C";

Asm
inc c
inc c
inc c
End asm
end function

SUB d

Print "Hello_D";

Asm
inc d
inc d
inc d
End asm

end sub

start:
Print "Hello_Start";

Asm
inc e
inc e
inc e
End asm

They are compiled in order but not in the way you think. In the generated ASM
file there is a small section to init the machine code and that includes the
goto statement, then the align and def and then it is all (code and data)
lumped together in one section as the compiler finds it, code first and then
the data.

So it did not solve the problem.

Also if you do:
Asm
ld e, (NN)
End asm

Then the assembler acctepts it and it is there in the generated ASM file and
when you compile it with zbasm it goes through without a hitch but as
there is no such instruction in the Z80A instruction set then the result is
garbage. That one can see in a debugger after the TAP file is loaded.

So i guess this is a item for the whishlist, two sections of code and data
under user control.
Reply
#9
That should be defs 6912,0 I think. My error.

You might want to do something like defs 6912, 128 just so you can see the code block for the buffer differently to empty ram.

I compiled this and I saw

initialize
gap of zeroes
buffer starting at 32768
E markers
A markers
B markers
C markers

Hmm. Not what I expected at all. I have to concur with your analysis. Darn. I had hopes for that method. I have no idea why the compiler would do that. It must be following a non linear logic. I even tried adding declare statements for the functions.

For:

Asm
ld e, (16384)
End asm

Yes, that looks like a bug in the assembler in accepting that. Looks like it does a LD E,0 instruction there.
Reply
#10
britlion Wrote:That should be defs 6912,0 I think. My error.

You might want to do something like defs 6912, 128 just so you can see the code block for the buffer differently to empty ram.

I compiled this and I saw

initialize
gap of zeroes
buffer starting at 32768
E markers
A markers
B markers
C markers

Hmm. Not what I expected at all. I have to concur with your analysis. Darn. I had hopes for that method. I have no idea why the compiler would do that.
I did defs 6912,65 and the Byte header was 28000, 12990 so it did fix the
length problem. I saw the same thing through the debugger. I had a
larger program once with a lot of text in it and the compiled text was
all out of order even if it was within the same section of code, but it did not
use subs. I have no idea....
britlion Wrote:For:

Asm
ld e, (16384)
End asm

Yes, that looks like a bug in the assembler in accepting that. Looks like it does a LD E,0 instruction there.
Then after that it does LD E, (HL) or was it LD (HL), E? I don't remember.
I think it was the latter. This passed through the assembler in the compiler
and through zbasm, double bug or the same bug that needs to be corrected
in two places.


Low strictly speaking would be below the ORG address or if I were to use
a new term, Start.Prog address. But that could be changed with a compiler
directive if a buffer has to go in between. The compiler would use Start.Prog
and the ASM part would use ORG.

The directive would let the beginning (or end for it goes bakwards towards
the 24 KiB limit) be a fixed number for a program.
Reply
#11
Darkstar Wrote:Also if you do:
Asm
ld e, (NN)
End asm

Then the assembler acctepts it and it is there in the generated ASM file and
when you compile it with zbasm it goes through without a hitch but as
there is no such instruction in the Z80A instruction set then the result is
garbage. That one can see in a debugger after the TAP file is loaded.
This is a matter of ambiguity: For the assembler (16384) is also an arithmetical expression. So the above is compiled to:
Code:
asm
ld e, NN
end asm
Since e is ubyte, the result is truncated to its lower byte. In this case, ld e,(16384) is assembled as ld e,0 (as Britlion pointed). This is rather problematic, because when you want to use parenthesis for an arithmetical expression with something like LD HL, (Mylabel + 3) it doesn't know whether you are referring to LD HL, [Mylabel + 3] or LD HL, Mylabel + 3. A rule of thumb: it will try always the 1st case and will fallback to the 2nd if the first is not possible (as it happened in the example above).

Pasmo already allows brackets [ ] for memory indirection. Zxbasm will also do (in the near future).
Reply
#12
boriel Wrote:
Darkstar Wrote:Also if you do:
Asm
ld e, (NN)
End asm

Then the assembler acctepts it and it is there in the generated ASM file and
when you compile it with zbasm it goes through without a hitch but as
there is no such instruction in the Z80A instruction set then the result is
garbage. That one can see in a debugger after the TAP file is loaded.
This is a matter of ambiguity: For the assembler (16384) is also an arithmetical expression. So the above is compiled to:
Code:
asm
ld e, NN
end asm
Since e is ubyte, the result is truncated to its lower byte. In this case, ld e,(16384) is assembled as ld e,0 (as Britlion pointed). This is rather problematic, because when you want to use parenthesis for an arithmetical expression with something like LD HL, (Mylabel + 3) it doesn't know whether you are referring to LD HL, [Mylabel + 3] or LD HL, Mylabel + 3. A rule of thumb: it will try always the 1st case and will fallback to the 2nd if the first is not possible (as it happened in the example above).

Pasmo already allows brackets [ ] for memory indirection. Zxbasm will also do (in the near future).

This is all fine and well but the instruction itself does not exist. In the thread
<!-- l --><a class="postlink-local" href="http://www.boriel.com/forum/how-to-tutorials/topic468-30.html#p1444">how-to-tutorials/topic468-30.html#p1444</a><!-- l --> Britlion posted on the string bug thread there was
a mention of doing variables FreeBasic style and then ld [e], (NN) could work
but as it stands...

For the record I was trying to do: "ld e, (23693)" and it produced ld e, 143
and some byte that I can't quite remember. Well, garbage in, garbage out in
the debugging window.

It's a work in progress Big Grin
Reply
#13
The heap should be user locateble in either high or low memory.
For background go to:
<!-- l --><a class="postlink-local" href="http://www.boriel.com/forum/bug-reports/paper-border-rerun-bug-t626.html#p2244">bug-reports/paper-border-rerun-bug-t626.html#p2244</a><!-- l -->

If I had a game in two parts like a multiloader game and the game parts of
it go into high memory then it is quite possible that it would wipe out
the heap and the shared data there. Also you could specify heap location
manually. This is easier than POKEing and PEEKing but not as easy as
sharing the variables themselves and I think it is a good comprimize both
in terms of user ease and compiler writing and runtime size. What does waste
memory is having two runtimes in memory and the same time for a two part
program, multilevel or not. This also ensures what part of the program really
got control for example if you got to return to BASIC to load a multilevel disk
based game then the BASIC part has got the real control.

My two cents.
Reply
#14
By the same token the runtime should be user relocateable or at least make
it the first item loaded or we would have overwrite issues when different
parts are loaded if there is to be one runtime at a time. Even an option
where you can compile parts without the runtime as that would save
loading time.
Reply
#15
Darkstar Wrote:By the same token the runtime should be user relocateable or at least make
it the first item loaded or we would have overwrite issues when different
parts are loaded if there is to be one runtime at a time. Even an option
where you can compile parts without the runtime as that would save
loading time.

If that option is going to be implemented then there would have to be a
way to specify what to include in the runtime and what not and that
would override certain optimisations that are now in place so that
has to be taken into consideration. By overriding then I mean if a list
what to include is specified.
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)