From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 01 .C ══════════════════
(c) Ivan Roshchin, Moscow
Fido: 2:5020/689.53
ZXNet: 500:95/462.53
E-mail: bestview@mtu-net.ru
WWW: http://www.ivr.da.ru
Output of black and white images with gradations of brightness
═════════════════════════ ═════════════════════════
("Radiomir. Your computer" 8-10/2002)
(Last edit date: 09/28/2002)
As you probably already guessed :), we will talk about various
ways to display black and white images on the ZX Spectrum screen with
gradations of brightness. To be specific, let us assume that the size
images - 256*192, which corresponds to the ZX screen resolution
Spectrum, and the number of brightness gradations is 256.
It is clear that about real 256 gradations of brightness on the ZX Spectrum
we can only dream, so we can only deduce something
similar to the original image.
How can you determine which withdrawal method is better? Of course you can
compare the original and resulting images “by eye”. But
it is much more convenient to have an objective criterion - calculated by
to some formula a value (let’s denote it R) reflecting the difference
between these images: from 0 (the images are the same) to 1
(maximum difference).
About how exactly I determined this value, you can
read in Appendix 1. Next, next to each image,
obtained by using one or another withdrawal method, II will give the value of R.
Yes, but what image should we take as the initial one? Here
it:
┌─────────────────────────┐
│ bw_s_1.pcx │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
└─────────────────────────┘
Fig. 1
So, the first withdrawal method. All pixels with less brightness
128 is displayed in black, and with a brightness of 128 or more - in white:
┌─────────────────────────┐
│ bw_s_2.pcx │
│ │
│ │
│ │ R = 0.0906
│ │
│ │
│ │
│ │
└─────────────────────────┘
Fig. 2
The next method is using so-called “chunks”.
In each 4*4 square we display one of the 17 textures with the most
close brightness level (in the first texture all 16 pixelsblack, in the second - 15 black and 1 white... in the last - all 16
pixels white). Thus, 17 pseudogradations are obtained
brightness
┌─────────────────────────┐
│ bw_s_3.pcx │
│ │
│ │
│ │ R = 0.0708
│ │
│ │
│ │
│ │
└─────────────────────────┘
Fig. 3
Consider such an image (as well as images
obtained using the methods described below) preferably
not at close range, when it is perceived as a jumble
pixels, and from afar, when the brightness of neighboring pixels
averaged. If you cannot move away from the monitor long enough
distance, or from such a distance the image seems very
small, then you can achieve the same effect by “blurring”
image - to do this, just install it in front of the screen
filter made of translucent polyethylene film.
Another withdrawal method. For each pixel (not for
plot 4*4, as in the previous method) we determine the most
texture similar in brightness and display a pixel from this
textures. The coordinates of the pixel that is taken from the texture areare defined as the remainders from dividing the pixel coordinates by
image to texture size.
In Fig. 4a shows the image obtained when
using 17 4*4 textures, and in Fig. 4b - when using
256 textures 16*16 (actually there are 257, not 256, but, since in
the original image has only 256 gradations of brightness, one of the textures
not used).
┌─────────────────────────┐ ┌─────────────────────────┐
│ bw_s_4a.pcx │ │ bw_s_4b.pcx │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
└─────────────────────────┘ └─────────────────────────┘
R = 0.0683 R = 0.0680
a) b)
Fig. 4
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 01 .C ══════════════════
(c) Ivan Roshchin, Moscow
Fido: 2:5020/689.53
ZXNet: 500:95/462.53
E-mail: bestview@mtu-net.ru
WWW: http://www.ivr.da.ru
Output of black and white images with gradations of brightness
═════════════════════════ ═════════════════════════
("Radiomir. Your computer" 8-10/2002)
(Last edit date: 09/28/2002)
As you probably already guessed :), we will talk about various
ways to display black and white images on the ZX Spectrum screen with
gradations of brightness. To be specific, let us assume that the size
images - 256*192, which corresponds to the ZX screen resolution
Spectrum, and the number of brightness gradations is 256.
It is clear that about real 256 gradations of brightness on the ZX Spectrum
we can only dream, so we can only deduce something
similar to the original image.
How can you determine which withdrawal method is better? Of course you can
compare the original and resulting images “by eye”. But
it is much more convenient to have an objective criterion - calculated by
to some formula a value (let’s denote it R) reflecting the difference
between these images: from 0 (the images are the same) to 1
(maximum difference).
About how exactly I determined this value, you can
read in Appendix 1. Next, next to each image,
obtained by using one or another withdrawal method, II will give the value of R.
Yes, but what image should we take as the initial one? Here
it:
┌─────────────────────────┐
│ bw_s_1.pcx │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
└─────────────────────────┘
Fig. 1
So, the first withdrawal method. All pixels with less brightness
128 is displayed in black, and with a brightness of 128 or more - in white:
┌─────────────────────────┐
│ bw_s_2.pcx │
│ │
│ │
│ │ R = 0.0906
│ │
│ │
│ │
│ │
└─────────────────────────┘
Fig. 2
The next method is using so-called “chunks”.
In each 4*4 square we display one of the 17 textures with the most
close brightness level (in the first texture all 16 pixelsblack, in the second - 15 black and 1 white... in the last - all 16
pixels white). Thus, 17 pseudogradations are obtained
brightness
┌─────────────────────────┐
│ bw_s_3.pcx │
│ │
│ │
│ │ R = 0.0708
│ │
│ │
│ │
│ │
└─────────────────────────┘
Fig. 3
Consider such an image (as well as images
obtained using the methods described below) preferably
not at close range, when it is perceived as a jumble
pixels, and from afar, when the brightness of neighboring pixels
averaged. If you cannot move away from the monitor long enough
distance, or from such a distance the image seems very
small, then you can achieve the same effect by “blurring”
image - to do this, just install it in front of the screen
filter made of translucent polyethylene film.
Another withdrawal method. For each pixel (not for
plot 4*4, as in the previous method) we determine the most
texture similar in brightness and display a pixel from this
textures. The coordinates of the pixel that is taken from the texture areare defined as the remainders from dividing the pixel coordinates by
image to texture size.
In Fig. 4a shows the image obtained when
using 17 4*4 textures, and in Fig. 4b - when using
256 textures 16*16 (actually there are 257, not 256, but, since in
the original image has only 256 gradations of brightness, one of the textures
not used).
┌─────────────────────────┐ ┌─────────────────────────┐
│ bw_s_4a.pcx │ │ bw_s_4b.pcx │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
└─────────────────────────┘ └─────────────────────────┘
R = 0.0683 R = 0.0680
a) b)
Fig. 4
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 02.C ══════════════════
This was, so to speak, an introduction. :) Already considered
known withdrawal methods, which, as you may have noticed, are not
are of sufficient quality. And now we will talk about
on how to get the most out of the ZX Spectrum. I
I’ll tell you about ways to improve the quality of output
The images are great!
Look: on the ZX Spectrum we have 8 colors available with two
gradations of brightness (bright=0 and bright=1). Because the color is black
with bright=1 it remains black, in total there are not 16 colors, but
15. When displaying in black and white, these 15 colors
turn into 15 gradations of brightness. Through their use and
You can improve the image quality!
Naturally, this requires that the ZX Spectrum be
connected to a black and white monitor or TV (or to
color, but with the ability to switch to black and white mode).
But even if this is not the case in your case, I still advise
read the article to the end: suddenly something turns out to be useful.
Let me immediately note that a method based on this effect
output of black and white images with gradations of brightness can be
used on computers other than the ZX Spectrum, on which
The image may be in color, but there is no grayscale.
By the way, it seems to me (I haven’t tested it in practice) thatgetting a black and white image on a color monitor is enough
build a simple adapter: three signals R, G, B coming from
computer, convert it into one (black and white) and send it to
monitor inputs R, G, B, that's all. At the same time, from the details
only a few resistances are needed.
So how to display an image? Obviously, first you need
determine the brightness of each of the available 15 gradations - the number in
range 0-255. The story about how to do this turned out to be
quite voluminous, and I decided to include it in Appendix 2. A
Now we will assume that the brightnesses have been calculated and we have received
values a0-a7 for colors 0-7 with bright=0 and values b0-b7
for the same colors with bright=1.
Since the attributes are set for familiarity (8*8 pixels),
We will display the image familiarly. Let's consider the process
output of one familiar place.
Let MIN_PIX and MAX_PIX be the minimum and
maximum brightness levels in the current familiarity of the original
images. Let MIN and MAX be the brightness of paper and
ink (if bright is selected) in the current familiarity
output image. Knowing MIN_PIX and MAX_PIX, you need to determine
familiarity attributes (ink, paper and bright) so that
conditions: MIN <= MIN_PIX, MAX >= MAX_PIX. Obviously, in the majority
In some cases this can be done in more than one way. Then from
of all possible pairs (MIN, MAX) we choose such that the differencebetween MAX and MIN was the smallest.
In order not to go through all 128 possible combinations of ink, paper and
bright, it’s convenient to do the following. First we check
is the condition MAX_PIX > a(7) satisfied? If yes, then bright is not
may be equal to 0, i.e. bright=1. Among b0-b7 we find
maximum b(i), under which the condition b(i) <=
MIN_PIX, and the minimum b(j) at which the condition is satisfied
b(j) >= MAX_PIX. Then paper=i, and ink=j.
If MAX_PIX <= a(7), you will have to consider two options:
bright=0 and bright=1. Find b(i) and b(j) as described above.
Then, among a0-a7, we find the maximum a(k), at which
the condition a(k) <= MIN_PIX is satisfied, and the minimum a(l), with
which fulfills the condition a(l) >= MAX_PIX. Now we need to choose
of two pairs (b(i),b(j)), (a(k),a(l)) such that the difference between
values are less. If this is the first pair, then bright=1, ink=j,
paper=i. If the second pair, then bright=0, ink=l, paper=k.
After the attributes of the familiarity are determined, it remains
find out which pixels in it will be ink colors and which ones will be paper colors. B
In the previous output method we received 257 pseudogradations of brightness
between black and white using 257 16*16 textures. Well, in this
way we can get using the same textures 257 pseudo-
gradations between MIN and MAX. For each pixel we define
texture that is closest in brightness and display the pixel on the screen
from this texture.This is what happens with this method of output. Isn't it true?
Has the image quality improved significantly?
┌─────────────────────────┐
│ bw_s_5.pcx │
│ │
│ │
│ │ R = 0.0106
│ │
│ │
│ │
│ │
└─────────────────────────┘
Fig. 5
The procedure for outputting an image in this way is given in
Appendix 3. There you will also find procedures for methods
described below.
To further improve image quality, you can
use the so-called multicolor effect. Its essence is
the following: if you change the familiarity attribute when the beam is already
drew part of it on the screen, then the rest of the familiarity
will be drawn with the new attribute value. It's possible
increase vertical attribute resolution.
If you need to double the resolution (i.e. set your
attribute for each half of the familiarity area - an area measuring 8*4
pixel), it is more convenient not to overwrite attribute values when
drawing the image, and do this: build the top
half familiarize themselves with their attributes on one screen, the bottomhalves with their attributes - on the other, and then switch
screens every time the next 4 lines of pixels are drawn
(i.e. the top or bottom half of the line is familiar).
This is what happens with this output method:
┌─────────────────────────┐
│ bw_s_6.pcx │
│ │
│ │
│ │ R = 0.0090
│ │
│ │
│ │
│ │
└─────────────────────────┘
Fig. 6
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 02.C ══════════════════
This was, so to speak, an introduction. :) Already considered
known withdrawal methods, which, as you may have noticed, are not
are of sufficient quality. And now we will talk about
on how to get the most out of the ZX Spectrum. I
I’ll tell you about ways to improve the quality of output
The images are great!
Look: on the ZX Spectrum we have 8 colors available with two
gradations of brightness (bright=0 and bright=1). Because the color is black
with bright=1 it remains black, in total there are not 16 colors, but
15. When displaying in black and white, these 15 colors
turn into 15 gradations of brightness. Through their use and
You can improve the image quality!
Naturally, this requires that the ZX Spectrum be
connected to a black and white monitor or TV (or to
color, but with the ability to switch to black and white mode).
But even if this is not the case in your case, I still advise
read the article to the end: suddenly something turns out to be useful.
Let me immediately note that a method based on this effect
output of black and white images with gradations of brightness can be
used on computers other than the ZX Spectrum, on which
The image may be in color, but there is no grayscale.
By the way, it seems to me (I haven’t tested it in practice) thatgetting a black and white image on a color monitor is enough
build a simple adapter: three signals R, G, B coming from
computer, convert it into one (black and white) and send it to
monitor inputs R, G, B, that's all. At the same time, from the details
only a few resistances are needed.
So how to display an image? Obviously, first you need
determine the brightness of each of the available 15 gradations - the number in
range 0-255. The story about how to do this turned out to be
quite voluminous, and I decided to include it in Appendix 2. A
Now we will assume that the brightnesses have been calculated and we have received
values a0-a7 for colors 0-7 with bright=0 and values b0-b7
for the same colors with bright=1.
Since the attributes are set for familiarity (8*8 pixels),
We will display the image familiarly. Let's consider the process
output of one familiar place.
Let MIN_PIX and MAX_PIX be the minimum and
maximum brightness levels in the current familiarity of the original
images. Let MIN and MAX be the brightness of paper and
ink (if bright is selected) in the current familiarity
output image. Knowing MIN_PIX and MAX_PIX, you need to determine
familiarity attributes (ink, paper and bright) so that
conditions: MIN <= MIN_PIX, MAX >= MAX_PIX. Obviously, in the majority
In some cases this can be done in more than one way. Then from
of all possible pairs (MIN, MAX) we choose such that the differencebetween MAX and MIN was the smallest.
In order not to go through all 128 possible combinations of ink, paper and
bright, it’s convenient to do the following. First we check
is the condition MAX_PIX > a(7) satisfied? If yes, then bright is not
may be equal to 0, i.e. bright=1. Among b0-b7 we find
maximum b(i), under which the condition b(i) <=
MIN_PIX, and the minimum b(j) at which the condition is satisfied
b(j) >= MAX_PIX. Then paper=i, and ink=j.
If MAX_PIX <= a(7), you will have to consider two options:
bright=0 and bright=1. Find b(i) and b(j) as described above.
Then, among a0-a7, we find the maximum a(k), at which
the condition a(k) <= MIN_PIX is satisfied, and the minimum a(l), with
which fulfills the condition a(l) >= MAX_PIX. Now we need to choose
of two pairs (b(i),b(j)), (a(k),a(l)) such that the difference between
values are less. If this is the first pair, then bright=1, ink=j,
paper=i. If the second pair, then bright=0, ink=l, paper=k.
After the attributes of the familiarity are determined, it remains
find out which pixels in it will be ink colors and which ones will be paper colors. B
In the previous output method we received 257 pseudogradations of brightness
between black and white using 257 16*16 textures. Well, in this
way we can get using the same textures 257 pseudo-
gradations between MIN and MAX. For each pixel we define
texture that is closest in brightness and display the pixel on the screen
from this texture.This is what happens with this method of output. Isn't it true?
Has the image quality improved significantly?
┌─────────────────────────┐
│ bw_s_5.pcx │
│ │
│ │
│ │ R = 0.0106
│ │
│ │
│ │
│ │
└─────────────────────────┘
Fig. 5
The procedure for outputting an image in this way is given in
Appendix 3. There you will also find procedures for methods
described below.
To further improve image quality, you can
use the so-called multicolor effect. Its essence is
the following: if you change the familiarity attribute when the beam is already
drew part of it on the screen, then the rest of the familiarity
will be drawn with the new attribute value. It's possible
increase vertical attribute resolution.
If you need to double the resolution (i.e. set your
attribute for each half of the familiarity area - an area measuring 8*4
pixel), it is more convenient not to overwrite attribute values when
drawing the image, and do this: build the top
half familiarize themselves with their attributes on one screen, the bottomhalves with their attributes - on the other, and then switch
screens every time the next 4 lines of pixels are drawn
(i.e. the top or bottom half of the line is familiar).
This is what happens with this output method:
┌─────────────────────────┐
│ bw_s_6.pcx │
│ │
│ │
│ │ R = 0.0090
│ │
│ │
│ │
│ │
└─────────────────────────┘
Fig. 6
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 03.C ══════════════════
Obviously, the maximum possible is its own attribute for each
familiarity lines (8 pixels). Here's what you get
image:
┌─────────────────────────┐
│ bw_s_7.pcx │
│ │
│ │
│ │ R = 0.0071
│ │
│ │
│ │
│ │
└─────────────────────────┘
Fig. 7
In the practical implementation of the maximum attribute
permissions, however, a problem arises. The thing is that for 224
clock cycle (time to draw one line) 32 bytes need to be updated
string attributes, but this is not so easy to do: ordinary
Data transfer methods take much longer. B
Following the procedure given in Appendix 3, I solved this problem like this:
not the entire image is displayed, but a window 11 characters wide,
which can be moved left and right. You can try
increase the width of the displayed image area using
methods for fast data transfer - for example, described in [5].
There is another way to improve quality. Can be formedtwo images such that the average between them is the most
similar to the original, and then quickly alternate them - while
the viewer will just see the middle image.
Let the attributes of both images be the same. If earlier
the brightness of a pixel in a familiar location could be one of two: MIN or
MAX, then when quickly alternating two images it can be
already one of three: MIN, MAX or (MIN+MAX)/2 (when on one
in the image the pixel brightness is MIN, and in the other - MAX). Due to
This is what improves quality. If earlier we received 257
pseudo-gradations of brightness from MIN to MAX using 257 different
textures 16*16, then now we get 257 pseudogradations from MIN to
(MIN+MAX)/2 and another 257 - from (MIN+MAX)/2 to MAX, and in total, like this
Thus, there will be 513 pseudogradations from MIN to MAX.
When such two images are formed, their attributes
(they are the same) are defined in the same way as before. For everyone
pixel we determine which of the 513 pseudogradations (from 0 to 512)
is closest to it in brightness. If the pseudogradation number
is less than 256, then the corresponding pixel is in the first image
turn it off, and on the second - take it from the texture with a number equal to
pseudogradation number. If the pseudogradation number is greater or
is 256, then in the first image we turn on the pixel, and in
the second - we take it from a texture with a number equal to the number
pseudogradation reduced by 256.
Using this method to output the original image,we get these two images:
┌─────────────────────────┐ ┌─────────────────────────┐
│ bw_s_8a.pcx │ │ bw_s_8b.pcx │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
└─────────────────────────┘ └─────────────────────────┘
a) b)
Fig. 8
When they alternate quickly, we will see the middle image -
here it is:
┌─────────────────────────┐
│ bw_s_9.pcx │
│ │
│ │
│ │ R = 0.0026
│ │
│ │
│ │
│ │
└─────────────────────────┘
Fig. 9
However, when considering it, you will be disappointed due toquite noticeable flicker with a frequency of 25 Hz. Is it possible
reduce, and if so, how? I'll try to explain with an example.
Suppose we need to get a gray color in some area of the screen for
due to the quick change of white and black. You can do this: in one
show this entire area white in one frame, and black in another
(Fig. 10a). The flickering will be very noticeable. Is it possible in
display a “checkerboard” texture in this area in one frame, and in
in another frame - the same texture, but in which instead of white
pixels are black and vice versa (Fig. 10b). Then there will be flickering
much less, and from afar it will not be noticeable at all.
┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐
│████████│ │ │ │██ ██ │ │ ██ ██│
│████████│ + │ │ │ ██ ██│ + │██ ██ │
│████████│ │ │ │██ ██ │ │ ██ ██│
│████████│ │ │ │ ██ ██│ │██ ██ │
└────────┘ └────────┘ └────────┘ └────────┘
a) b)
Fig. 10
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 04.C ══════════════════
Why does this happen? When looking at the image
from afar, the brightness of neighboring pixels is averaged, and since in
In the second case, neighboring pixels flicker in antiphase, then they
the average brightness in both one and the other frame will be the same,
and as a result the image will not flicker.
In other words, the average brightness of fairly small areas on
the first and second images should be approximately the same, and
This can be achieved if the neighboring flickering pixels are in
antiphase.
In our case (Fig. 8) the images are very different
from each other, and therefore the flickering when they change is very noticeable.
To make the images more similar to each other, let's do
the following: looking at the pixels (to be specific, on the left
to the right and from top to bottom), we define flickering (i.e. different
in the first and second images); let's make the first such pixel
dark in the first image and light in the second, the next one is
on the contrary, light in the first image and dark in the second (i.e.
it will be in antiphase with the previous one), and so on.
This is what those shown in Fig. turn into. 8 images
after this processing:
┌─────────────────────────┐ ┌─────────────────────────┐
│ bw_s_11a.pcx │ │ bw_s_11b.pcx │
│ │ │ ││ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
└─────────────────────────┘ └─────────────────────────┘
a) b)
Fig. 11
It's immediately obvious that they are now much more similar to each other.
friend. Flickering when they change quickly becomes significantly
less, and from a distance it is not noticeable at all. Resulting
the image will, of course, be the same (Fig. 9): after all, from
changing the places of the terms does not change the sum.
By the way, here’s some advice on how to avoid seeing flickering even from close up
distances. Take a piece of paper, poke a hole in it and
look at the screen through this hole. Amount of light
There will be less light entering the eye, and the flicker will be greatly reduced.
Rapid alternation of two images can be combined with
multicolor, which will further improve the quality. This is what happens
when for each half of the familiar place its own attribute is set:
┌─────────────────────────┐
│ bw_s_12.pcx │
│ ││ │
│ │ R = 0.0022
│ │
│ │
│ │
│ │
└─────────────────────────┘
Fig. 12
But - when a different attribute is set for each line
familiar places:
┌─────────────────────────┐
│ bw_s_13.pcx │
│ │
│ │
│ │ R = 0.0017
│ │
│ │
│ │
│ │
└─────────────────────────┘
Fig. 13
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 05 .C ══════════════════
The methods described above were illustrated using the example of one
original image. Below are examples for a few more
images (on the left - the original one, on the right - the resulting one
output). A method was used with rapid alternation of two
images and multicolor, attributes were set for each
familiarity lines.
(By the way, scanning photographs that served as source
images, performed at my request by Alexey Letaev -
thank you!)
┌─────────────────────────┐ ┌─────────────────────────┐
│ bw_s_14a.pcx ││ bw_s_14b.pcx │
│ ││ │
│ ││ │
│ ││ │R = 0.0020
│ ││ │
│ ││ │
│ ││ │
│ ││ │
└─────────────────────────┘ └─────────────────────────┘
┌─────────────────────────┐ ┌─────────────────────────┐
│ bw_s_14c.pcx ││ bw_s_14d.pcx │
│ ││ │
│ ││ │
│ ││ │R = 0.0021│ ││ │
│ ││ │
│ ││ │
│ ││ │
└─────────────────────────┘└─────────────────────────┘
┌─────────────────────────┐┌─────────────────────────┐
│ bw_s_14e.pcx ││ bw_s_14f.pcx │
│ ││ │
│ ││ │
│ ││ │R = 0,0029
│ ││ │
│ ││ │
│ ││ │
│ ││ │
└─────────────────────────┘└─────────────────────────┘
┌─────────────────────────┐┌─────────────────────────┐
│ bw_s_14g.pcx ││ bw_s_14h.pcx │
│ ││ │
│ ││ │
│ ││ │R = 0,0023
│ ││ │
│ ││ │
│ ││ │
│ ││ │
└─────────────────────────┘└─────────────────────────┘
Rice. 14
To see even more detail in the image, you can:
firstly, use output with magnification, and secondly,
increase the contrast of the displayed image area.
When outputting with magnification, we will not be able to see everything at once.
picture, but due to this we will be able to distinguish on the visible fragment
more details. Here is an example: in Fig. 15a shows the central
part of fig. 5, doubled for ease of comparison, and
fig. 15b - what happens if you double the central
part directly in the withdrawal process.
┌─────────────────────────┐ ┌─────────────────────────┐
│ bw_s_15a.pcx │ │ bw_s_15b.pcx │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
└─────────────────────────┘ └─────────────────────────┘
a) b)
Fig. 15
The output procedures given in Appendix 3 can outputthe specified area of the image with a magnification of 2, 4, 8... times.
I think it won't be difficult for you to figure out how to increase
image a number of times that is not a power of two.
Now about increasing the contrast. If in the output
image (or in the output part of the image) is the darkest
point lighter than 0 and/or the lightest point darker than 255, then you can
"stretch" the brightness range to 0 - 255 using the following formula:
A-Amin
A'= 255 ───────────
Amax-Amin
where A is the original brightness,
A' - new brightness,
Amin - brightness of the darkest point,
Amax - brightness of the brightest point.
After this transformation we will see dark areas
images are darker than necessary, and light images are lighter, but at the same time
more details can be discerned. In Fig. 16 you can
see the image obtained as in Fig. 15b, but with an increase
contrast.
┌─────────────────────────┐
│ bw_s_16.pcx │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
└─────────────────────────┘Rice. 16
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 05 .C ══════════════════
The methods described above were illustrated using the example of one
original image. Below are examples for a few more
images (on the left - the original one, on the right - the resulting one
output). A method was used with rapid alternation of two
images and multicolor, attributes were set for each
familiarity lines.
(By the way, scanning photographs that served as source
images, performed at my request by Alexey Letaev -
thank you!)
┌─────────────────────────┐ ┌─────────────────────────┐
│ bw_s_14a.pcx ││ bw_s_14b.pcx │
│ ││ │
│ ││ │
│ ││ │R = 0.0020
│ ││ │
│ ││ │
│ ││ │
│ ││ │
└─────────────────────────┘ └─────────────────────────┘
┌─────────────────────────┐ ┌─────────────────────────┐
│ bw_s_14c.pcx ││ bw_s_14d.pcx │
│ ││ │
│ ││ │
│ ││ │R = 0.0021│ ││ │
│ ││ │
│ ││ │
│ ││ │
└─────────────────────────┘└─────────────────────────┘
┌─────────────────────────┐┌─────────────────────────┐
│ bw_s_14e.pcx ││ bw_s_14f.pcx │
│ ││ │
│ ││ │
│ ││ │R = 0,0029
│ ││ │
│ ││ │
│ ││ │
│ ││ │
└─────────────────────────┘└─────────────────────────┘
┌─────────────────────────┐┌─────────────────────────┐
│ bw_s_14g.pcx ││ bw_s_14h.pcx │
│ ││ │
│ ││ │
│ ││ │R = 0,0023
│ ││ │
│ ││ │
│ ││ │
│ ││ │
└─────────────────────────┘└─────────────────────────┘
Rice. 14
To see even more detail in the image, you can:
firstly, use output with magnification, and secondly,
increase the contrast of the displayed image area.
When outputting with magnification, we will not be able to see everything at once.
picture, but due to this we will be able to distinguish on the visible fragment
more details. Here is an example: in Fig. 15a shows the central
part of fig. 5, doubled for ease of comparison, and
fig. 15b - what happens if you double the central
part directly in the withdrawal process.
┌─────────────────────────┐ ┌─────────────────────────┐
│ bw_s_15a.pcx │ │ bw_s_15b.pcx │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
└─────────────────────────┘ └─────────────────────────┘
a) b)
Fig. 15
The output procedures given in Appendix 3 can outputthe specified area of the image with a magnification of 2, 4, 8... times.
I think it won't be difficult for you to figure out how to increase
image a number of times that is not a power of two.
Now about increasing the contrast. If in the output
image (or in the output part of the image) is the darkest
point lighter than 0 and/or the lightest point darker than 255, then you can
"stretch" the brightness range to 0 - 255 using the following formula:
A-Amin
A'= 255 ───────────
Amax-Amin
where A is the original brightness,
A' - new brightness,
Amin - brightness of the darkest point,
Amax - brightness of the brightest point.
After this transformation we will see dark areas
images are darker than necessary, and light images are lighter, but at the same time
more details can be discerned. In Fig. 16 you can
see the image obtained as in Fig. 15b, but with an increase
contrast.
┌─────────────────────────┐
│ bw_s_16.pcx │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
└─────────────────────────┘Rice. 16
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════06 ══════════════════
Gamma correction
───────────────
The gradations of brightness in the original image are indicated by numbers
from 0 to 255. We, in fact, considered these numbers to be brightness
pixels (referring to the definition of brightness given in Appendix
2). That is, we assumed that the brightness of a pixel depends linearly on
number corresponding to this pixel in the image: if,
for example, one pixel corresponds to the number 10, and another - 20,
then the second pixel is twice as bright as the first.
However, in general this is not the case. Dependency is not here
linear, but power (the exponent is called “gamma”).
Linear dependence is only a special case when gamma is equal to 1.
In Fig. Figure 17 shows graphs for three possible cases (along the axis
x is the value corresponding to the pixel in the image,
and the y-axis is the actual brightness of the pixel).
┌───────────────────┐┌───────── ──────────┐┌───────────────────┐
│ bw_s_17a.pcx ││ bw_s_17b.pcx ││ bw_s_17c.pcx │
│ ││ ││ │
│ ││ ││ │
│ ││ ││ │
│ ││ ││ │
│ ││ ││ │└───────────────────┘└───────── ──────────┘└───────────────────┘
a) gamma=1 b) gamma>1 c) gamma<1
Fig. 17
As we can see, if the gamma is greater than 1, then in an image with a larger
information about dark areas is presented accurately and with less
accuracy - about light ones, and if the gamma is less than 1 - vice versa.
To correctly output images with a gamma other than
units, and gamma correction is needed. From the pixel value (we denote
its x) we must get its brightness (let's call it y). If and
value, and brightness are numbers from 0 to 255, then the formula is:
y=255*(x/255)^Gamma
It is convenient to first calculate y for all x from 0 to 255 by putting
values into the table, and when outputting, simply take from the table
ready value.
The withdrawal procedures given in Appendix 3 can be performed
gamma correction if you set the conditional compilation flag to 1
GAMMA_CORR. In this case, the default gamma value is 1.5.
For a different value, it will be necessary to recalculate the correction table with
using the BASIC program given there. By the way, this
you can run the program just to see what
graphs are obtained at different gamma values.
How can you find out what gamma value the output image has?
If you are taking this image from a png file (or
some other format where the gamma value is specified infile itself), then there are no difficulties. And if you (like me)
you will take an image from a pcx file, where there is no such information
contained? Then you will have to select this coefficient by experienced
way. Try to display the image first without
gamma correction. If it looks natural, then nothing else
and don't. If it is too dark, it means the gamma is greater than 1;
if it is too light, the gamma is less than 1.
By the way: images with a gamma greater than 1 will be displayed more
precisely due to the fact that they contain more information about the dark
areas, and the distribution of Spectrum brightness gradations (see.
fig. 19 in Appendix 2) is exactly that (at least for
me) that dark areas are reproduced much more accurately
light.
Working with pcx files
────────────────────
The source images must be taken from somewhere. I read them from
files in pcx format. Appendix 4 provides two procedures for
working with such files. The first procedure reads data from
pcx file and forms an array of pixel data in memory. Second
the procedure performs the opposite task - writes a pcx file using
pixel data located in memory (it is with its help that I
prepared all pcx files with drawings for this article).
Another procedure given there directly with
pcx files does not work, but will be useful if you need
convert the image to Spectrum format (6912 bytes)to a black and white pcx file. It forms an array of data in memory
about the pixels of such an image based on specified gradation values
brightness, and based on this data you can already create a pcx file.
And one more small addition
───────────────────────────────
If the source image is color and you want to output it
in black and white, then to obtain the pixel brightness by
Given the known values of the components R, G, B, use the following
formula:
Y = 0.299R + 0.587G + 0.114B
Since brightness needs to be calculated for each pixel, and even
image the size of a Spectrum screen - 256*192 -
contains 49152 pixels, it is clear that this calculation must
be as fast as possible. It is convenient to use the tabular method.
Let there be a table with a length of 768 (i.e. 3*256) bytes,
starting with an address that is a multiple of 256 (let’s denote this address
TAB_RGB). Let the first 256 bytes of this table contain
function values y=0.299x for x from 0 to 255, rounded to
integers (for practical use such accuracy is quite
sufficient), in the second 256 bytes - the function values y=0.587x,
and in the third 256 bytes - the function values y=0.114x (also for x
from 0 to 255).
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════06 ══════════════════
Gamma correction
───────────────
The gradations of brightness in the original image are indicated by numbers
from 0 to 255. We, in fact, considered these numbers to be brightness
pixels (referring to the definition of brightness given in Appendix
2). That is, we assumed that the brightness of a pixel depends linearly on
number corresponding to this pixel in the image: if,
for example, one pixel corresponds to the number 10, and another - 20,
then the second pixel is twice as bright as the first.
However, in general this is not the case. Dependency is not here
linear, but power (the exponent is called “gamma”).
Linear dependence is only a special case when gamma is equal to 1.
In Fig. Figure 17 shows graphs for three possible cases (along the axis
x is the value corresponding to the pixel in the image,
and the y-axis is the actual brightness of the pixel).
┌───────────────────┐┌───────── ──────────┐┌───────────────────┐
│ bw_s_17a.pcx ││ bw_s_17b.pcx ││ bw_s_17c.pcx │
│ ││ ││ │
│ ││ ││ │
│ ││ ││ │
│ ││ ││ │
│ ││ ││ │└───────────────────┘└───────── ──────────┘└───────────────────┘
a) gamma=1 b) gamma>1 c) gamma<1
Fig. 17
As we can see, if the gamma is greater than 1, then in an image with a larger
information about dark areas is presented accurately and with less
accuracy - about light ones, and if the gamma is less than 1 - vice versa.
To correctly output images with a gamma other than
units, and gamma correction is needed. From the pixel value (we denote
its x) we must get its brightness (let's call it y). If and
value, and brightness are numbers from 0 to 255, then the formula is:
y=255*(x/255)^Gamma
It is convenient to first calculate y for all x from 0 to 255 by putting
values into the table, and when outputting, simply take from the table
ready value.
The withdrawal procedures given in Appendix 3 can be performed
gamma correction if you set the conditional compilation flag to 1
GAMMA_CORR. In this case, the default gamma value is 1.5.
For a different value, it will be necessary to recalculate the correction table with
using the BASIC program given there. By the way, this
you can run the program just to see what
graphs are obtained at different gamma values.
How can you find out what gamma value the output image has?
If you are taking this image from a png file (or
some other format where the gamma value is specified infile itself), then there are no difficulties. And if you (like me)
you will take an image from a pcx file, where there is no such information
contained? Then you will have to select this coefficient by experienced
way. Try to display the image first without
gamma correction. If it looks natural, then nothing else
and don't. If it is too dark, it means the gamma is greater than 1;
if it is too light, the gamma is less than 1.
By the way: images with a gamma greater than 1 will be displayed more
precisely due to the fact that they contain more information about the dark
areas, and the distribution of Spectrum brightness gradations (see.
fig. 19 in Appendix 2) is exactly that (at least for
me) that dark areas are reproduced much more accurately
light.
Working with pcx files
────────────────────
The source images must be taken from somewhere. I read them from
files in pcx format. Appendix 4 provides two procedures for
working with such files. The first procedure reads data from
pcx file and forms an array of pixel data in memory. Second
the procedure performs the opposite task - writes a pcx file using
pixel data located in memory (it is with its help that I
prepared all pcx files with drawings for this article).
Another procedure given there directly with
pcx files does not work, but will be useful if you need
convert the image to Spectrum format (6912 bytes)to a black and white pcx file. It forms an array of data in memory
about the pixels of such an image based on specified gradation values
brightness, and based on this data you can already create a pcx file.
And one more small addition
───────────────────────────────
If the source image is color and you want to output it
in black and white, then to obtain the pixel brightness by
Given the known values of the components R, G, B, use the following
formula:
Y = 0.299R + 0.587G + 0.114B
Since brightness needs to be calculated for each pixel, and even
image the size of a Spectrum screen - 256*192 -
contains 49152 pixels, it is clear that this calculation must
be as fast as possible. It is convenient to use the tabular method.
Let there be a table with a length of 768 (i.e. 3*256) bytes,
starting with an address that is a multiple of 256 (let’s denote this address
TAB_RGB). Let the first 256 bytes of this table contain
function values y=0.299x for x from 0 to 255, rounded to
integers (for practical use such accuracy is quite
sufficient), in the second 256 bytes - the function values y=0.587x,
and in the third 256 bytes - the function values y=0.114x (also for x
from 0 to 255).
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════07 ══════════════════
Here is a 32-byte procedure for constructing such a table:
MK_T_RGB LD BC,TAB_RGB+#2FF ;Address of the last byte of the table.
LD H,29 ;0.114*256
CALL MK_T_RGB_1
LD H,150 ;0.587*256
CALL MK_T_RGB_1
LD H,77 ;0.299*256
MK_T_RGB_1 XOR A
LD L,A
LD D,A
LD E,H
MK_T_RGB_2 SBC HL,DE
RET C
;Write the rounded result:
LD A,H
BIT 7,L
JR Z,MK_T_RGB_3
INC A
MK_T_RGB_3 LD (BC),A
DEC B.C.
JR MK_T_RGB_2
Using this table you can calculate brightness very quickly
pixel - for example, using the following program fragment:
;Input: values of components R, G, B - in registers L, D, E
; respectively.
;Output: calculated Y value - in accumulator.
;Length: 9 bytes.
;Execution time: 44 clock cycles.
LD H,TAB_RGB/256
LD A,(HL);A=0.299R
INC H
LD L,D
ADD A,(HL) ;A=0.299R+0.587G
INC H
LD L,E
ADD A,(HL) ;A=0.299R+0.587G+0.114B
If you need to process a group of pixels at once, it will be more profitable
do not load register H every time, but load it only oncetimes, and then only change with the INC and DEC commands (processing by
two pixels per loop) as in the procedure below:
;Input: IX - source data start address (R1,G1,B1,R2,G2,B2...);
; DE - where to put the result (Y1,Y2...);
; BC - number of processed pixels (must be
; even) divided by 2.
;Length: 49 bytes.
CALC_Y EXX
LD DE,6
EXX
LD H,TAB_RGB/256
CALC_Y_1 LD L,(IX) ;L=R1
LD A,(HL);A=0.299R1
INC H
LD L,(IX+1) ;L=G1
ADD A,(HL) ;A=0.299R1+0.587G1
INC H
LD L,(IX+2) ;L=B1
ADD A,(HL) ;A=0.299R1+0.587G1+0.114B1
LD(DE),A
INC DE
LD L,(IX+5) ;L=B2
LD A,(HL);A=0.114B2
DEC H
LD L,(IX+4) ;L=G2
ADD A,(HL) ;A=0.114B2+0.587G2
DEC H
LD L,(IX+3) ;L=R2
ADD A,(HL) ;A=0.114B2+0.587G2+0.299R2
LD(DE),A
INC DE
EXX
ADD IX,DE ;IX:=IX+6
EXX
DEC B.C.
LD A,B
OR C
JR NZ,CALC_Y_1
RET
If it is known in advance that DE at the input will be even (or
odd), then the first (or, accordingly, second) commandINC DE can be replaced with the faster INC E.
If no more than 512 pixels are processed, then
counter, you can use not a register pair BC, but only
one register B, using the DJNZ instruction to loop.
Literature
──────────
1. A. Bordachev. "About the PCX format." Information library
technology, vol. 8. Moscow, "Infoart", 1993.
2. T. Kencl. "Internet File Formats". St. Petersburg, "Peter", 1997.
3. K. Bochkov. "Scanning is so easy...". "PC World"
11/2000.
4. S. Kashchavtsev. "The Magic Power of Curves - II". "Computerra"
49-50/1998.
5. E. Zaretsky. "Fast graphics output." "Radio amateur. Yours
computer" 5.6/2001.
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════08 ══════════════════
Appendix 1
────────────
Here I will talk about how I determined R - the difference between
two images.
Let's calculate the standard deviation:
h-1 l-1
┌── ┌── 2
> > (p1(x,y)-p2(x,y))
└── └──
y=0 x=0
r1 = ─────────────────────────
l*h
where h is the height of the compared images,
l - width,
p1(x,y) - brightness of the pixel with coordinates (x,y) in the first
image, reduced to the range 0-1 (if,
for example, in an image there are 256 gradations of brightness, then
the pixel value must be divided by 255),
p2(x,y) - the same for the second image.
Next, we similarly calculate the standard deviation for
of all sections 2*2 pixels, considering the brightness of the section to be average
brightness of its pixels:
h-2 l-2
┌── ┌── 2
> > (m1(x,y)-m2(x,y))
└── └──
y=0 x=0
r2 = ─────────────────────────
(l-1)*(h-1)
1 1 1 1┌── ┌── ┌── ┌──
> > p1(x+i,y+j) > > p2(x+i,y+j)
└── └── └── └──
j=0 i=0 j=0 i=0
where m1(x,y) = ──────────────────, m2(x,y) = ──────────────────.
4 4
And for 4*4 pixel areas:
h-4 l-4
┌── ┌── 2
> > (n1(x,y)-n2(x,y))
└── └──
y=0 x=0
r3 = ─────────────────────────
(l-3)*(h-3)
3 3 3 3
┌── ┌── ┌── ┌──
> > p1(x+i,y+j) > > p2(x+i,y+j)
└── └── └── └──
j=0 i=0 j=0 i=0
where n1(x,y) = ──────────────────, n2(x,y) = ──────────────────.
16 16
The value of r1 reflects the difference at close range (when
individual pixels can be distinguished in the compared images).
The value r2 is the difference from the average distance (when it is impossible
distinguish details less than 2*2 pixels). Finally, the sizer3 - difference from a long distance (when details are no longer visible
less than 4*4 pixels). Let's define R as the average between r1, r2 and
r3:
r1 + r2 + r3
R = ──────────────
3
R (like r1, r2, r3) can take values in the range from
0 to 1, with 0 corresponding to a complete match of the compared
images, and 1 - maximum difference.
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════08 ══════════════════
Appendix 1
────────────
Here I will talk about how I determined R - the difference between
two images.
Let's calculate the standard deviation:
h-1 l-1
┌── ┌── 2
> > (p1(x,y)-p2(x,y))
└── └──
y=0 x=0
r1 = ─────────────────────────
l*h
where h is the height of the compared images,
l - width,
p1(x,y) - brightness of the pixel with coordinates (x,y) in the first
image, reduced to the range 0-1 (if,
for example, in an image there are 256 gradations of brightness, then
the pixel value must be divided by 255),
p2(x,y) - the same for the second image.
Next, we similarly calculate the standard deviation for
of all sections 2*2 pixels, considering the brightness of the section to be average
brightness of its pixels:
h-2 l-2
┌── ┌── 2
> > (m1(x,y)-m2(x,y))
└── └──
y=0 x=0
r2 = ─────────────────────────
(l-1)*(h-1)
1 1 1 1┌── ┌── ┌── ┌──
> > p1(x+i,y+j) > > p2(x+i,y+j)
└── └── └── └──
j=0 i=0 j=0 i=0
where m1(x,y) = ──────────────────, m2(x,y) = ──────────────────.
4 4
And for 4*4 pixel areas:
h-4 l-4
┌── ┌── 2
> > (n1(x,y)-n2(x,y))
└── └──
y=0 x=0
r3 = ─────────────────────────
(l-3)*(h-3)
3 3 3 3
┌── ┌── ┌── ┌──
> > p1(x+i,y+j) > > p2(x+i,y+j)
└── └── └── └──
j=0 i=0 j=0 i=0
where n1(x,y) = ──────────────────, n2(x,y) = ──────────────────.
16 16
The value of r1 reflects the difference at close range (when
individual pixels can be distinguished in the compared images).
The value r2 is the difference from the average distance (when it is impossible
distinguish details less than 2*2 pixels). Finally, the magnituder3 - difference from a long distance (when details are no longer visible
less than 4*4 pixels). Let's define R as the average between r1, r2 and
r3:
r1 + r2 + r3
R = ──────────────
3
R (like r1, r2, r3) can take values in the range from
0 to 1, with 0 corresponding to a complete match of the compared
images, and 1 - maximum difference.
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════09 ══════════════════
Appendix 2
────────────
Here we will talk about how to determine the values a0-a7
(brightness of colors 0-7 at bright=0) and b0-b7 (brightness of the same
colors with bright=1), when the image is displayed in black and white
form.
Obviously, these values may be different for different
ZX Spectrum models, for different color converters
signal in black and white, as well as for different monitors and
TVs. Therefore, instead of providing a ready-made set
numbers, I'll tell you how you can determine them specifically for
your computer-monitor system, and for this you do not
you will need any special equipment.
What can you say right away?
1. The darkest color is black:
a0 = 0, b0 = 0.
2. The brightest color is white with bright=1:
b7 = 255.
3. The brightness of all other colors takes intermediate
values:
0 < (a1-a7, b1-b6) < 255.
4. As the color number increases, the brightness increases:
a1 > a0 b1 > b0
a2 > a1 b2 > b1
....... .......
a7 > a6 b7 > b6.
5. For each color except black, brightness at bright=1
more than with bright=0:
b1 > a1
b2 > a2
.......
b7 > a7.So, we already know something. Based on this information,
it can be assumed that the brightness distribution will be approximately
like this:
┌─────────────────────────┐
│ bw_s_18.pcx │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
└─────────────────────────┘
Fig. 18
Above we said “brightness”, but what is the physical meaning of this
concepts? Let's define it this way: the brightness of a certain color (more precisely,
brightness of the gradation corresponding to this color in black and white
display mode) is a value proportional to the amount
photons emitted by a single region of a given color per
unit of time. For example, if the brightness of one color is doubled
brighter than another, this means that some area of this
colors emits twice as many photons per unit time as
the same area of a different color.
If you display any color in one half of the screen
(hereinafter - the original color), and in the other half - a mixture of pixels
two colors, darker and lighter than the original (hereinafter referred to as
dark color and light color), then, choosing the ratio of the numberpixels of these two colors, you can achieve equal brightness
both halves of the screen. Equality of brightness means that both
halves of the screen emit the same amount per unit time
number of photons. Then the following formula is valid:
x = k*y + (1-k)*z (1)
where x is the brightness of the original color,
y is the brightness of the dark color,
z - brightness of light color,
k is the proportion of pixels of dark color,
1-k - the proportion of pixels of light color.
For example, if the brightness equalized when filling the second
half of the screen with a 4*4 texture, in which there are 3 pixels of dark color
and 13 pixels of light color, it would be written like this:
x = 3/16*y + 13/16*z.
We use the above to calculate the required brightnesses
a0-a7 and b0-b7. First we express the brightness of each of the colors b1-b6
through the brightness of its neighboring colors:
b1 = k1*b0 + (1-k1)*b2 (2)
b2 = k2*b1 + (1-k2)*b3
......................
b6 = k6*b5 + (1-k6)*b7.
From (2) it follows:
b1 - k1*b0
b2 = ──────────
1 - k1
And further:
b2 - k2*b1
b3 = ──────────
1 - k2
b3 - k3*b2
b4 = ──────────
1 - k3
...............
b6 - k6*b5
b7 = ──────────
1 - k6
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════09 ══════════════════
Appendix 2
────────────
Here we will talk about how to determine the values a0-a7
(brightness of colors 0-7 at bright=0) and b0-b7 (brightness of the same
colors with bright=1), when the image is displayed in black and white
form.
Obviously, these values may be different for different
ZX Spectrum models, for different color converters
signal in black and white, as well as for different monitors and
TVs. Therefore, instead of providing a ready-made set
numbers, I'll tell you how you can determine them specifically for
your computer-monitor system, and for this you do not
you will need any special equipment.
What can you say right away?
1. The darkest color is black:
a0 = 0, b0 = 0.
2. The brightest color is white with bright=1:
b7 = 255.
3. The brightness of all other colors takes intermediate
values:
0 < (a1-a7, b1-b6) < 255.
4. As the color number increases, the brightness increases:
a1 > a0 b1 > b0
a2 > a1 b2 > b1
....... .......
a7 > a6 b7 > b6.
5. For each color except black, brightness at bright=1
more than with bright=0:
b1 > a1
b2 > a2
.......
b7 > a7.So, we already know something. Based on this information,
it can be assumed that the brightness distribution will be approximately
like this:
┌─────────────────────────┐
│ bw_s_18.pcx │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
└─────────────────────────┘
Fig. 18
Above we said “brightness”, but what is the physical meaning of this
concepts? Let's define it this way: the brightness of a certain color (more precisely,
brightness of the gradation corresponding to this color in black and white
display mode) is a value proportional to the amount
photons emitted by a single region of a given color per
unit of time. For example, if the brightness of one color is doubled
brighter than another, this means that some area of this
colors emits twice as many photons per unit time as
the same area of a different color.
If you display any color in one half of the screen
(hereinafter - the original color), and in the other half - a mixture of pixels
two colors, darker and lighter than the original (hereinafter referred to as
dark color and light color), then, choosing the ratio of the numberpixels of these two colors, you can achieve equal brightness
both halves of the screen. Equality of brightness means that both
halves of the screen emit the same amount per unit time
number of photons. Then the following formula is valid:
x = k*y + (1-k)*z (1)
where x is the brightness of the original color,
y is the brightness of the dark color,
z - brightness of light color,
k is the proportion of pixels of dark color,
1-k - the proportion of pixels of light color.
For example, if the brightness equalized when filling the second
half of the screen with a 4*4 texture, in which there are 3 pixels of dark color
and 13 pixels of light color, it would be written like this:
x = 3/16*y + 13/16*z.
We use the above to calculate the required brightnesses
a0-a7 and b0-b7. First we express the brightness of each of the colors b1-b6
through the brightness of its neighboring colors:
b1 = k1*b0 + (1-k1)*b2 (2)
b2 = k2*b1 + (1-k2)*b3
......................
b6 = k6*b5 + (1-k6)*b7.
From (2) it follows:
b1 - k1*b0
b2 = ──────────
1 - k1
And further:
b2 - k2*b1
b3 = ──────────
1 - k2
b3 - k3*b2
b4 = ──────────
1 - k3
...............
b6 - k6*b5
b7 = ──────────
1 - k6
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 10 .C ══════════════════
Thus, knowing b0 and b1, as well as k1-k6 (to be found
empirically), you can sequentially calculate b2-b7.
We know the value of b0 - it is 0. b1 is unknown, but we
we know that b7 = 255. We can express b7 in terms of b1, then
determine b1 and then calculate b2-b6. But this path is connected with
cumbersome transformations. Let's do it simpler. Note that
b2-b7 linearly depend on b1. This is easy to verify from
the above formulas (not forgetting that b0=0). Let's take b1=1 and
Let's calculate b2-b7. Then it is enough to determine how many times
the resulting value of b7 was less than 255, and then multiply
each of the calculated values b1-b7 by this amount, so that
get their true values.
After the brightnesses of colors with bright=1 are known, it remains
determine the brightness of colors with bright=0 (a1-a7). For this
it is enough to find experimentally for each of these colors
an equivalent brightness mixture of two colors with bright=1, after
why use formula (1).
That was the theory, now let's move on to practice. Let's consider
how I determined the brightness levels on my computer and what to
used this one.
First I wrote a procedure that fills the top
part of the screen is one color, and the lower part is a mixture of two pixels
other colors (using 4*4 texture). Here it is:
LD H,1 ;original colorLD D,0 ;dark color
LD E,2 ;light color
LD L,4 ;texture number (0-16)
;Fill the top half of the screen:
LD A,H
ADD A,A
ADD A,A
ADD A,A
ADD A,H
;If the original color is with increased brightness (bright=1), then
;the command ADD A,#40 follows, otherwise it is not needed (you can replace
;to ADD A,0):
ADD A,#40
EXX
LD HL,#5800
LD DE,#5801
LD BC,#180
LD(HL),A
LDIR
EXX
;Fill the bottom half of the screen:
LD A,D
ADD A,A
ADD A,A
ADD A,A
ADD A,E
ADD A,#40
EXX
LD BC,#17F
LD(HL),A
LDIR
EXX
;Texture filling:
LD H,0
ADD HL,HL
ADD HL,HL ;*4
LD DE,TEXTURES
ADD HL,DE
EX DE,HL
;DE points to the first byte of the texture.
LD B,24
LD HL,#4880
M2 PUSH DE
PUSH BC
LD B,4
M1 LD A,(DE)
PUSH BC
PUSH DE
PUSH HL
LD D,H
LD E,L
LD(HL),A
INC DE
LD BC,#1F
LDIR
POP HL
POP DE
POP B.C.
CALL DOWN_HL INC DE
DJNZ M1
POP BC
POP DE
DJNZ M2
RET
DOWN_HL INC H
LD A,H
AND 7
RET NZ
LD A,L
ADD A,#20
LD L,A
RET C
LD A,H
SUB 8
LD H,A
RET
TEXTURES DB %00000000
DB %00000000
DB %00000000
DB %00000000
DB %00000000
DB %01000100
DB %00000000
DB %00000000
DB %00000000
DB %01000100
DB %00000000
DB %00010001
DB %00000000
DB %01010101
DB %00000000
DB %00010001
DB %00000000
DB %01010101
DB %00000000
DB %01010101
DB %00000000
DB %01010101
DB %00100010
DB %01010101
DB %10001000
DB %01010101
DB %00100010
DB %01010101
DB %10101010
DB %01010101
DB %00100010
DB %01010101
DB %10101010
DB %01010101
DB %10101010
DB %01010101
DB %10101010
DB %01110111
DB %10101010
DB %01010101
DB %10101010
DB %01110111
DB %10101010
DB %11011101
DB %10101010
DB %01110111
DB %10101010
DB %11111111
DB %10101010
DB %11111111
DB %10101010
DB %11111111
DB %10101010
DB %11111111
DB %11101110
DB %11111111
DB %10111011
DB %11111111
DB %11101110
DB %11111111
DB %11111111
DB %11111111
DB %11101110
DB %11111111
DB %11111111
DB %11111111
DB %11111111
DB %11111111
════════════════════════════════════════════════
С уважением, Иван Рощин.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 10 .C ══════════════════
Thus, knowing b0 and b1, as well as k1-k6 (to be found
empirically), you can sequentially calculate b2-b7.
We know the value of b0 - it is 0. b1 is unknown, but we
we know that b7 = 255. We can express b7 in terms of b1, then
determine b1 and then calculate b2-b6. But this path is connected with
cumbersome transformations. Let's do it simpler. Note that
b2-b7 linearly depend on b1. This is easy to verify from
the above formulas (not forgetting that b0=0). Let's take b1=1 and
Let's calculate b2-b7. Then it is enough to determine how many times
the resulting value of b7 was less than 255, and then multiply
each of the calculated values b1-b7 by this amount, so that
get their true values.
After the brightnesses of colors with bright=1 are known, it remains
determine the brightness of colors with bright=0 (a1-a7). For this
it is enough to find experimentally for each of these colors
an equivalent brightness mixture of two colors with bright=1, after
why use formula (1).
That was the theory, now let's move on to practice. Let's consider
how I determined the brightness levels on my computer and what to
used this one.
First I wrote a procedure that fills the top
part of the screen is one color, and the lower part is a mixture of two pixels
other colors (using 4*4 texture). Here it is:
LD H,1 ;original colorLD D,0 ;dark color
LD E,2 ;light color
LD L,4 ;texture number (0-16)
;Fill the top half of the screen:
LD A,H
ADD A,A
ADD A,A
ADD A,A
ADD A,H
;If the original color is with increased brightness (bright=1), then
;the command ADD A,#40 follows, otherwise it is not needed (you can replace
;to ADD A,0):
ADD A,#40
EXX
LD HL,#5800
LD DE,#5801
LD BC,#180
LD(HL),A
LDIR
EXX
;Fill the bottom half of the screen:
LD A,D
ADD A,A
ADD A,A
ADD A,A
ADD A,E
ADD A,#40
EXX
LD BC,#17F
LD(HL),A
LDIR
EXX
;Texture filling:
LD H,0
ADD HL,HL
ADD HL,HL ;*4
LD DE,TEXTURES
ADD HL,DE
EX DE,HL
;DE points to the first byte of the texture.
LD B,24
LD HL,#4880
M2 PUSH DE
PUSH BC
LD B,4
M1 LD A,(DE)
PUSH BC
PUSH DE
PUSH HL
LD D,H
LD E,L
LD(HL),A
INC DE
LD BC,#1F
LDIR
POP HL
POP DE
POP B.C.
CALL DOWN_HL INC DE
DJNZ M1
POP BC
POP DE
DJNZ M2
RET
DOWN_HL INC H
LD A,H
AND 7
RET NZ
LD A,L
ADD A,#20
LD L,A
RET C
LD A,H
SUB 8
LD H,A
RET
TEXTURES DB %00000000
DB %00000000
DB %00000000
DB %00000000
DB %00000000
DB %01000100
DB %00000000
DB %00000000
DB %00000000
DB %01000100
DB %00000000
DB %00010001
DB %00000000
DB %01010101
DB %00000000
DB %00010001
DB %00000000
DB %01010101
DB %00000000
DB %01010101
DB %00000000
DB %01010101
DB %00100010
DB %01010101
DB %10001000
DB %01010101
DB %00100010
DB %01010101
DB %10101010
DB %01010101
DB %00100010
DB %01010101
DB %10101010
DB %01010101
DB %10101010
DB %01010101
DB %10101010
DB %01110111
DB %10101010
DB %01010101
DB %10101010
DB %01110111
DB %10101010
DB %11011101
DB %10101010
DB %01110111
DB %10101010
DB %11111111
DB %10101010
DB %11111111
DB %10101010
DB %11111111
DB %10101010
DB %11111111
DB %11101110
DB %11111111
DB %10111011
DB %11111111
DB %11101110
DB %11111111
DB %11111111
DB %11111111
DB %11101110
DB %11111111
DB %11111111
DB %11111111
DB %11111111
DB %11111111
════════════════════════════════════════════════
С уважением, Иван Рощин.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 11.C ══════════════════
To make it more convenient to compare the brightness of the halves of the screen when
on one half the brightness is uniform, and on the other there is texture, I
looked at the image through a translucent polyethylene
film (a regular bag is suitable for this).
Using the above procedure the following were obtained
ratios:
b1 = 9/16 b0 + 7/16 b2
b2 = 10/16 b1 + 6/16 b3
b3 = 8/16 b2 + 8/16 b4
b4 = 13/16 b3 + 3/16 b5
b5 = 9/16 b4 + 7/16 b6
b6 = 10/16 b5 + 6/16 b7
(i.e. k1=9/16, k2=10/16, k3=8/16, k4=13/16, k5=9/16, k6=10/16)
a1 = 3/16 b0 + 13/16 b1
a2 = 9/16 b1 + 7/16 b2
a3 = 9/16 b2 + 7/16 b3
a4 = b3
a5 = 9/16 b4 + 7/16 b5
a6 = 11/16 b5 + 5/16 b6
a7 = 11/16 b6 + 5/16 b7
Then I wrote a program that calculated from this data
required values a0-a7 and b0-b7:
10 DIM k(6): DIM a(7): DIM b(7)
20 LET k(1)=9/16: LET k(2)=10/16: LET k(3)=8/16: LET k(4)=
13/16: LET k(5)=9/16: LET k(6)=10/16
30 REM CALCULATE b(1)-b(7)
40 LET b(1)=1
50 LET b(2)=1/(1-k(1))
60 FOR i=3 TO 7
70 LET b(i)=(b(i-1)-k(i-1)*b(i-2))/(1-k(i-1))
80 NEXT i
90 LET n=255/b(7)
100 FOR i=1 TO 7
110 LET b(i)=b(i)*n
120 NEXT i
130 REM CALCULATE a(1)-a(7)
140 LET a(1)=13/16*b(1)
150 LET a(2)=9/16*b(1)+7/16*b(2)160 LET a(3)=9/16*b(2)+7/16*b(3)
170 LET a(4)=b(3)
180 LET a(5)=9/16*b(4)+7/16*b(5)
190 LET a(6)=11/16*b(5)+5/16*b(6)
200 LET a(7)=11/16*b(6)+5/16*b(7)
210 REM PRINT RESULT
220 PRINT "a0=0",0
230 FOR i=1 TO 7
240 PRINT "a";i;"=";a(i), INT (a(i)*256+0.5)
250 NEXT i
260 PRINT
270 PRINT "b0=0",0
280 FOR i=1 TO 7
290 PRINT "b";i;"=";b(i), INT (b(i)*256+0.5)
300 NEXT i
Results of the program: in the left column - calculated
brightness values, in the right - they are the same, but in 256 shares - in this
format they are stored in image output procedures (see.
Appendix 3).
a0 = 0 0
a1 = 4.344111 1112
a2 = 8.3540597 2139
a3 = 17.233232 4412
a4 = 23.677792 6062
a5 = 56.855343 14555
a6 = 104.72922 26811
a7 = 181.85936 46556
b0 = 0 0
b1 = 5.3465982 1369
b2 = 12.220796 3129
b3 = 23.677792 6062
b4 = 35.134788 8995
b5 = 84.781772 21704
b6 = 148.61361 38045
b7 = 255 65280
And here's what it looks like on the graph:
┌─────────────────────────┐
│ bw_s_19.pcx │
│ │
│ │
│ │
│ │
│ ││ │
│ │
└─────────────────────────┘
Fig. 19
As you can see, this is different from the initial assumptions
(Fig. 18). The values are distributed unevenly: the smaller
brightness, the closer they are to each other. How can this be explained?
Obviously, when converting a color image to black and white
strive to ensure that different colors turn into good
differing shades of gray. And for this brightness
neighboring gradations must differ from each other by more than one and
the same value, and in the same number of times - these are
features of human vision.
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 12 .C ══════════════════
Appendix 3
────────────
Here are the texts of procedures for displaying images
six different ways discussed in the article. Original
the image (256*192, with 256 gradations of brightness) should be
pre-placed in three banks of RAM, the numbers of which (more precisely,
numbers output to port #7FFD for their connection) are indicated in
N_BANK array (by default there are #10, #11 and #13). Information about
pixels are stored "from left to right, top to bottom": in the first bank -
the upper third of the image, in the second - the middle, in the third -
lower
Before using the procedures you must determine the brightness
gradations for your computer-monitor system (see Appendix
2) and place the resulting values in the arrays BRIGHT_0 and
BRIGHT_1. The default values for my system are there,
which will not exactly match yours. Of course you can
Leave them too, but the output quality will be worse.
In procedures using multicolor, all delays
designed for the Pentagon. For different computer model them,
will most likely have to change.
1. Procedure for displaying an image in black and white mode with 15
gradations of brightness (as in Fig. 5). Forms an image in
screen area.
ORG #6000
CALL CLS_1
;Start of the main loop.
LD DE,0
MAIN PUSH DEPUSH DE
;Take the initial data of the familiarity:
LD B,8
LD HL,PIX_SRC
CALL GET_DATA
;We process:
LD HL,PIX_SRC
LD DE,PIX_DST
LD B,64
LD C,9
CALL WORK_DATA
;Determine the attribute address for the current
; familiarity and place the value there:
POP DE
CALL GET_A_ATR
LD(HL),A
;Place information about pixels
;current acquaintance:
LD B,8
LD HL,PIX_DST
MAIN_1 PUSH BC
PUSH DE
LD B,8
MAIN_2 PUSH BC
LD C,(HL)
INC HL
LD B,(HL)
INC HL
CALL SET_TP_1
INC D
POP B.C.
DJNZ MAIN_2
POP DE
INC E
POP B.C.
DJNZ MAIN_1
;Move on to the next familiarity:
POP DE
LD A,D
ADD A,8
LD D,A
JR NZ,MAIN
LD A,E
ADD A,8
LD E,A
CP 192
JR NZ,MAIN
;End of main loop.
RET
PIX_SRC DS 8*8
PIX_DST DS 8*8*2
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 12 .C ══════════════════
Appendix 3
────────────
Here are the texts of procedures for displaying images
six different ways discussed in the article. Original
the image (256*192, with 256 gradations of brightness) should be
pre-placed in three banks of RAM, the numbers of which (more precisely,
numbers output to port #7FFD for their connection) are indicated in
N_BANK array (by default there are #10, #11 and #13). Information about
pixels are stored "from left to right, top to bottom": in the first bank -
the upper third of the image, in the second - the middle, in the third -
lower
Before using the procedures you must determine the brightness
gradations for your computer-monitor system (see Appendix
2) and place the resulting values in the arrays BRIGHT_0 and
BRIGHT_1. The default values for my system are there,
which will not exactly match yours. Of course you can
Leave them too, but the output quality will be worse.
In procedures using multicolor, all delays
designed for the Pentagon. For different computer model them,
will most likely have to change.
1. Procedure for displaying an image in black and white mode with 15
gradations of brightness (as in Fig. 5). Forms an image in
screen area.
ORG #6000
CALL CLS_1
;Start of the main loop.
LD DE,0
MAIN PUSH DEPUSH DE
;Take the initial data of the familiarity:
LD B,8
LD HL,PIX_SRC
CALL GET_DATA
;We process:
LD HL,PIX_SRC
LD DE,PIX_DST
LD B,64
LD C,9
CALL WORK_DATA
;Determine the attribute address for the current
; familiarity and place the value there:
POP DE
CALL GET_A_ATR
LD(HL),A
;Place information about pixels
;current acquaintance:
LD B,8
LD HL,PIX_DST
MAIN_1 PUSH BC
PUSH DE
LD B,8
MAIN_2 PUSH BC
LD C,(HL)
INC HL
LD B,(HL)
INC HL
CALL SET_TP_1
INC D
POP B.C.
DJNZ MAIN_2
POP DE
INC E
POP B.C.
DJNZ MAIN_1
;Move on to the next familiarity:
POP DE
LD A,D
ADD A,8
LD D,A
JR NZ,MAIN
LD A,E
ADD A,8
LD E,A
CP 192
JR NZ,MAIN
;End of main loop.
RET
PIX_SRC DS 8*8
PIX_DST DS 8*8*2
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 13.C ══════════════════
2. Procedure for displaying an image with double attribute
resolution through the use of multicolor (as in
fig. 6). Forms the upper halves of familiarity with its
attributes on the first screen, and the lower halves with their own
attributes - on the second. Then, when the beam draws
the upper halves of the familiarity, the first one is set as active
screen, and when the lower halves are the second. Exit mode
viewing - by pressing any key.
ORG #6000
CALL CLS_1
CALL CLS_2
;Start of the main loop.
LD DE,0
MAIN PUSH DE
PUSH DE
;Take the initial data of the 8*4 area:
LD B,4
LD HL,PIX_SRC
CALL GET_DATA
;We process:
LD HL,PIX_SRC
LD DE,PIX_DST
LD B,32
LD C,9
CALL WORK_DATA
;Depending on whether the top or
;the lower half of the familiarity area is processed-
;Yes, the result should be placed on
;first or second screen. Installed
;we enter the address of the corresponding procedure:
POP DE
LD HL,SET_TP_1
BIT 2,E
JR Z,MAIN_A
LD HL,SET_TP_2
MAIN_A LD (ADR_CALL),HL
;Place the attribute:
CALL GET_A_ATR
BIT 2,E
JR Z,MAIN_BSET 7,H
EX AF,AF'
LD A,#17
CALL SETPORT
EX AF,AF'
MAIN_B LD (HL),A
;Place pixels:
LD B,4
LD HL,PIX_DST
MAIN_1 PUSH BC
PUSH DE
LD B,8
MAIN_2 PUSH BC
LD C,(HL)
INC HL
LD B,(HL)
INC HL
CALL SET_TP_1 ;or SET_TP_2
ADR_CALL EQU $-2
INC D
POP B.C.
DJNZ MAIN_2
POP DE
INC E
POP B.C.
DJNZ MAIN_1
;Move on to the next area:
POP DE
LD A,D
ADD A,8
LD D,A
JR NZ,MAIN
LD A,E
ADD A,4
LD E,A
CP 192
JR NZ,MAIN
;End of main loop.
;Image output: every 4
;lines change the active screen.
CALL ON_IM2
VIEW_LOOP HALT
LD HL,17762
CALL WAIT
LD B,24
VIEW_1 LD A,#10
CALL SETPORT
EXX
LD HL,814
CALL WAIT
EXX
LD A,#18
CALL SETPORT
EXX
LD HL,795
CALL WAIT
EXX
DJNZ VIEW_1
;Keyboard polling:
XOR A
IN A,(254)
CPL
AND #1FJR Z,VIEW_LOOP
;Exit when pressing any key:
CALL OFF_IM2
RET
PIX_SRC DS 8*4
PIX_DST DS 8*4*2
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 14 .C ══════════════════
3. Procedure for displaying an image with maximum attribute
resolution (as in Fig. 7). When forming an image
pixel information is written to the screen area, and
information about attributes (#1800 bytes - your own attribute for
each line of familiarity) is recorded in the ATR array (by
by default located at address #8000). Then to the area
attributes attributes for the first line are placed; bye beam
draws the first line, placed in the attribute area
attributes of the second line, and so on. Since in 224 cycles
(time to draw one line) can’t reroll 32
byte of attributes, the procedure does not display the entire image, but
in a window measuring 11 familiar spaces (88 pixels) horizontally.
You can use the "O" and "P" keys to move the window around the image.
left/right. Exit viewing mode by pressing the space bar.
ATR EQU #8000 ;Attributes (#1800).
LINES EQU #7200 ;Attribute addresses
;on screen (#180).
ORG #6000
LD HL,ATR
LD (ATR_ADR),HL
CALL CLS_1
;Set PAPER 0: INK 7 to
;you could see the construction process
;images:
LD HL,#5800
LD (HL),7
LD DE,#5801
LD BC,#2FF
LDIR
;Start of the main loop.LD DE,0
MAIN PUSH DE
PUSH DE
;Take the initial data of the area 8*1:
LD B,1
LD HL,PIX_SRC
CALL GET_DATA
;We process:
LD HL,PIX_SRC
LD DE,PIX_DST
LD B,8
LD C,9
CALL WORK_DATA
;Place the attribute:
LD HL,0
ATR_ADR EQU $-2
LD(HL),A
INC HL
LD (ATR_ADR),HL
;Place pixels:
POP DE
LD HL,PIX_DST
LD B,8
MAIN_1 PUSH BC
LD C,(HL)
INC HL
LD B,(HL)
INC HL
CALL SET_TP_1
INC D
POP B.C.
DJNZ MAIN_1
;Move on to the next area:
POP DE
LD A,D
ADD A,8
LD D,A
JR NZ,MAIN
INC E
LD A,E
CP 192
JR NZ,MAIN
;End of main loop.
;Image output:
CALL ON_IM2
VIEW_LOOP HALT
LD(SAVE_SP),SP
D.I.
;Clear the attribute area:
LD SP,#5B00
LD HL,0
LD B,48
CLS_ATR PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
DJNZ CLS_ATR
════════════════════════ ════════════════════════Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 13.C ══════════════════
2. Procedure for displaying an image with double attribute
resolution through the use of multicolor (as in
fig. 6). Forms the upper halves of familiarity with its
attributes on the first screen, and the lower halves with their own
attributes - on the second. Then, when the beam draws
the upper halves of the familiarity, the first one is set as active
screen, and when the lower halves are the second. Exit mode
viewing - by pressing any key.
ORG #6000
CALL CLS_1
CALL CLS_2
;Start of the main loop.
LD DE,0
MAIN PUSH DE
PUSH DE
;Take the initial data of the 8*4 area:
LD B,4
LD HL,PIX_SRC
CALL GET_DATA
;We process:
LD HL,PIX_SRC
LD DE,PIX_DST
LD B,32
LD C,9
CALL WORK_DATA
;Depending on whether the top or
;the lower half of the familiarity area is processed-
;Yes, the result should be placed on
;first or second screen. Installed
;we enter the address of the corresponding procedure:
POP DE
LD HL,SET_TP_1
BIT 2,E
JR Z,MAIN_A
LD HL,SET_TP_2
MAIN_A LD (ADR_CALL),HL
;Place the attribute:
CALL GET_A_ATR
BIT 2,E
JR Z,MAIN_BSET 7,H
EX AF,AF'
LD A,#17
CALL SETPORT
EX AF,AF'
MAIN_B LD (HL),A
;Place pixels:
LD B,4
LD HL,PIX_DST
MAIN_1 PUSH BC
PUSH DE
LD B,8
MAIN_2 PUSH BC
LD C,(HL)
INC HL
LD B,(HL)
INC HL
CALL SET_TP_1 ;or SET_TP_2
ADR_CALL EQU $-2
INC D
POP B.C.
DJNZ MAIN_2
POP DE
INC E
POP B.C.
DJNZ MAIN_1
;Move on to the next area:
POP DE
LD A,D
ADD A,8
LD D,A
JR NZ,MAIN
LD A,E
ADD A,4
LD E,A
CP 192
JR NZ,MAIN
;End of main loop.
;Image output: every 4
;lines change the active screen.
CALL ON_IM2
VIEW_LOOP HALT
LD HL,17762
CALL WAIT
LD B,24
VIEW_1 LD A,#10
CALL SETPORT
EXX
LD HL,814
CALL WAIT
EXX
LD A,#18
CALL SETPORT
EXX
LD HL,795
CALL WAIT
EXX
DJNZ VIEW_1
;Keyboard polling:
XOR A
IN A,(254)
CPL
AND #1FJR Z,VIEW_LOOP
;Exit when pressing any key:
CALL OFF_IM2
RET
PIX_SRC DS 8*4
PIX_DST DS 8*4*2
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 14 .C ══════════════════
3. Procedure for displaying an image with maximum attribute
resolution (as in Fig. 7). When forming an image
pixel information is written to the screen area, and
information about attributes (#1800 bytes - your own attribute for
each line of familiarity) is recorded in the ATR array (by
by default located at address #8000). Then to the area
attributes attributes for the first line are placed; bye beam
draws the first line, placed in the attribute area
attributes of the second line, and so on. Since in 224 cycles
(time to draw one line) can’t reroll 32
byte of attributes, the procedure does not display the entire image, but
in a window measuring 11 familiar spaces (88 pixels) horizontally.
You can use the "O" and "P" keys to move the window around the image.
left/right. Exit viewing mode by pressing the space bar.
ATR EQU #8000 ;Attributes (#1800).
LINES EQU #7200 ;Attribute addresses
;on screen (#180).
ORG #6000
LD HL,ATR
LD (ATR_ADR),HL
CALL CLS_1
;Set PAPER 0: INK 7 to
;you could see the construction process
;images:
LD HL,#5800
LD (HL),7
LD DE,#5801
LD BC,#2FF
LDIR
;Start of the main loop.LD DE,0
MAIN PUSH DE
PUSH DE
;Take the initial data of the area 8*1:
LD B,1
LD HL,PIX_SRC
CALL GET_DATA
;We process:
LD HL,PIX_SRC
LD DE,PIX_DST
LD B,8
LD C,9
CALL WORK_DATA
;Place the attribute:
LD HL,0
ATR_ADR EQU $-2
LD(HL),A
INC HL
LD (ATR_ADR),HL
;Place pixels:
POP DE
LD HL,PIX_DST
LD B,8
MAIN_1 PUSH BC
LD C,(HL)
INC HL
LD B,(HL)
INC HL
CALL SET_TP_1
INC D
POP B.C.
DJNZ MAIN_1
;Move on to the next area:
POP DE
LD A,D
ADD A,8
LD D,A
JR NZ,MAIN
INC E
LD A,E
CP 192
JR NZ,MAIN
;End of main loop.
;Image output:
CALL ON_IM2
VIEW_LOOP HALT
LD(SAVE_SP),SP
D.I.
;Clear the attribute area:
LD SP,#5B00
LD HL,0
LD B,48
CLS_ATR PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
DJNZ CLS_ATR
════════════════════════ ════════════════════════Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 15.C ══════════════════
;Create a table of attribute addresses:
MAKE_LT LD HL,#5AE0
LD DE,0
X EQU $-2
ADD HL,DE
LD SP,LINES+#180
LD DE,-#20
LD B,24
MAKE_LT_1 PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
ADD HL,DE
DJNZ MAKE_LT_1
;The larger X, the larger it should be
;delay: X is greater by 1 - delay
;more by 4 bars.
LD HL,(X)
ADD HL,HL
ADD HL,HL
LD DE,9987
ADD HL,DE
CALL WAIT
;We display our attributes in each line:
LD A,192
LD HL,ATR
X_2 EQU $-2
LD SP,LINES
LD B,0
;Execution time of the following
;section - 224 clock cycles, which is equal to time
;draws one line of the image.
ONE_LINE LD C,32 ;7
POP DE ;10
LDI ;16
LDI ;16
LDI ;16
LDI ;16
LDI ;16
LDI ;16
LDI ;16
LDI ;16
LDI ;16
LDI ;16
LDI ;16ADD HL,BC ;11
INC BC ;6 (WAIT)
DEC A ;4
JP NZ,ONE_LINE ;10
LD SP,0
SAVE_SP EQU $-2
EI
;Keyboard polling:
LD A,#DF
IN A,(254)
RRA
JR C,NE_P
;Pressed key "P" - shift visible
;section to the right:
LD A,(X)
CP 21
JR NEW_X
NE_P RRA
JR C,NE_O
;The "O" key is pressed - shift the visible
;section to the left:
LD A,(X)
SUB 1
NEW_X ADC A,0
LD(X),A
LD(X_2),A
JP VIEW_LOOP
;If the spacebar is pressed, exit:
NE_O LD A,#7F
IN A,(254)
RRA
JP C,VIEW_LOOP
CALL OFF_IM2
RET
PIX_SRC DS 8
PIX_DST DS 8*2
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 15.C ══════════════════
;Create a table of attribute addresses:
MAKE_LT LD HL,#5AE0
LD DE,0
X EQU $-2
ADD HL,DE
LD SP,LINES+#180
LD DE,-#20
LD B,24
MAKE_LT_1 PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
ADD HL,DE
DJNZ MAKE_LT_1
;The larger X, the larger it should be
;delay: X is greater by 1 - delay
;more by 4 bars.
LD HL,(X)
ADD HL,HL
ADD HL,HL
LD DE,9987
ADD HL,DE
CALL WAIT
;We display our attributes in each line:
LD A,192
LD HL,ATR
X_2 EQU $-2
LD SP,LINES
LD B,0
;Execution time of the following
;section - 224 clock cycles, which is equal to time
;draws one line of the image.
ONE_LINE LD C,32 ;7
POP DE ;10
LDI ;16
LDI ;16
LDI ;16
LDI ;16
LDI ;16
LDI ;16
LDI ;16
LDI ;16
LDI ;16
LDI ;16
LDI ;16ADD HL,BC ;11
INC BC ;6 (WAIT)
DEC A ;4
JP NZ,ONE_LINE ;10
LD SP,0
SAVE_SP EQU $-2
EI
;Keyboard polling:
LD A,#DF
IN A,(254)
RRA
JR C,NE_P
;The "P" key is pressed - shift the visible
;section to the right:
LD A,(X)
CP 21
JR NEW_X
NE_P RRA
JR C,NE_O
;The "O" key is pressed - shift the visible
;section to the left:
LD A,(X)
SUB 1
NEW_X ADC A,0
LD(X),A
LD(X_2),A
JP VIEW_LOOP
;If the spacebar is pressed, exit:
NE_O LD A,#7F
IN A,(254)
RRA
JP C,VIEW_LOOP
CALL OFF_IM2
RET
PIX_SRC DS 8
PIX_DST DS 8*2
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 16.C ══════════════════
4. Procedure for outputting an image with improved quality for
by quickly alternating two images (as in Fig. 9).
Forms one image on the first screen, another on
the second, and then changes the active screen in each frame. Exit
from viewing mode - by pressing any key.
ORG #6000
CALL CLS_1
CALL CLS_2
;Start of the main loop.
LD DE,0
MAIN PUSH DE
PUSH DE
;Take the initial data of the familiarity:
LD B,8
LD HL,PIX_SRC
CALL GET_DATA
;We process:
LD HL,PIX_SRC
LD DE,PIX_DST
LD B,64
LD C,10
CALL WORK_DATA
;Write down the attribute:
POP DE
CALL GET_A_ATR
LD(HL),A
;Output pixels:
LD B,8
LD HL,PIX_DST
CALL PUT_DATA
;Move on to the next familiarity:
POP DE
LD A,D
ADD A,8
LD D,A
JR NZ,MAIN
LD A,E
ADD A,8
LD E,A
CP 192
JR NZ,MAIN
;End of main loop.
CALL A_FLICKER
;Copy the attributes to the second screen:
LD A,#17
CALL SETPORT
LD HL,#5800LD DE,#D800
LD BC,#300
LDIR
;Switch screens every 1/50 second:
LD A,#10
OUTPUT HALT
XOR 8
CALL SETPORT
LD B,A
XOR A
IN A,(254)
CPL
AND #1F
LD A,B
JR Z,OUTPUT
RET
PIX_SRC DS 8*8
PIX_DST DS 8*8*2
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 16.C ══════════════════
4. Procedure for outputting an image with improved quality for
by quickly alternating two images (as in Fig. 9).
Forms one image on the first screen, another on
the second, and then changes the active screen in each frame. Exit
from viewing mode - by pressing any key.
ORG #6000
CALL CLS_1
CALL CLS_2
;Start of the main loop.
LD DE,0
MAIN PUSH DE
PUSH DE
;Take the initial data of the familiarity:
LD B,8
LD HL,PIX_SRC
CALL GET_DATA
;We process:
LD HL,PIX_SRC
LD DE,PIX_DST
LD B,64
LD C,10
CALL WORK_DATA
;Write down the attribute:
POP DE
CALL GET_A_ATR
LD(HL),A
;Output pixels:
LD B,8
LD HL,PIX_DST
CALL PUT_DATA
;Move on to the next familiarity:
POP DE
LD A,D
ADD A,8
LD D,A
JR NZ,MAIN
LD A,E
ADD A,8
LD E,A
CP 192
JR NZ,MAIN
;End of main loop.
CALL A_FLICKER
;Copy the attributes to the second screen:
LD A,#17
CALL SETPORT
LD HL,#5800LD DE,#D800
LD BC,#300
LDIR
;Switch screens every 1/50 second:
LD A,#10
OUTPUT HALT
XOR 8
CALL SETPORT
LD B,A
XOR A
IN A,(254)
CPL
AND #1F
LD A,B
JR Z,OUTPUT
RET
PIX_SRC DS 8*8
PIX_DST DS 8*8*2
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 17.C ══════════════════
5. Procedure for displaying images with fast interleave and
double attribute resolution (as in Fig. 12). When
records pixel information when forming an image
the first image to the first screen, and the second image to
to the second screen. Attributes (#600 bytes) are written to an array
ATR. Further, in the first frame, when the beam draws the upper
half of the familiarity, the first screen is set to active, and
when the lower halves are the second. In the next frame, on the contrary,
the upper halves of the familiarity are taken from the second screen, and
the lower ones - from the first, and so on. While the beam draws
the upper halves of the familiarity are recorded on the other screen
attributes for the lower halves are familiar, and when the beam
draws the lower halves, they are recorded on another screen
attributes for the upper halves of the next line are familiar, and so on
further. Exit viewing mode by pressing any key.
ATR EQU #8000 ;Attributes (#600)
ORG #6000
LD HL,ATR
LD (ATR_ADR),HL
LD A,#D8
LD(ATR_SCR),A
CALL CLS_1
CALL CLS_2
;Set PAPER 0: INK 7 to
;you could see the construction process
;images:
LD HL,#5800
LD (HL),7
LD DE,#5801LD BC,#2FF
LDIR
;Start of the main loop.
LD DE,0
MAIN PUSH DE
PUSH DE
;Take the initial data of half
;familiar places:
LD B,4
LD HL,PIX_SRC
CALL GET_DATA
;We process:
LD HL,PIX_SRC
LD DE,PIX_DST
LD B,32
LD C,10
CALL WORK_DATA
;Write down the attribute:
LD HL,0
ATR_ADR EQU $-2
LD(HL),A
INC HL
LD (ATR_ADR),HL
;Output pixels:
POP DE
LD B,4
LD HL,PIX_DST
CALL PUT_DATA
;Move on to the next half
;familiar places:
POP DE
LD A,D
ADD A,8
LD D,A
JR NZ,MAIN
LD A,E
ADD A,4
LD E,A
CP 192
JR NZ,MAIN
;End of main loop.
CALL A_FLICKER
;Image output:
CALL ON_IM2
LD XH,#17
VIEW_LOOP HALT
LD HL,16982
CALL WAIT
LD XL,#18
LD HL,ATR
LD DE,0 ;#5800 or #D800
ATR_SCR EQU $-1
LD B,E ;=LD B,0
;Display attributes on one of the screens:
VIEW_4PIX PUSH DE
LD C,#20
LD A,D
XOR #80
LD D,A
VIEW_1 LDI:LDI:LDI:LDILDI:LDI:LDI:LDI
JP PE,VIEW_1
POP DE
EXX
LD HL,193
CALL WAIT
EXX
;Make this screen active:
LD A,XH
CALL SETPORT
;Display attributes on another screen:
LD C,#20
VIEW_2 LDI:LDI:LDI:LDI
LDI:LDI:LDI:LDI
JP PE,VIEW_2
EXX
LD HL,246
CALL WAIT
EXX
;Make this screen active:
LD A,XH
XOR 8
CALL SETPORT
DEC XL
JR NZ,VIEW_4PIX
;In the next frame the screens change
;in places:
LD A,XH
XOR 8
LD XH,A
LD A,(ATR_SCR)
XOR #80
LD(ATR_SCR),A
;Keyboard polling:
XOR A
IN A,(254)
CPL
AND #1F
JR Z,VIEW_LOOP
;Exit by pressing any key:
CALL OFF_IM2
RET
PIX_SRC DS 8*4
PIX_DST DS 8*4*2
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 17.C ══════════════════
5. Procedure for displaying images with fast interleave and
double attribute resolution (as in Fig. 12). When
records pixel information when forming an image
the first image to the first screen, and the second image to
to the second screen. Attributes (#600 bytes) are written to an array
ATR. Further, in the first frame, when the beam draws the upper
half of the familiarity, the first screen is set to active, and
when the lower halves are the second. In the next frame, on the contrary,
the upper halves of the familiarity are taken from the second screen, and
the lower ones - from the first, and so on. While the beam draws
the upper halves of the familiarity are recorded on the other screen
attributes for the lower halves are familiar, and when the beam
draws the lower halves, they are recorded on another screen
attributes for the upper halves of the next line are familiar, and so on
further. Exit viewing mode by pressing any key.
ATR EQU #8000 ;Attributes (#600)
ORG #6000
LD HL,ATR
LD (ATR_ADR),HL
LD A,#D8
LD(ATR_SCR),A
CALL CLS_1
CALL CLS_2
;Set PAPER 0: INK 7 to
;you could see the construction process
;images:
LD HL,#5800
LD (HL),7
LD DE,#5801LD BC,#2FF
LDIR
;Start of the main loop.
LD DE,0
MAIN PUSH DE
PUSH DE
;Take the initial data of half
;familiar places:
LD B,4
LD HL,PIX_SRC
CALL GET_DATA
;We process:
LD HL,PIX_SRC
LD DE,PIX_DST
LD B,32
LD C,10
CALL WORK_DATA
;Write down the attribute:
LD HL,0
ATR_ADR EQU $-2
LD(HL),A
INC HL
LD (ATR_ADR),HL
;Output pixels:
POP DE
LD B,4
LD HL,PIX_DST
CALL PUT_DATA
;Move on to the next half
;familiar places:
POP DE
LD A,D
ADD A,8
LD D,A
JR NZ,MAIN
LD A,E
ADD A,4
LD E,A
CP 192
JR NZ,MAIN
;End of main loop.
CALL A_FLICKER
;Image output:
CALL ON_IM2
LD XH,#17
VIEW_LOOP HALT
LD HL,16982
CALL WAIT
LD XL,#18
LD HL,ATR
LD DE,0 ;#5800 or #D800
ATR_SCR EQU $-1
LD B,E ;=LD B,0
;Display attributes on one of the screens:
VIEW_4PIX PUSH DE
LD C,#20
LD A,D
XOR #80
LD D,A
VIEW_1 LDI:LDI:LDI:LDILDI:LDI:LDI:LDI
JP PE,VIEW_1
POP DE
EXX
LD HL,193
CALL WAIT
EXX
;Make this screen active:
LD A,XH
CALL SETPORT
;Display attributes on another screen:
LD C,#20
VIEW_2 LDI:LDI:LDI:LDI
LDI:LDI:LDI:LDI
JP PE,VIEW_2
EXX
LD HL,246
CALL WAIT
EXX
;Make this screen active:
LD A,XH
XOR 8
CALL SETPORT
DEC XL
JR NZ,VIEW_4PIX
;In the next frame the screens change
;in places:
LD A,XH
XOR 8
LD XH,A
LD A,(ATR_SCR)
XOR #80
LD(ATR_SCR),A
;Keyboard polling:
XOR A
IN A,(254)
CPL
AND #1F
JR Z,VIEW_LOOP
;Exit by pressing any key:
CALL OFF_IM2
RET
PIX_SRC DS 8*4
PIX_DST DS 8*4*2
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 18.C ══════════════════
6. Procedure for outputting images with fast interleave and
maximum attribute resolution (as in Fig. 13). When
records pixel information when forming an image
the first image on the first screen, the second image on
second screen, and attribute values (#1800 bytes) into an array
ATR. Then, as in procedure 3, the attribute area
dynamically updated. Active screen in every frame
is changing. The image is displayed in a window of size 11 characters
(88 pixels) horizontally. Using the "O" and "P" keys, the window can be
move around the image left/right. Exit mode
viewing - by pressing the space bar.
ATR EQU #8000 ;Attributes (#1800).
LINES EQU #7200 ;Attribute addresses
;on screen (#180).
ORG #6000
LD HL,ATR
LD (ATR_ADR),HL
CALL CLS_1
CALL CLS_2
;Set PAPER 0: INK 7 to
;you could see the construction process
;images:
LD HL,#5800
LD (HL),7
LD DE,#5801
LD BC,#2FF
LDIR
;Start of the main loop.
LD DE,0
MAIN PUSH DE
PUSH DE
;We take the initial data of the familiarity line:
LD B,1
LD HL,PIX_SRC
CALL GET_DATA
;We process:LD HL,PIX_SRC
LD DE,PIX_DST
LD B,8
LD C,10
CALL WORK_DATA
;Write down the attribute:
LD HL,0
ATR_ADR EQU $-2
LD(HL),A
INC HL
LD (ATR_ADR),HL
;Output pixels:
POP DE
LD B,1
LD HL,PIX_DST
CALL PUT_DATA
;Move on to the next familiarity line:
POP DE
LD A,D
ADD A,8
LD D,A
JR NZ,MAIN
INC E
LD A,E
CP 192
JR NZ,MAIN
;End of main loop.
CALL A_FLICKER
;Image output:
CALL ON_IM2
VIEW_LOOP HALT
LD(SAVE_SP),SP
D.I.
;Clear the attribute area:
CLS_ATR LD SP,#5B00
LD HL,0
LD B,48
CLS_ATR_1 PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
DJNZ CLS_ATR_1
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 18.C ══════════════════
6. Procedure for outputting images with fast interleave and
maximum attribute resolution (as in Fig. 13). When
records pixel information when forming an image
the first image on the first screen, the second image on
second screen, and attribute values (#1800 bytes) into an array
ATR. Then, as in procedure 3, the attribute area
dynamically updated. Active screen in every frame
is changing. The image is displayed in a window of size 11 characters
(88 pixels) horizontally. Using the "O" and "P" keys, the window can be
move around the image left/right. Exit mode
viewing - by pressing the space bar.
ATR EQU #8000 ;Attributes (#1800).
LINES EQU #7200 ;Attribute addresses
;on screen (#180).
ORG #6000
LD HL,ATR
LD (ATR_ADR),HL
CALL CLS_1
CALL CLS_2
;Set PAPER 0: INK 7 to
;you could see the construction process
;images:
LD HL,#5800
LD (HL),7
LD DE,#5801
LD BC,#2FF
LDIR
;Start of the main loop.
LD DE,0
MAIN PUSH DE
PUSH DE
;We take the initial data of the familiarity line:
LD B,1
LD HL,PIX_SRC
CALL GET_DATA
;We process:LD HL,PIX_SRC
LD DE,PIX_DST
LD B,8
LD C,10
CALL WORK_DATA
;Write down the attribute:
LD HL,0
ATR_ADR EQU $-2
LD(HL),A
INC HL
LD (ATR_ADR),HL
;Output pixels:
POP DE
LD B,1
LD HL,PIX_DST
CALL PUT_DATA
;Move on to the next familiarity line:
POP DE
LD A,D
ADD A,8
LD D,A
JR NZ,MAIN
INC E
LD A,E
CP 192
JR NZ,MAIN
;End of main loop.
CALL A_FLICKER
;Image output:
CALL ON_IM2
VIEW_LOOP HALT
LD(SAVE_SP),SP
D.I.
;Clear the attribute area:
CLS_ATR LD SP,#5B00
LD HL,0
LD B,48
CLS_ATR_1 PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
DJNZ CLS_ATR_1
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 19.C ══════════════════
;Create a table of attribute addresses:
MAKE_LT LD HL,#5AE0
LD DE,0
X EQU $-2
ADD HL,DE
LD SP,LINES+#180
LD DE,-#20
LD B,24
MAKE_LT_1 PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
ADD HL,DE
DJNZ MAKE_LT_1
;The larger X, the larger it should be
;delay: X is greater by 1 - delay
;more by 4 bars.
LD HL,(X)
ADD HL,HL
ADD HL,HL
LD DE,9987
ADD HL,DE
CALL WAIT
;We display our attributes in each line:
LD A,192
LD HL,ATR
X_2 EQU $-2
LD SP,LINES
LD B,0
;Execution time of the following
;section - 224 clock cycles, which is equal to time
;draws one line of the image.
ONE_LINE LD C,32 ;7
POP DE ;10
LDI ;16
LDI ;16
LDI ;16
LDI ;16
LDI ;16
LDI ;16
LDI ;16
LDI ;16
LDI ;16
LDI ;16
LDI ;16ADD HL,BC ;11
INC BC ;6 (WAIT)
DEC A ;4
JP NZ,ONE_LINE ;10
LD SP,0
SAVE_SP EQU $-2
EI
;Delay so that the beam has time to draw
;the last line of the image before
;how to set another active screen:
LD HL,224
CALL WAIT
;Set another screen:
LD A,#10
N_SCR EQU $-1
XOR #0F
LD(N_SCR),A
CALL SETPORT
;Change the starting address in the procedure
;clearing the attribute area and in the procedure
;building a table of attribute addresses:
LD HL,CLS_ATR+2
LD A,(HL)
XOR #80
LD(HL),A
LD HL,MAKE_LT+2
LD A,(HL)
XOR #80
LD(HL),A
;Keyboard polling:
LD A,#DF
IN A,(254)
RRA
JR C,NE_P
;Pressed key "P" - shift visible
;section to the right:
LD A,(X)
CP 21
JR NEW_X
NE_P RRA
JR C,NE_O
;The "O" key is pressed - shift the visible
;section to the left:
LD A,(X)
SUB 1
NEW_X ADC A,0
LD(X),A
LD(X_2),A
JP VIEW_LOOP
;If the spacebar is pressed, exit:
NE_O LD A,#7F
IN A,(254)
RRA
JP C,VIEW_LOOPCALL OFF_IM2
RET
PIX_SRC DS 8
PIX_DST DS 8*2
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 19.C ══════════════════
;Create a table of attribute addresses:
MAKE_LT LD HL,#5AE0
LD DE,0
X EQU $-2
ADD HL,DE
LD SP,LINES+#180
LD DE,-#20
LD B,24
MAKE_LT_1 PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
ADD HL,DE
DJNZ MAKE_LT_1
;The larger X, the larger it should be
;delay: X is greater by 1 - delay
;more by 4 bars.
LD HL,(X)
ADD HL,HL
ADD HL,HL
LD DE,9987
ADD HL,DE
CALL WAIT
;We display our attributes in each line:
LD A,192
LD HL,ATR
X_2 EQU $-2
LD SP,LINES
LD B,0
;Execution time of the following
;section - 224 clock cycles, which is equal to time
;draws one line of the image.
ONE_LINE LD C,32 ;7
POP DE ;10
LDI ;16
LDI ;16
LDI ;16
LDI ;16
LDI ;16
LDI ;16
LDI ;16
LDI ;16
LDI ;16
LDI ;16
LDI ;16ADD HL,BC ;11
INC BC ;6 (WAIT)
DEC A ;4
JP NZ,ONE_LINE ;10
LD SP,0
SAVE_SP EQU $-2
EI
;Delay so that the beam has time to draw
;the last line of the image before
;how to set another active screen:
LD HL,224
CALL WAIT
;Set another screen:
LD A,#10
N_SCR EQU $-1
XOR #0F
LD(N_SCR),A
CALL SETPORT
;Change the starting address in the procedure
;clearing the attribute area and in the procedure
;building a table of attribute addresses:
LD HL,CLS_ATR+2
LD A,(HL)
XOR #80
LD(HL),A
LD HL,MAKE_LT+2
LD A,(HL)
XOR #80
LD(HL),A
;Keyboard polling:
LD A,#DF
IN A,(254)
RRA
JR C,NE_P
;The "P" key is pressed - shift the visible
;section to the right:
LD A,(X)
CP 21
JR NEW_X
NE_P RRA
JR C,NE_O
;The "O" key is pressed - shift the visible
;section to the left:
LD A,(X)
SUB 1
NEW_X ADC A,0
LD(X),A
LD(X_2),A
JP VIEW_LOOP
;If the spacebar is pressed, exit:
NE_O LD A,#7F
IN A,(254)
RRA
JP C,VIEW_LOOPCALL OFF_IM2
RET
PIX_SRC DS 8
PIX_DST DS 8*2
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 20 .C ══════════════════
General part of withdrawal procedures
───────────────────────────
GAMMA_CORR EQU 0 ;Execute
;gamma correction?
;---------------------------------------
;Numbers of memory banks in which
;the original image is stored:
N_BANK DB #10,#11,#13
;257-byte table to indicate
;interrupt routine addresses
;(starts with an address that is a multiple of 256):
INT_TAB EQU #7000
;Interrupt routine address
;(high byte equals low):
INT_ADR EQU #7171
;Brightness gradations in 8.8 format:
BRIGHT_0 DW 0.1112.2139.4412.6062
DW 14555,26811,46556
BRIGHT_1 DW 0.1369.3129.6062.8995
DW 21704,38045,65280
;---------------------------------------
;Screen cleaning procedures:
CLS_1 LD HL,#4000 ;1st screen
LD(HL),L
LD DE,#4001
JR CLS
CLS_2 LD A,#17 ;2nd screen
CALL SETPORT
LD HL,#C000
LD(HL),L
LD DE,#C001
CLS LD BC,#1AFF
LDIR
RET
;---------------------------------------
;Procedure GET_DATA - place in buffer
;information about the rectangular area
;images are 8 pixels wide.
;
;Input: DE - coordinates of the upper left
; area angle,
; B - area height,
; HL - buffer address.GET_DATA PUSH BC
PUSH DE
LD B,8
GET_DATA1 CALL GET_1R
LD(HL),A
INC HL
INC D
DJNZ GET_DATA1
POP DE
INC E
POP B.C.
DJNZ GET_DATA
RET
;---------------------------------------
;Procedure PUT_DATA outputs pixels
;rectangular image area on
;first and second screens (for methods with
;quick change of two screens).
;
;Input: DE - coordinates of the upper left
; area angle,
; B - area height,
; HL - pixel data address.
PUT_DATA PUSH BC
PUSH DE
LD B,8
PUT_D_1 PUSH BC
LD C,(HL)
INC HL
LD B,(HL)
INC HL
;If BC>=256, put it on the first screen
;turn on the pixel and reduce BC by
;256:
INC B ;if B=0, then BC<256
DEC B
JR Z,PUT_D_2
DEC B ;i.e. BC:=BC-256
CALL SET_P_1
;Now BC is the texture number (0-256).
;Place a pixel with this on the second screen
;texture:
PUT_D_2 CALL SET_TP_2
INC D
POP B.C.
DJNZ PUT_D_1
POP DE
INC E
POP B.C.
DJNZ PUT_DATA
RET
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 20 .C ══════════════════
General part of withdrawal procedures
───────────────────────────
GAMMA_CORR EQU 0 ;Execute
;gamma correction?
;---------------------------------------
;Numbers of memory banks in which
;the original image is stored:
N_BANK DB #10,#11,#13
;257-byte table to indicate
;interrupt routine addresses
;(starts with an address that is a multiple of 256):
INT_TAB EQU #7000
;Interrupt routine address
;(high byte equals low):
INT_ADR EQU #7171
;Brightness gradations in 8.8 format:
BRIGHT_0 DW 0.1112.2139.4412.6062
DW 14555,26811,46556
BRIGHT_1 DW 0.1369.3129.6062.8995
DW 21704,38045,65280
;---------------------------------------
;Screen cleaning procedures:
CLS_1 LD HL,#4000 ;1st screen
LD(HL),L
LD DE,#4001
JR CLS
CLS_2 LD A,#17 ;2nd screen
CALL SETPORT
LD HL,#C000
LD(HL),L
LD DE,#C001
CLS LD BC,#1AFF
LDIR
RET
;---------------------------------------
;Procedure GET_DATA - place in buffer
;information about the rectangular area
;images are 8 pixels wide.
;
;Input: DE - coordinates of the upper left
; area angle,
; B - area height,
; HL - buffer address.GET_DATA PUSH BC
PUSH DE
LD B,8
GET_DATA1 CALL GET_1R
LD(HL),A
INC HL
INC D
DJNZ GET_DATA1
POP DE
INC E
POP B.C.
DJNZ GET_DATA
RET
;---------------------------------------
;Procedure PUT_DATA outputs pixels
;rectangular image area on
;first and second screens (for methods with
;quick change of two screens).
;
;Input: DE - coordinates of the upper left
; area angle,
; B - area height,
; HL - pixel data address.
PUT_DATA PUSH BC
PUSH DE
LD B,8
PUT_D_1 PUSH BC
LD C,(HL)
INC HL
LD B,(HL)
INC HL
;If BC>=256, put it on the first screen
;turn on the pixel and reduce BC by
;256:
INC B ;if B=0, then BC<256
DEC B
JR Z,PUT_D_2
DEC B ;i.e. BC:=BC-256
CALL SET_P_1
;Now BC is the texture number (0-256).
;Place a pixel with this on the second screen
;texture:
PUT_D_2 CALL SET_TP_2
INC D
POP B.C.
DJNZ PUT_D_1
POP DE
INC E
POP B.C.
DJNZ PUT_DATA
RET
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 21.C ══════════════════
;---------------------------------------
;The WORK_DATA procedure determines by
;data about a certain area of pixels,
;what attribute to use when
;display, and what pseudogradation
;will be closest in brightness to
;each pixel.
;
;Gamma correction is carried out according to
;table GAMMA_T (if GAMMA_CORR=1).
;
;Input: HL - source data address
; (1 byte per pixel),
; B - number of pixels,
; C - number of pseudogradations
; (257 -> C=9; 513 -> C=10),
; DE - address of generated data
; (2 bytes per pixel).
;
;Output: A - attribute value, with DE
; data has been generated.
;Local variables (8.8):
MIN_PIX DS 2 ;name original brightness
MAX_PIX DS 2 ;max. original brightness
MIN DS 2 ;brightness PAPER
MAX DS 2 ;brightness INK
;The procedure itself went:
WORK_DATA PUSH DE
PUSH HL
LD A,B
LD(N_PIX_1),A
LD A,C
LD(DIV_CNT),A
;Determine the smallest and largest
;brightness values.
LD DE,#FF00 ;start values
WORK_ZN_1 LD A,(HL)
CP D
JR NC,WORK_ZN_2
LD D,A
WORK_ZN_2 CP E
JR C,WORK_ZN_3
LD E,A
WORK_ZN_3 INC HL
DJNZ WORK_ZN_1
;D - the lowest brightness value,;E - greatest (without correction).
;We perform gamma correction and record
;adjusted values in MIN_PIX and
;MAX_PIX:
IF GAMMA_CORR
LD H,GAMMA_T/256
LD L,D
LD B,(HL)
INC H
LD C,(HL)
LD (MIN_PIX),BC
LD L,E
LD C,(HL)
DEC H
LD B,(HL)
LD(MAX_PIX),BC
ELSE
LD B,D
LD C,0
LD (MIN_PIX),BC
LD B,E
LD(MAX_PIX),BC
ENDIF
;Find the smallest interval, in
;which contains the minimum and
;maximum value.
;If MAX_PIX is greater than BRIGHT_0 (7), then
;table BRIGHT_0 is definitely not
;consider:
LD HL,(BRIGHT_0+14)
AND A
SBC HL,BC
JR NC,TWO_TABL
LD HL,BRIGHT_1
CALL DEF_MM
LD A,1
JR OK_MINMAX
;Consider both tables:
TWO_TABL LD HL,BRIGHT_1
CALL DEF_MM
PUSH IX ;save on the stack
EXX
LD HL,BRIGHT_0
CALL DEF_MM
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 21.C ══════════════════
;---------------------------------------
;The WORK_DATA procedure determines by
;data about a certain area of pixels,
;what attribute to use when
;display, and what pseudogradation
;will be closest in brightness to
;each pixel.
;
;Gamma correction is carried out according to
;table GAMMA_T (if GAMMA_CORR=1).
;
;Input: HL - source data address
; (1 byte per pixel),
; B - number of pixels,
; C - number of pseudogradations
; (257 -> C=9; 513 -> C=10),
; DE - address of generated data
; (2 bytes per pixel).
;
;Output: A - attribute value, with DE
; data has been generated.
;Local variables (8.8):
MIN_PIX DS 2 ;name original brightness
MAX_PIX DS 2 ;max. original brightness
MIN DS 2 ;brightness PAPER
MAX DS 2 ;brightness INK
;The procedure itself went:
WORK_DATA PUSH DE
PUSH HL
LD A,B
LD(N_PIX_1),A
LD A,C
LD(DIV_CNT),A
;Determine the smallest and largest
;brightness values.
LD DE,#FF00 ;start values
WORK_ZN_1 LD A,(HL)
CP D
JR NC,WORK_ZN_2
LD D,A
WORK_ZN_2 CP E
JR C,WORK_ZN_3
LD E,A
WORK_ZN_3 INC HL
DJNZ WORK_ZN_1
;D - the lowest brightness value,;E - greatest (without correction).
;We perform gamma correction and record
;adjusted values in MIN_PIX and
;MAX_PIX:
IF GAMMA_CORR
LD H,GAMMA_T/256
LD L,D
LD B,(HL)
INC H
LD C,(HL)
LD (MIN_PIX),BC
LD L,E
LD C,(HL)
DEC H
LD B,(HL)
LD(MAX_PIX),BC
ELSE
LD B,D
LD C,0
LD (MIN_PIX),BC
LD B,E
LD(MAX_PIX),BC
ENDIF
;Find the smallest interval, in
;which contains the minimum and
;maximum value.
;If MAX_PIX is greater than BRIGHT_0 (7), then
;table BRIGHT_0 is definitely not
;consider:
LD HL,(BRIGHT_0+14)
AND A
SBC HL,BC
JR NC,TWO_TABL
LD HL,BRIGHT_1
CALL DEF_MM
LD A,1
JR OK_MINMAX
;Consider both tables:
TWO_TABL LD HL,BRIGHT_1
CALL DEF_MM
PUSH IX ;save on the stack
EXX
LD HL,BRIGHT_0
CALL DEF_MM
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 22.C ══════════════════
;Compare HL and HL':
LD(SAVE_HL),HL
EXX
PUSH DE
PUSH HL
LD DE,0
SAVE_HL EQU $-2
XOR A
SBC HL,DE
POP HL
POP DE
ADC A,0
JR NZ,OK_MM
EXX
POP HL ;IX is not needed on the stack
JR OK_MINMAX
OK_MM POP IX ;restore IX
OK_MINMAX LD (MIN),DE
LD(MAX),BC
;A - BRIGHT value (0 or 1)
;XH,XL - color numbers.
;Form the attribute value:
RRCA ;0th bit
RRCA ;becomes 6th
LD B,A
LD A,XH
ADD A,A
ADD A,A
ADD A,A ;PAPER
ADD A,XL ;+INK
ADD A,B ;+BRIGHT
;Pixel processing:
POP HL ;SOURCE
POP DE ;DESTINY
LD B,0 ;COUNTER
N_PIX_1 EQU $-1
PUSH AF ;saved attributes
PIX_LOOP PUSH HL
IF GAMMA_CORR
LD L,(HL)
LD H,GAMMA_T/256
LD A,(HL)
INC H
LD L,(HL)
LD H,A
ELSE
LD H,(HL)
LD L,0
ENDIF
EX (SP),HL
INC HL
EXX
LD HL,(MAX)
LD DE,(MIN)
AND ASBC HL,DE
;Since MAX>MIN, flag C is cleared.
LD B,H
LD C,L
;BC - MAX-MIN in 256 parts.
POP HL ;current brightness
SBC HL,DE
;HL - current brightness-MIN in 256 parts.
;Divide HL by BC, getting inverted
;quotient (in 256 or 512 parts) in IX:
LD XL,#FF
LD DE,0
LD A,0 ;counter: 9 or 10
DIV_CNT EQU $-1
DIV_LOOP EX AF,AF'
LD A,D
SUB E
LD D,A
SBC HL,BC
JR NC,DIV_1
LD A,D
ADD A,E
LD D,A
ADC HL,BC
DIV_1 LD A,XL
ADC A,A
LD XL,A
LD A,XH
ADC A,A
LD XH,A
SRL B
RR C
RR E
EX AF,AF'
DEC A
JR NZ,DIV_LOOP
;Write down the result, first
;inverting it:
EXX
LD A,XL
CPL
LD(DE),A
INC DE
LD A,XH
CPL
LD(DE),A
INC DE
DJNZ PIX_LOOP
POP AF ;attributes
RET
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 23.C ══════════════════
;---------------------------------------
;Procedure DEF_MM
;
;Input: HL points to the brightness table,
; MIN_PIX - minimum. brightness
; MAX_PIX - max. brightness.
;
;Output: XH - min brightness number,
; XL - max brightness number,
; DE - min. brightness,
; BC - max. brightness,
; HL - the difference between them.
;
;(Checking that the values of D and E should
;be <= maximum value in
;table, must be executed before
;challenge!)
DEF_MM LD IX,#08FF
;XL=8, XH=-1 - initial numbers
;colors with minimum and maximum
;brightness.
PUSH HL
LD DE,15
ADD HL,DE
;Now HL points to the latter,
;highest value in the table.
;Look through the table from end to end
;start until MIN_PIX is greater
;or equal to the next value.
LD DE,(MIN_PIX)
DEF_MM_1 DEC XH
LD B,(HL)
DEC HL
LD C,(HL)
DEC HL
PUSH DE
EX DE,HL
AND A
SBC HL,BC
EX DE,HL
POP DE
JR C,DEF_MM_1
LD H,B
LD L,C ;brightness min.
EX (SP),HL
;Now XH is the min brightness number.
;Looking through the table from beginning to
;end, find the first number greater
;or equal to MAX_PIX. Since the values in;the table increases, it will also
;the smallest such number.
LD BC,(MAX_PIX)
DEF_MM_2 INC XL
LD E,(HL)
INC HL
LD D,(HL)
INC HL
EX DE,HL
AND A
SBC HL,BC
EX DE,HL
JR C,DEF_MM_2
;Calculate the difference:
EX DE,HL
ADD HL,BC ;brightness max.
POP DE ;brightness min.
PUSH HL
AND A
SBC HL,DE
POP B.C.
RET
;---------------------------------------
;SET_TP_1, SET_TP_2 - output procedures
;points with a given texture correspond-
;specifically on the first and second screen.
;
;Input: BC - texture number (0-256),
; D - X, E - Y.
;
;Output: DE, HL - no changes.
SET_TP_1 PUSH HL
LD HL,SET_P_1
JR SET_TP
SET_TP_2 PUSH HL
LD HL,SET_P_2
SET_TP LD (SET_YES+1),HL
DEC B
JR Z,SET_YES ;if 256
LD B,16
CALL TEXTURE
AND A
JR Z,SET_NO
SET_YES CALL SET_P_1 ;or SET_P_2
SET_NO POP HL
RET
;---------------------------------------
;SET_P_1 and SET_P_2 are procedures that set
;point to the first and second respectively
;screen.
SET_P_1 PUSH HL
CALL BYTE
JR SET_P
SET_P_2 PUSH HL
LD A,#17
CALL SETPORTCALL BYTE
SET 7,H
SET_P OR (HL)
LD(HL),A
POP HL
RET
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 24 .C ══════════════════
;---------------------------------------
;The TEXTURE procedure determines by
;given texture and pixel coordinates
;on the screen, this pixel is turned on or
;off.
;
;Input: B - texture side length
; (number of the form 2^n),
; C - texture number,
; D,E - pixel coordinates: x,y.
;
;Output: A - pixel state:
; 0 - disabled, 1 - enabled.
;
;DE does not change.
TEXTURE
;If the texture side length is 1,
;then the state of the pixel is equal to the number
;textures:
SRL B ;Divide the length by 2.
LD A,C ;Texture number.
RET Z ;Exit if length
;was equal to 1.
; We formulate the conditions of a new, more
;simple task, for half the size
;texture being a quarter
;original. The answer to the new problem will be
;the answer to the original problem.
; The side length of the new texture is narrower
;computed and found in B. Remaining
;define the number of the new texture. He
;equal to the integer part of the division of the number
;original texture by 4, perhaps
;increased by 1 (this depends on
;the remainder obtained by dividing and from
;of which quarter it ends up in
;pixel).
;Include in register H the bit with the number
;the quarter in which the pixel appears.
;Numbering of quarters: 0 1
; 2 3.
LD HL,#0101
LD A,B
AND DJR Z,TX_1
INC H ;H:=2
TX_1 LD A,B
AND E
JR Z,TX_2
SLA H
SLA H
;Include bits with numbers in the L register
;those quarters where the texture number is 1
;more.
TX_2 LD A,C
SRL C
SRL C
AND 3
JR Z,TEXTURE ;no such
DEC A
JR Z,PLUS_1 ;0 quarter
SET 3,L
DEC A
JR Z,PLUS_1 ;0.3 quarters
SET 1,L ;0,1,3 quarters
;Now we check: in the quarter where
;turned out to be a pixel, the texture number should
;to be 1 more? If yes, we increase
;him:
PLUS_1 LD A,H
AND L
JR Z,TEXTURE
INC C
JR TEXTURE
;---------------------------------------
BYTE LD A,E
AND A
RRA
SCF
RRA
AND A
RRA
XOR E
AND #F8
XOR E
LD H,A
LD A,D
RLCA
RLCA
RLCA
XOR E
AND #C7
XOR E
RLCA
RLCA
LD L,A
LD A,D
AND 7
LD B,A
INC B
LD A,1
LOOP RRCA
DJNZ LOOP
RET
════════════════════════ ════════════════════════Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 24 .C ══════════════════
;---------------------------------------
;The TEXTURE procedure determines by
;given texture and pixel coordinates
;on the screen, this pixel is turned on or
;off.
;
;Input: B - texture side length
; (number of the form 2^n),
; C - texture number,
; D,E - pixel coordinates: x,y.
;
;Output: A - pixel state:
; 0 - disabled, 1 - enabled.
;
;DE does not change.
TEXTURE
;If the texture side length is 1,
;then the state of the pixel is equal to the number
;textures:
SRL B ;Divide the length by 2.
LD A,C ;Texture number.
RET Z ;Exit if length
;was equal to 1.
; We formulate the conditions of a new, more
;simple task, for half the size
;texture being a quarter
;original. The answer to the new problem will be
;the answer to the original problem.
; The side length of the new texture is narrower
;computed and found in B. Remaining
;define the number of the new texture. He
;equal to the integer part of the division of the number
;original texture by 4, perhaps
;increased by 1 (this depends on
;the remainder obtained by dividing and from
;of which quarter it ends up in
;pixel).
;Include in register H the bit with the number
;the quarter in which the pixel appears.
;Numbering of quarters: 0 1
; 2 3.
LD HL,#0101
LD A,B
AND DJR Z,TX_1
INC H ;H:=2
TX_1 LD A,B
AND E
JR Z,TX_2
SLA H
SLA H
;Include bits with numbers in the L register
;those quarters where the texture number is 1
;more.
TX_2 LD A,C
SRL C
SRL C
AND 3
JR Z,TEXTURE ;no such
DEC A
JR Z,PLUS_1 ;0 quarter
SET 3,L
DEC A
JR Z,PLUS_1 ;0.3 quarters
SET 1,L ;0,1,3 quarters
;Now we check: in the quarter where
;turned out to be a pixel, the texture number should
;to be 1 more? If yes, we increase
;him:
PLUS_1 LD A,H
AND L
JR Z,TEXTURE
INC C
JR TEXTURE
;---------------------------------------
BYTE LD A,E
AND A
RRA
SCF
RRA
AND A
RRA
XOR E
AND #F8
XOR E
LD H,A
LD A,D
RLCA
RLCA
RLCA
XOR E
AND #C7
XOR E
RLCA
RLCA
LD L,A
LD A,D
AND 7
LD B,A
INC B
LD A,1
LOOP RRCA
DJNZ LOOP
RET
════════════════════════ ════════════════════════Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 25 .C ══════════════════
;---------------------------------------
;The GET_A_ATR procedure determines the address in
;attribute areas.
;
;Input: DE - coordinates in pixels.
;Output: HL - address in the attribute area,
; the remaining registers are not changed.
GET_A_ATR PUSH AF
PUSH DE
LD H,0
LD A,E
AND %11111000
LD L,A
ADD HL,HL
ADD HL,HL
LD A,D
RRA
RRA
RRA
AND %00011111
LD E,A
LD D,#58
ADD HL,DE
POP DE
POP AF
RET
;---------------------------------------
;Procedure GET_1R - read value
;one pixel of the original image.
;
;Input: D - X, E - Y.
;Output: A - value,
; BC,DE,HL - no changes.
GET_1R PUSH BC
PUSH DE
PUSH HL
LD A,D ;switch places
LD D,E
LD E,A
LD A,D
RLCA
RLCA
AND 3
LD HL,N_BANK
LD B,0
LD C,A
ADD HL,BC
LD A,(HL) ;installed
CALL SETPORT ;necessary
;memory bank
SET 7,D
SET 6,D
;DE points to the address where it is stored
;pixel value.LD A,(DE)
POP HL
POP DE
POP B.C.
RET
;---------------------------------------
;Procedure A_FLICKER - installation
;flickering pixels in antiphase.
A_FLICKER LD A,#17
CALL SETPORT
LD HL,#4000 ;1 image
LD DE,#C000 ;2 image
LD C,L
FL_MAIN LD B,#80 ;mask
LD A,(DE)
XOR (HL)
;Now the bits in A are set, corresponding
;existing pixels that differ by
;first and second screen, i.e. flickering.
FL_PIX RLA
JR NC,FL_NEXT
EX AF,AF'
LD A,B
;The least significant bit of register C indicates
;which screen to turn on the pixel, and on
;how to turn it off. Team INC C him
;the value is reversed
;when processing each flickering
;pixels.
INC C
BIT 0.C
JR NZ,FL_1
;On the first screen we turn on the pixel, on
;second - turn off:
OR(HL)
LD(HL),A
EX DE,HL
LD A,B
CPL
AND (HL)
JR FL_2
;On the first screen we turn off the pixel, on
;second - turn on:
FL_1 CPL
AND (HL)
LD(HL),A
EX DE,HL
LD A,B
OR(HL)
FL_2 LD (HL),A
EX DE,HL
EX AF,AF'
FL_NEXT SRL B
JR NZ,FL_PIXINC HL
INC DE
LD A,H
CP #58
JR NZ,FL_MAIN
RET
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 25 .C ══════════════════
;---------------------------------------
;The GET_A_ATR procedure determines the address in
;attribute areas.
;
;Input: DE - coordinates in pixels.
;Output: HL - address in the attribute area,
; the remaining registers are not changed.
GET_A_ATR PUSH AF
PUSH DE
LD H,0
LD A,E
AND %11111000
LD L,A
ADD HL,HL
ADD HL,HL
LD A,D
RRA
RRA
RRA
AND %00011111
LD E,A
LD D,#58
ADD HL,DE
POP DE
POP AF
RET
;---------------------------------------
;Procedure GET_1R - read value
;one pixel of the original image.
;
;Input: D - X, E - Y.
;Output: A - value,
; BC,DE,HL - no changes.
GET_1R PUSH BC
PUSH DE
PUSH HL
LD A,D ;switch places
LD D,E
LD E,A
LD A,D
RLCA
RLCA
AND 3
LD HL,N_BANK
LD B,0
LD C,A
ADD HL,BC
LD A,(HL) ;installed
CALL SETPORT ;necessary
;memory bank
SET 7,D
SET 6,D
;DE points to the address where it is stored
;pixel value.LD A,(DE)
POP HL
POP DE
POP B.C.
RET
;---------------------------------------
;Procedure A_FLICKER - installation
;flickering pixels in antiphase.
A_FLICKER LD A,#17
CALL SETPORT
LD HL,#4000 ;1 image
LD DE,#C000 ;2 image
LD C,L
FL_MAIN LD B,#80 ;mask
LD A,(DE)
XOR (HL)
;Now the bits in A are set, corresponding
;existing pixels that differ by
;first and second screen, i.e. flickering.
FL_PIX RLA
JR NC,FL_NEXT
EX AF,AF'
LD A,B
;The least significant bit of register C indicates
;which screen to turn on the pixel, and on
;how to turn it off. Team INC C him
;the value is reversed
;when processing each flickering
;pixels.
INC C
BIT 0.C
JR NZ,FL_1
;On the first screen we turn on the pixel, on
;second - turn off:
OR(HL)
LD(HL),A
EX DE,HL
LD A,B
CPL
AND (HL)
JR FL_2
;On the first screen turn off the pixel, on
;second - turn on:
FL_1 CPL
AND (HL)
LD(HL),A
EX DE,HL
LD A,B
OR(HL)
FL_2 LD (HL),A
EX DE,HL
EX AF,AF'
FL_NEXT SRL B
JR NZ,FL_PIXINC HL
INC DE
LD A,H
CP #58
JR NZ,FL_MAIN
RET
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 26.C ══════════════════
;---------------------------------------
SETPORT PUSH BC
LD BC,#7FFD
OUT(C),A
POP B.C.
RET
IF GAMMA_CORR
;---------------------------------------
;512-byte gamma correction table
;(for gamma=1.5), location address
;divisible by 256. The table first follows
;high bytes of all 256 values, and
;then the younger ones.
DS 256-$256 ;leveling
GAMMA_T
db #00,#00,#00,#00,#00,#00,#00,#01
db #01,#01,#01,#02,#02,#02,#03,#03
db #04,#04,#04,#05,#05,#06,#06,#06
db #07,#07,#08,#08,#09,#09,#0A,#0A
db #0B,#0B,#0C,#0C,#0D,#0E,#0E,#0F
db #0F,#10,#11,#11,#12,#12,#13,#14
db #14,#15,#16,#16,#17,#18,#18,#19
db #1A,#1A,#1B,#1C,#1D,#1D,#1E,#1F
db #20,#20,#21,#22,#23,#23,#24,#25
db #26,#27,#27,#28,#29,#2A,#2B,#2B
db #2C,#2D,#2E,#2F,#30,#31,#31,#32
db #33,#34,#35,#36,#37,#38,#39,#39
db #3A,#3B,#3C,#3D,#3E,#3F,#40,#41
db #42,#43,#44,#45,#46,#47,#48,#49
db #4A,#4B,#4C,#4D,#4E,#4F,#50,#51
db #52,#53,#54,#55,#56,#57,#58,#59
db #5A,#5B,#5C,#5D,#5E,#60,#61,#62
db #63,#64,#65,#66,#67,#68,#69,#6B
db #6C,#6D,#6E,#6F,#70,#71,#73,#74
db #75,#76,#77,#78,#7A,#7B,#7C,#7D
db #7E,#7F,#81,#82,#83,#84,#85,#87
db #88,#89,#8A,#8C,#8D,#8E,#8F,#90
db #92,#93,#94,#95,#97,#98,#99,#9B
db #9C,#9D,#9E,#A0,#A1,#A2,#A4,#A5 db #A6,#A7,#A9,#AA,#AB,#AD,#AE,#AF
db #B1,#B2,#B3,#B5,#B6,#B7,#B9,#BA
db #BB,#BD,#BE,#BF,#C1,#C2,#C4,#C5
db #C6,#C8,#C9,#CA,#CC,#CD,#CF,#D0
db #D1,#D3,#D4,#D6,#D7,#D9,#DA,#DB
db #DD,#DE,#E0,#E1,#E3,#E4,#E5,#E7
db #E8,#EA,#EB,#ED,#EE,#F0,#F1,#F3
db #F4,#F6,#F7,#F9,#FA,#FC,#FD,#FF
db #00,#10,#2D,#53,#80,#B3,#EC,#29
db #6B,#B1,#FB,#49,#9A,#EF,#48,#A3
db #02,#64,#C8,#30,#9A,#07,#76,#E8
db #5D,#D4,#4D,#C9,#47,#C8,#4A,#CF
db #56,#DF,#6A,#F7,#87,#18,#AB,#41
db #D8,#71,#0C,#A8,#47,#E7,#8A,#2E
db #D3,#7B,#24,#CF,#7B,#2A,#DA,#8B
db #3E,#F3,#A9,#61,#1B,#D6,#92,#50
db #10,#D1,#94,#58,#1D,#E4,#AD,#77
db #42,#0F,#DD,#AD,#7E,#50,#24,#F9
db #CF,#A7,#80,#5A,#36,#13,#F1,#D1
db #B2,#94,#78,#5D,#43,#2A,#12,#FC
db #E7,#D3,#C1,#AF,#9F,#90,#83,#76
db #6B,#61,#58,#50,#49,#44,#3F,#3C
db #3A,#39,#39,#3A,#3D,#40,#45,#4B
db #52,#5A,#63,#6D,#78,#84,#92,#A0
db #B0,#C0,#D2,#E5,#F9,#0D,#23,#3A
db #52,#6B,#85,#A0,#BC,#D9,#F7,#16
db #36,#57,#79,#9C,#C0,#E5,#0B,#32
db #5A,#83,#AD,#D8,#04,#31,#5F,#8D
db #BD,#EE,#1F,#52,#85,#BA,#EF,#25
db #5D,#95,#CE,#08,#43,#7F,#BB,#F9
db #38,#77,#B7,#F9,#3B,#7E,#C2,#07
db #4D,#93,#DB,#23,#6C,#B7,#02,#4D
db #9A,#E8,#36,#86,#D6,#27,#79,#CC
db #1F,#74,#C9,#20,#77,#CE,#27,#81
db #DB,#36,#92,#EF,#4D,#AC,#0B,#6B
db #CC,#2E,#91,#F4,#58,#BD,#23,#8A
db #F1,#5A,#C3,#2D,#97,#03,#6F,#DC
db #4A,#B9,#28,#99,#0A,#7B,#EE,#61
db #D6,#4A,#C0,#37,#AE,#26,#9F,#18
db #93,#0E,#89,#06,#83,#02,#80,#00
ENDIF
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 27.C ══════════════════
;---------------------------------------
;Procedure WAIT - delay from 188 to
;65535 cycles. Should be located with
;addresses divisible by 256.
;
;Input: HL - delay time in clock cycles
;(this already includes the execution time
;HL loading commands and procedure call).
DS 256-$256 ;leveling
TAB_ADR DB ADR_0256
DB ADR_1256
DB ADR_2256
DB ADR_3256
DB ADR_4256
DB ADR_5256
DB ADR_6256
DB ADR_7256
DB ADR_8256
DB ADR_9256
DB ADR_10256
DB ADR_11256
DB ADR_12256
DB ADR_13256
DB ADR_14256
DB ADR_15256
DB ADR_16256
DB ADR_17256
DB ADR_18256
DB ADR_19256
DB ADR_20256
DB ADR_21256
DB ADR_22256
DB ADR_23256
DB ADR_24256
DB ADR_25256
DB ADR_26256
DB ADR_27256
DB ADR_28256
DB ADR_29256
DB ADR_30256
DB ADR_31256
ADR_28 NOP
ADR_24 NOP
ADR_20 NOP
ADR_16 NOP
ADR_12 NOP
ADR_8 NOP
ADR_4 NOP
ADR_0 NOP ;4 clock cycles
RET
ADR_29 NOP
ADR_25 NOPADR_21 NOP
ADR_17 NOP
ADR_13 NOP
ADR_9 NOP
ADR_5 NOP
ADR_1 RET NZ ;5 cycles
RET
ADR_30 NOP
ADR_26 NOP
ADR_22 NOP
ADR_18 NOP
ADR_14 NOP
ADR_10 NOP
ADR_6 NOP
ADR_2 INC HL ;6 clock cycles
RET
ADR_31 NOP
ADR_27 NOP
ADR_23 NOP
ADR_19 NOP
ADR_15 NOP
ADR_11 NOP
ADR_7 NOP
ADR_3 LD A,(HL) ;7 clock cycles
RET
WAIT LD DE,-156
ADD HL,DE
LD A,L
AND 31
LD E,A
;To divide HL by 32, it is enough
;shift HL 5 bits to the right, but
;there is a shorter and faster way:
;shift HL 3 places to the left, placing
;move the most significant bits of the result into
;battery, and then produce
;permutation: A -> H -> L.
XOR A
ADD HL,HL
R.L.A.
ADD HL,HL
R.L.A.
ADD HL,HL
R.L.A.
LD L,H
LD H,A
;Loop with execution time 32 cycles:
WAIT_1 DEC HL
LD A,H
OR L
NOP ;for
NOP ;delays
JP NZ,WAIT_1
EX DE,HL
LD H,TAB_ADR/256
LD L,(HL)
JP(HL)
;---------------------------------------
;Switching on and off procedures
;second interrupt mode:
ON_IM2 LD HL,INT_TABLD (HL),INT_ADR/256
LD DE,INT_TAB+1
LD BC,#100
LDIR
LD A,INT_TAB/256
LD I,A
LD HL,#C9FB ;EI:RET
LD (INT_ADR),HL
IM 2
RET
OFF_IM2 IM 1
LD A,#3F
LD I,A
RET
════════════════════════════════════════════════
С уважением, Иван Рощин.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 28.C ══════════════════
Comments
───────────
Not all subroutines from the general part are used in each
withdrawal procedure. To reduce the volume of unused
subroutines can be deleted. Information about which
subroutines are not used in each of the six output procedures,
are given in the table:
┌──────────────────┬──────────── ───────────────────────────────┐
│ Number │ Subroutines, │
│ inference procedures │ not used in this procedure │
├──────────────────┼──────────── ───────────────────────────────┤
│ 1 │ CLS_2, SET_TP_2, SET_P_2, ON_IM2, OFF_IM2,│
│ │ WAIT, PUT_DATA, A_FLICKER │
├──────────────────┼──────────── ───────────────────────────────┤
│ 2 │ PUT_DATA, A_FLICKER │
├──────────────────┼──────────── ───────────────────────────────┤
│ 3 │ GET_A_ATR, CLS_2, SET_TP_2, SET_P_2, │
│ │ PUT_DATA, A_FLICKER │
├──────────────────┼──────────── ───────────────────────────────┤
│ 4 │ ON_IM2, OFF_IM2, WAIT │
├──────────────────┼──────────── ───────────────────────────────┤
│ 5 │ GET_A_ATR │├──────────────────┼──────────── ───────────────────────────────┤
│ 6 │ GET_A_ATR │
└──────────────────┴──────────── ───────────────────────────────┘
Table 1
* * *
How to display an image with magnification? If required
2x magnification (size of displayed image area
then it will be 128*96), then to the beginning of the GET_1R procedure, after
PUSH commands, add this fragment:
LD A,D
SRL A
ADD A, X coordinate of the upper left corner of the area
LD D,A
LD A,E
SRL A
ADD A,Y coordinate of the upper left corner of the area
LD E,A
If you need an increase of 4, 8... times (the size of the output
the area will then be 64*48, 32*24...), then we set
not one, but two, three... SRL A teams.
By the way, to double the above fragment
can be optimized: first add double the coordinate, and
then divide by two with RRA instead of SRL A - this will be
shorter and faster.
* * *
How to generate a gamma correction table (GAMMA_T) for
the desired gamma value? Below is the corresponding program
in BASIC. This program generates a table in the same format as
where it is stored in the common part of the output procedures: values informat 8.8, in the first 256 bytes - integer parts, in the next
256 - fractional. The total length of the table is therefore
512 bytes.
When starting the program you need to specify the desired value
scales. Simultaneously with the calculation of the table, a
graph of pixel brightness depending on its value in
image for a given gamma value. After graduation
calculations, specify the name of the file where the table will be written, or
just press "Enter" to restart the program.
10 CLEAR 32767
15 CLS
20 INPUT "Gamma: ";G
30 FOR i=0 TO 255
40 LET y=(i/255)^G
50 LET Word= INT (y*255*256+0.5)
60 LET HighByte=INT (Word/256)
70 LET LowByte= Word-HighByte*256
80 POKE 32768+i, HighByte
90 POKE 32768+256+i, LowByte
100 PLOT i*175/255,y*175
110 NEXT i
120 INPUT "File: ";a$
130 IF a$="" THEN GO TO 15
140 RANDOMIZE USR 15619: REM :SAVE a$ CODE 32768,512
* * *
When outputting an image to obtain pseudogradations of brightness
257 different 16*16 textures are used. Simple counting
shows that storing them requires 257*16*16/8=8224 bytes
memory, or more than 8 KB. I didn’t want to waste so much memory, and I
used this technique: when you need to find out whether it is turned on or
the pixel with these coordinates located incertain texture, the answer is obtained using a special
algorithm based on texture properties. It appears
It would be interesting to consider this algorithm in more detail.
First, about the properties that must be satisfied
textures used to obtain pseudo-gradations of brightness (in
general case). Let l be the length of the texture side, and l
is a number of the form 2^k (in this case l=16=2^4). Quantity
textures N is equal to l^2+1 (in this case N=16^2+1=257).
Firstly, in each subsequent texture by one included
the pixel is larger than the previous one. If we number the textures from
0 to N-1, then the texture number will be equal to the number included in it
pixels
Secondly, any two adjacent textures differ in only one
pixel. Taking into account the previous property, we obtain that each
the next texture is obtained from the previous one by turning on
one more pixel.
Thirdly, the on and off pixels in each
texture are distributed evenly. What does it mean? Let's take
texture and divide it into four equal parts:
┌─────┬─────┐
│ | │
│ | │
├-----+-----┤
│ | │
│ | │
└─────┴─────┘
Fig. 20
════════════════════════ ════════════════════════Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 28.C ══════════════════
Comments
───────────
Not all routines from the general part are used in every
withdrawal procedure. To reduce the volume of unused
subroutines can be deleted. Information about which
subroutines are not used in each of the six output procedures,
are given in the table:
┌──────────────────┬──────────── ───────────────────────────────┐
│ Number │ Subroutines, │
│ inference procedures │ not used in this procedure │
├──────────────────┼──────────── ───────────────────────────────┤
│ 1 │ CLS_2, SET_TP_2, SET_P_2, ON_IM2, OFF_IM2,│
│ │ WAIT, PUT_DATA, A_FLICKER │
├──────────────────┼──────────── ───────────────────────────────┤
│ 2 │ PUT_DATA, A_FLICKER │
├──────────────────┼──────────── ───────────────────────────────┤
│ 3 │ GET_A_ATR, CLS_2, SET_TP_2, SET_P_2, │
│ │ PUT_DATA, A_FLICKER │
├──────────────────┼──────────── ───────────────────────────────┤
│ 4 │ ON_IM2, OFF_IM2, WAIT │
├──────────────────┼──────────── ───────────────────────────────┤
│ 5 │ GET_A_ATR │├──────────────────┼──────────── ───────────────────────────────┤
│ 6 │ GET_A_ATR │
└──────────────────┴──────────── ───────────────────────────────┘
Table 1
* * *
How to display an image with magnification? If required
2x magnification (size of displayed image area
then it will be 128*96), then to the beginning of the GET_1R procedure, after
PUSH commands, add this fragment:
LD A,D
SRL A
ADD A, X coordinate of the upper left corner of the area
LD D,A
LD A,E
SRL A
ADD A,Y coordinate of the upper left corner of the area
LD E,A
If you need an increase of 4, 8... times (the size of the output
the area will then be 64*48, 32*24...), then we set
not one, but two, three... SRL A teams.
By the way, to double the above fragment
can be optimized: first add double the coordinate, and
then divide by two with RRA instead of SRL A - this will be
shorter and faster.
* * *
How to generate a gamma correction table (GAMMA_T) for
the desired gamma value? Below is the corresponding program
in BASIC. This program generates a table in the same format as
where it is stored in the common part of the output procedures: values informat 8.8, in the first 256 bytes - integer parts, in the next
256 - fractional. The total length of the table is therefore
512 bytes.
When starting the program you need to specify the desired value
scales. Simultaneously with the calculation of the table, a
graph of pixel brightness depending on its value in
image for a given gamma value. After graduation
calculations, specify the name of the file where the table will be written, or
just press "Enter" to restart the program.
10 CLEAR 32767
15 CLS
20 INPUT "Gamma: ";G
30 FOR i=0 TO 255
40 LET y=(i/255)^G
50 LET Word= INT (y*255*256+0.5)
60 LET HighByte=INT (Word/256)
70 LET LowByte= Word-HighByte*256
80 POKE 32768+i, HighByte
90 POKE 32768+256+i, LowByte
100 PLOT i*175/255,y*175
110 NEXT i
120 INPUT "File: ";a$
130 IF a$="" THEN GO TO 15
140 RANDOMIZE USR 15619: REM :SAVE a$ CODE 32768,512
* * *
When outputting an image to obtain pseudogradations of brightness
257 different 16*16 textures are used. Simple counting
shows that storing them requires 257*16*16/8=8224 bytes
memory, or more than 8 KB. I didn’t want to waste so much memory, and I
used this technique: when you need to find out whether it is turned on or
the pixel with these coordinates located incertain texture, the answer is obtained using a special
algorithm based on texture properties. It appears
It would be interesting to consider this algorithm in more detail.
First, about the properties that must be satisfied
textures used to obtain pseudo-gradations of brightness (in
general case). Let l be the length of the texture side, and l
is a number of the form 2^k (in this case l=16=2^4). Quantity
textures N is equal to l^2+1 (in this case N=16^2+1=257).
Firstly, in each subsequent texture by one included
the pixel is larger than the previous one. If we number the textures from
0 to N-1, then the texture number will be equal to the number included in it
pixels
Secondly, any two adjacent textures differ in only one
pixel. Taking into account the previous property, we obtain that each
the next texture is obtained from the previous one by turning on
one more pixel.
Thirdly, the on and off pixels in each
texture are distributed evenly. What does it mean? Let's take
texture and divide it into four equal parts:
┌─────┬─────┐
│ | │
│ | │
├-----+-----┤
│ | │
│ | │
└─────┴─────┘
Fig. 20
════════════════════════ ════════════════════════Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 29.C ══════════════════
Moreover, in any two parts the number of included (as well as
turned off) pixels will differ by no more than one.
If each part is again divided by four, this condition will be
be carried out for the resulting smaller parts, and so on.
Obviously, if the texture number (let's call it n) is a multiple of 4
(and therefore the number of included pixels is a multiple of 4), then
the only option for their location is in the quarters of the texture,
satisfying the above condition - n/4 pixels per
every quarter.
Let the texture number not be a multiple of 4, m is the remainder of the division.
Then, for the uniformity condition to be satisfied, in m quarters
there should be [n/4]+1 pixels, and in the remaining 4-m quarters -
by [n/4] pixels. Let us assume for definiteness that the pixels
are located in three possible cases (when m = 1, 2 or 3)
as follows (taking into account the second property - neighboring textures
differ by only one pixel!):
┌───────┬───────┐ ┌───────┬───────┐ ┌───────┬───────┐
│ | │ │ | │ │ | │
│[n/4]+1| [n/4] │ │[n/4]+1| [n/4] │ │[n/4]+1|[n/4+1]│
│ | │ │ | │ │ | │
├-------+-------┤ ├-------+-------┤ ├-------+-------┤
│ | │ │ | │ │ | ││ [n/4] | [n/4] │ │ [n/4] |[n/4+1]│ │ [n/4] |[n/4+1]│
│ | │ │ | │ │ | │
└───────┴───────┘ └───────┴───────┘ └───────┴───────┘
a) m=1 b) m=2 c) m=3
Fig. 21
The above information is sufficient to
build all N textures, and in order to determine
the pixel with these coordinates located is turned on or off
in a certain texture.
The algorithm is very simple. Let l be the side length known
textures, n - texture number, x and y - pixel coordinates on
screen. You need to get a - pixel state: 0 - off, 1 -
included.
1. If the texture size is 1*1, then the pixel state is
texture number (i.e. if l=1, then a=n).
2. We formulate the conditions of a new, simpler problem differently,
for half the texture being a quarter
original (i.e. we get new l and n). New problem answer
will be the answer to the original problem.
2.1. If n is a multiple of 4, then l:=l/2, n:=n/4, and solve the problem
with these conditions.
2.2. Otherwise, we determine in which quarter of the texture
turns out to be a pixel. If l and x = 0, then the pixel
in the left half, otherwise in the right. If l and y = 0,
then the pixel is in the upper half, otherwise in the lower half.2.3. Using m - the remainder of dividing n by 4 - we determine
which quarters will include [n/4] pixels, and in
which - [n/4]+1 (see Fig. 21).
2.4. If in that quarter of the texture where it turns out
pixel, [n/4] pixels included, then l:=l/2,
n:=[n/4], and solve the problem with these conditions.
2.5. Otherwise l:=l/2, n:=[n/4]+1, and solve the problem with these
conditions.
In the program, this is done by the TEXTURE procedure, the length of which
only 46 bytes. Compare that to the original 8224 bytes!
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 29.C ══════════════════
Moreover, in any two parts the number of included (as well as
turned off) pixels will differ by no more than one.
If each part is again divided by four, this condition will be
be carried out for the resulting smaller parts, and so on.
Obviously, if the texture number (let's call it n) is a multiple of 4
(and therefore the number of included pixels is a multiple of 4), then
the only option for their location is in the quarters of the texture,
satisfying the above condition - n/4 pixels per
every quarter.
Let the texture number not be a multiple of 4, m is the remainder of the division.
Then, for the uniformity condition to be satisfied, in m quarters
there should be [n/4]+1 pixels, and in the remaining 4-m quarters -
by [n/4] pixels. Let us assume for definiteness that the pixels
are located in three possible cases (when m = 1, 2 or 3)
as follows (taking into account the second property - neighboring textures
differ by only one pixel!):
┌───────┬───────┐ ┌───────┬───────┐ ┌───────┬───────┐
│ | │ │ | │ │ | │
│[n/4]+1| [n/4] │ │[n/4]+1| [n/4] │ │[n/4]+1|[n/4+1]│
│ | │ │ | │ │ | │
├-------+-------┤ ├-------+-------┤ ├-------+-------┤
│ | │ │ | │ │ | ││ [n/4] | [n/4] │ │ [n/4] |[n/4+1]│ │ [n/4] |[n/4+1]│
│ | │ │ | │ │ | │
└───────┴───────┘ └───────┴───────┘ └───────┴───────┘
a) m=1 b) m=2 c) m=3
Fig. 21
The above information is sufficient to
build all N textures, and in order to determine
the pixel with these coordinates located is turned on or off
in a certain texture.
The algorithm is very simple. Let l be the side length known
textures, n - texture number, x and y - pixel coordinates on
screen. You need to get a - pixel state: 0 - off, 1 -
included.
1. If the texture size is 1*1, then the pixel state is
texture number (i.e. if l=1, then a=n).
2. We formulate the conditions of a new, simpler problem differently,
for half the texture being a quarter
original (i.e. we get new l and n). New problem answer
will be the answer to the original problem.
2.1. If n is a multiple of 4, then l:=l/2, n:=n/4, and solve the problem
with these conditions.
2.2. Otherwise, we determine in which quarter of the texture
turns out to be a pixel. If l and x = 0, then the pixel
in the left half, otherwise in the right. If l and y = 0,
then the pixel is in the upper half, otherwise in the lower half.2.3. Using m - the remainder of dividing n by 4 - we determine
which quarters will include [n/4] pixels, and in
which - [n/4]+1 (see Fig. 21).
2.4. If in that quarter of the texture where it turns out
pixel, [n/4] pixels included, then l:=l/2,
n:=[n/4], and solve the problem with these conditions.
2.5. Otherwise l:=l/2, n:=[n/4]+1, and solve the problem with these
conditions.
In the program, this is done by the TEXTURE procedure, the length of which
only 46 bytes. Compare that to the original 8224 bytes!
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 30 .C ══════════════════
Appendix 4
────────────
Here are the texts of procedures for working with pcx files,
containing black and white images with 256 gradations of brightness.
I’ll briefly tell you about the format of such files - this is necessary for
understanding the logic of the procedures.
╔════════════════════════╤════════════╗
║ File part │ Length ║
╠════════════════════════╪════════════╣
║ Heading │ 128 ║
╟────────────────────────┼────────────╢
║ Packed information │ variable ║
║ about image pixels │ ║
╟────────────────────────┼────────────╢
║ Palette type │ 1 ║
╟────────────────────────┼────────────╢
║ Palette │ 768 ║
╚════════════════════════╧════════════╝
Table 2
_File header_ contains size information
images, number of colors, etc. In the reading procedure
(READ_PCX) header is ignored because this data
are assumed to be already known. In the write procedure (MAKE_PCX)
The header is always written the same.
_Image pixel information_ is stored in the packaged
form. When packing a sequence of identical bytesis replaced by two bytes: counter and original. So that you can
recognize the byte counter when unpacking, its most significant two bits
are set to 1, and the length of the sequence (from 1 to 63)
stored in the lower six bits. If the length of the sequence
greater than 63, several counter-value pairs are recorded. If
when packing, a separate byte is found, the most significant two bits
which are equal to 1, so that when unpacking it is not confused with
byte-counter, preceded by a byte-counter with the number
repetitions equal to 1.
_Palette type_ - #0A, if the palette byte values are
in the range 0-63, and #0C if in the range 0-255. In procedure
reading the palette type is ignored, and in the writing procedure -
#0C is written.
_Palette_ - it contains information about the components R, G,
B each of the 256 colors of the image. In the reading procedure, the palette
is ignored, and in the recording procedure it is written
sequence of bytes (0,0,0, 1,1,1,..., 255,255,255).
Based on the above, we can determine the maximum
file length: 2 bytes per pixel - that's 256*192*2 = #18000, more
#80 bytes - header, 1 byte - palette type and #300 bytes -
the palette itself, total #18381 bytes. But in TR-DOS the file length is not
may exceed #FF00 bytes. Therefore, in the MAKE_PCX procedure, if
the file length is greater than #FF00, two
file: the first with length #FF00, and the second with the remainder.So, the texts of the procedures.
1. READ_PCX - reading an image from a pcx file into three banks
memory.
Input: file name and extension specified in FILENAME, bank numbers
memory - in N_BANK.
Output: A=1 - OK, A=0 - file not found.
;Data structures:
BUFER EQU #7000 ;256-byte buffer (address is a multiple of 256!)
;The procedure itself went:
ORG #6000
READ_PCX LD HL,BUFER
LD (GET_B_0),HL
LD HL,N_BANK
LD (PUT_B_1),HL
LD HL,#C000
LD (PUT_B_2),HL
;Looking for file:
LD HL,FILENAME
LD C,#13
CALL #3D13
LD C,#0A
CALL #3D13
LD A,C
INC A
RET Z ;no such file
DEC A
LD C,8; read
CALL #3D13 ;descriptor
;Determine the track/sector address of the start
;file:
LD HL,(#5CEB)
LD (GET_B_1),HL
;Skipping the first 128 bytes of the file -
;this is the title.
LD B,128
M1 CALL GET_B
DJNZ M1
LD HL,#C000 ;number of pix.
;Main loop:
M3 CALL GET_B ;read byte
;If this is a byte counter, then two of it
;most significant bits are set to 1, and
;lower 6 bits - quantity
;repetitions.
LD B,1
LD C,A
CPL
AND %11000000
LD A,C
JR NZ,M2════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 30 .C ══════════════════
Appendix 4
────────────
Here are the texts of procedures for working with pcx files,
containing black and white images with 256 gradations of brightness.
I’ll briefly tell you about the format of such files - this is necessary for
understanding the logic of the procedures.
╔════════════════════════╤════════════╗
║ File part │ Length ║
╠════════════════════════╪════════════╣
║ Heading │ 128 ║
╟────────────────────────┼────────────╢
║ Packed information │ variable ║
║ about image pixels │ ║
╟────────────────────────┼────────────╢
║ Palette type │ 1 ║
╟────────────────────────┼────────────╢
║ Palette │ 768 ║
╚════════════════════════╧════════════╝
Table 2
_File header_ contains size information
images, number of colors, etc. In the reading procedure
(READ_PCX) header is ignored because this data
are assumed to be already known. In the write procedure (MAKE_PCX)
The header is always written the same.
_Image pixel information_ is stored in the packaged
form. When packing a sequence of identical bytesis replaced by two bytes: counter and original. So that you can
recognize the byte counter when unpacking, its most significant two bits
are set to 1, and the length of the sequence (from 1 to 63)
stored in the lower six bits. If the length of the sequence
greater than 63, several counter-value pairs are recorded. If
when packing, a separate byte is found, the most significant two bits
which are equal to 1, so that when unpacking it is not confused with
byte-counter, preceded by a byte-counter with the number
repetitions equal to 1.
_Palette type_ - #0A, if the palette byte values are
in the range 0-63, and #0C if in the range 0-255. In procedure
reading the palette type is ignored, and in the writing procedure -
#0C is written.
_Palette_ - it contains information about the components R, G,
B each of the 256 colors of the image. In the reading procedure, the palette
is ignored, and in the recording procedure it is written
sequence of bytes (0,0,0, 1,1,1,..., 255,255,255).
Based on the above, we can determine the maximum
file length: 2 bytes per pixel - that's 256*192*2 = #18000, more
#80 bytes - header, 1 byte - palette type and #300 bytes -
the palette itself, total #18381 bytes. But in TR-DOS the file length is not
may exceed #FF00 bytes. Therefore, in the MAKE_PCX procedure, if
the file length is greater than #FF00, two
file: the first with length #FF00, and the second with the remainder.So, the texts of the procedures.
1. READ_PCX - reading an image from a pcx file into three banks
memory.
Input: file name and extension specified in FILENAME, bank numbers
memory - in N_BANK.
Output: A=1 - OK, A=0 - file not found.
;Data structures:
BUFER EQU #7000 ;256-byte buffer (address is a multiple of 256!)
;The procedure itself went:
ORG #6000
READ_PCX LD HL,BUFER
LD (GET_B_0),HL
LD HL,N_BANK
LD (PUT_B_1),HL
LD HL,#C000
LD (PUT_B_2),HL
;Looking for file:
LD HL,FILENAME
LD C,#13
CALL #3D13
LD C,#0A
CALL #3D13
LD A,C
INC A
RET Z ;no such file
DEC A
LD C,8; read
CALL #3D13 ;descriptor
;Determine the track/sector address of the start
;file:
LD HL,(#5CEB)
LD (GET_B_1),HL
;Skipping the first 128 bytes of the file -
;this is the title.
LD B,128
M1 CALL GET_B
DJNZ M1
LD HL,#C000 ;number of pix.
;Main loop:
M3 CALL GET_B ;read byte
;If this is a byte counter, then two of it
;most significant bits are set to 1, and
;lower 6 bits - quantity
;repetitions.
LD B,1
LD C,A
CPL
AND %11000000
LD A,C
JR NZ,M2════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 31.C ══════════════════
;Repeat the next byte specified
;number of times:
LD A,%00111111
AND C
LD B,A
CALL GET_B
M2 CALL PUT_B
DEC HL
DJNZ M2
LD A,H ;all #C000 pixels?
OR L
JR NZ,M3
INC A ;A:=1
RET
;File name:
FILENAME DB "picture pcx"
;Numbers of memory banks used:
N_BANK DB #10,#11,#13
;---------------------------------------
;Procedure GET_B - reading a byte from
;file.
GET_B PUSH HL
PUSH BC
LD HL,0 ;address in buffer
GET_B_0 EQU $-2
LD A,L
AND A
JR NZ,GET_B_3
;You need to read the sector of the file:
PUSH HL
LD B,1
LD DE,0
GET_B_1 EQU $-2
PUSH DE
LD HL,BUFER
LD C,5
CALL #3D13
;Increase the track/sector address:
POP DE
INC E
BIT 4,E
JR Z,GET_B_2
LD E,0
INC D
GET_B_2 LD (GET_B_1),DE
POP HL
;Read a byte from the buffer:
GET_B_3 LD A,(HL)
INC L
LD (GET_B_0),HL
POP B.C.
POP HL
RET
;---------------------------------------;Procedure PUT_B - placing information
;about the next pixel in memory.
PUT_B PUSH AF
PUSH HL
PUSH AF
PUSH BC
LD HL,0
PUT_B_1 EQU $-2
LD A,(HL) ;N bank
LD BC,#7FFD
OUT(C),A
POP B.C.
POP AF
LD HL,0 ;address
PUT_B_2 EQU $-2
LD(HL),A
INC HL ;increased addresses
LD A,H
OR L
JR NZ,PUT_B_3
LD HL,(PUT_B_1) ;go to
INC HL ;following
LD (PUT_B_1),HL ;jar
LD HL,#C000
PUT_B_3 LD (PUT_B_2),HL
POP HL
POP AF
RET
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 31.C ══════════════════
;Repeat the next byte specified
;number of times:
LD A,%00111111
AND C
LD B,A
CALL GET_B
M2 CALL PUT_B
DEC HL
DJNZ M2
LD A,H ;all #C000 pixels?
OR L
JR NZ,M3
INC A ;A:=1
RET
;File name:
FILENAME DB "picture pcx"
;Numbers of memory banks used:
N_BANK DB #10,#11,#13
;---------------------------------------
;Procedure GET_B - reading a byte from
;file.
GET_B PUSH HL
PUSH BC
LD HL,0 ;address in buffer
GET_B_0 EQU $-2
LD A,L
AND A
JR NZ,GET_B_3
;You need to read the file sector:
PUSH HL
LD B,1
LD DE,0
GET_B_1 EQU $-2
PUSH DE
LD HL,BUFER
LD C,5
CALL #3D13
;Increase the track/sector address:
POP DE
INC E
BIT 4,E
JR Z,GET_B_2
LD E,0
INC D
GET_B_2 LD (GET_B_1),DE
POP HL
;Read a byte from the buffer:
GET_B_3 LD A,(HL)
INC L
LD (GET_B_0),HL
POP B.C.
POP HL
RET
;---------------------------------------;Procedure PUT_B - placing information
;about the next pixel in memory.
PUT_B PUSH AF
PUSH HL
PUSH AF
PUSH BC
LD HL,0
PUT_B_1 EQU $-2
LD A,(HL) ;N bank
LD BC,#7FFD
OUT(C),A
POP B.C.
POP AF
LD HL,0 ;address
PUT_B_2 EQU $-2
LD(HL),A
INC HL ;increased addresses
LD A,H
OR L
JR NZ,PUT_B_3
LD HL,(PUT_B_1) ;go to
INC HL ;following
LD (PUT_B_1),HL ;jar
LD HL,#C000
PUT_B_3 LD (PUT_B_2),HL
POP HL
POP AF
RET
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 32.C ══════════════════
2. MAKE_PCX - recording images from three memory banks to
pcx file.
Input: file name and extension specified in NAME_EXT, bank numbers
memory - in N_BANK.
Output: A=1 - OK, A=0 - writing the file failed (there is no space on
disk or disk directory).
;Data structures:
FILE_BUFER EQU #7000 ;write buffer
CAT_BUFER EQU #7100 ;disk directory
;The procedure itself went:
ORG #6000
MAKE_PCX CALL INIT_GET
;First we convert "idle" so that
;find out the size of the resulting file. Into this
;time colors are displayed on the border
;stripes - to show that the computer
;not stuck.
LD D,0 ;DHL - length
LD HL,#381
LD BC,1
MAKE_PCX_1 CALL GET_BYTE
JR C,MAKE_PCX_2
AND 7 ;Change
OUT (254),A ;BORDER
ADD HL,BC
LD A,D
ADC A,0
LD D,A
JR MAKE_PCX_1
MAKE_PCX_2 LD (LEN_FILE),HL
LD A,D
LD(LEN_FILE+2),A
;The length in bytes has been determined, now
;get the length in sectors:
LD BC,255
ADD HL,BC
LD A,D
ADC A,0
LD L,H
LD H,A ;HL - length.
PUSH HL ;Remember her...
;Reading the catalogue:
LD HL,CAT_BUFER
LD DE,0LD B,9
LD C,5
CALL #3D13
;Checking free space in the area
;files:
LD HL,(CAT_BUFER+#8E5)
POP DE ;file length
XOR A ;A:=0, flag C is cleared
SBC HL,DE
RET C ;No disk space!
;Correction of free space:
LD (CAT_BUFER+#8E5),HL
;Now we determine how much it will be
;files: one or two?
LD A,1 ;number of files
EX DE,HL
LD DE,255
SBC HL,DE ;flag C is reset!
JR C,MAKE_PCX_7 ;1 file
INC A ;2 files
MAKE_PCX_7 LD (FILES),A
;Checking free space in the area
;directory:
LD HL,CAT_BUFER+#8E4
LD B,(HL)
ADD A,B
LD (HL),A ;correction
CP 128+1
LD A,0
RET NC ;No space!
;Determine the number of the first sector of the file:
LD HL,(CAT_BUFER+#8E1)
LD (DISK_ADR),HL
;Place information about the file in the directory
;(or about two files, if the length is greater
;#FF00):
LD H,0
LD L,B
ADD HL,HL
ADD HL,HL
ADD HL,HL
ADD HL,HL ;*16
LD DE,CAT_BUFER
ADD HL,DE
EX DE,HL
LD HL,NAME_EXT ;Title
LD BC,11
LDIR
LD A,(FILES)
CP 2JR NZ,MAKE_PCX_A
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 32.C ══════════════════
2. MAKE_PCX - recording images from three memory banks to
pcx file.
Input: file name and extension specified in NAME_EXT, bank numbers
memory - in N_BANK.
Output: A=1 - OK, A=0 - writing the file failed (there is no space on
disk or disk directory).
;Data structures:
FILE_BUFER EQU #7000 ;write buffer
CAT_BUFER EQU #7100 ;disk directory
;The procedure itself went:
ORG #6000
MAKE_PCX CALL INIT_GET
;First we convert "idle" so that
;find out the size of the resulting file. Into this
;time colors are displayed on the border
;stripes - to show that the computer
;not stuck.
LD D,0 ;DHL - length
LD HL,#381
LD BC,1
MAKE_PCX_1 CALL GET_BYTE
JR C,MAKE_PCX_2
AND 7 ;Change
OUT (254),A ;BORDER
ADD HL,BC
LD A,D
ADC A,0
LD D,A
JR MAKE_PCX_1
MAKE_PCX_2 LD (LEN_FILE),HL
LD A,D
LD(LEN_FILE+2),A
;The length in bytes has been determined, now
;get the length in sectors:
LD BC,255
ADD HL,BC
LD A,D
ADC A,0
LD L,H
LD H,A ;HL - length.
PUSH HL ;Remember her...
;Reading the catalogue:
LD HL,CAT_BUFER
LD DE,0LD B,9
LD C,5
CALL #3D13
;Checking free space in the area
;files:
LD HL,(CAT_BUFER+#8E5)
POP DE ;file length
XOR A ;A:=0, flag C is cleared
SBC HL,DE
RET C ;No disk space!
;Correction of free space:
LD (CAT_BUFER+#8E5),HL
;Now we determine how much it will be
;files: one or two?
LD A,1 ;number of files
EX DE,HL
LD DE,255
SBC HL,DE ;flag C is reset!
JR C,MAKE_PCX_7 ;1 file
INC A ;2 files
MAKE_PCX_7 LD (FILES),A
;Checking free space in the area
;directory:
LD HL,CAT_BUFER+#8E4
LD B,(HL)
ADD A,B
LD (HL),A ;correction
CP 128+1
LD A,0
RET NC ;No space!
;Determine the number of the first sector of the file:
LD HL,(CAT_BUFER+#8E1)
LD (DISK_ADR),HL
;Place information about the file in the directory
;(or about two files, if the length is greater
;#FF00):
LD H,0
LD L,B
ADD HL,HL
ADD HL,HL
ADD HL,HL
ADD HL,HL ;*16
LD DE,CAT_BUFER
ADD HL,DE
EX DE,HL
LD HL,NAME_EXT ;Title
LD BC,11
LDIR
LD A,(FILES)
CP 2JR NZ,MAKE_PCX_A
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 33.C ══════════════════
;Two files are written.
;First file:
EX DE,HL
LD (HL),0 ;Length:
INC HL
LD (HL),#FF ;#FF00 bytes,
INC HL
LD (HL),#FF ;#FF sectors.
INC HL
LD A,#FF ;Address to
CALL NEW_TS ;disk.
;Second file:
EX DE,HL
LD HL,NAME_EXT ;Title.
LD BC,8
LDIR
LD A,"0" ;First character
LD (DE),A ;extensions - "0"
INC DE
INC HL
LDI
LDI
EX DE,HL ;Length:
LD A,(LEN_FILE) ;junior
LD (HL),A ;byte...
INC HL
PUSH HL
LD HL,(LEN_FILE+1)
DEC H
INC HL ;-#FF00
LD A,L ;and senior
POP HL ;byte.
LD(HL),A
PUSH HL
DEC HL
LD L,(HL)
LD H,A
INC H
DEC HL
LD A,H ;Length in sectors.
POP HL
INC HL
LD(HL),A
INC HL
CALL NEW_TS
JR MAKE_PCX_9
;One file is written:
MAKE_PCX_A EX DE,HL
LD DE,(LEN_FILE)
LD (HL),E ;LengthINC HL ;in bytes.
LD(HL),D
INC HL
INC D ;Length
DEC DE ;in sectors.
LD(HL),D
INC HL
LD A,D
CALL NEW_TS
;Write the directory:
MAKE_PCX_9 LD HL,CAT_BUFER
LD DE,0
LD B,9
LD C,6
CALL #3D13
;Write the contents of the file:
CALL OPEN_FILE
;Header (128 bytes):
LD HL,HEADER
LD B,128
MAKE_PCX_6 LD A,(HL)
CALL PUT_BYTE
INC HL
DJNZ MAKE_PCX_6
;Pixel data:
CALL INIT_GET
MAKE_PCX_3 CALL GET_BYTE
JR C,MAKE_PCX_4
CALL PUT_BYTE
JR MAKE_PCX_3
;Palette type:
MAKE_PCX_4 LD A,#0C
CALL PUT_BYTE
;Palette (#300 bytes):
XOR A
MAKE_PCX_5 CALL PUT_BYTE
CALL PUT_BYTE
CALL PUT_BYTE
INC A
JR NZ,MAKE_PCX_5
;Close the file:
CALL CLOSE_FILE
LD A,1
RET
NAME_EXT DB "picture pcx" ;name
LEN_FILE DS 3 ;pcx file length
FILES DS 1 ;how many files there will be
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
2 October 2002
Hello, All!
═══════════════════ 34 .C ══════════════════
;---------------------------------------
;The NEW_TS procedure places at the specified
;in the HL address information about the first
;free track/sector and updates
;this information is in the system sector.
;
;Input: A - file length in sectors.
;Output: HL:=HL+2.
NEW_TS LD DE,(CAT_BUFER+#8E1)
LD(HL),E
INC HL ;Address at
LD (HL),D ;disk.
INC HL
;Increase the numbers of the first unoccupied
;track/sector:
LD B,A ;Length in sectors
AND 15
ADD A,E
BIT 4.A
RES 4,A
LD E,A
JR Z,NEW_TS_1
INC D
NEW_TS_1 LD A,B
RRA
RRA
RRA
RRA
AND 15
ADD A,D
LD D,A
LD (CAT_BUFER+#8E1),DE
RET
;---------------------------------------
;The GET_BYTE procedure returns to
;the battery has another byte packed
;pixel data. If flag C
;installed - it means the data has run out.
;
;BC,DE,HL do not change.
STATUS DS 1 ;N state of proc.
BYTE_B DS 1 ;stored byte
;Initialization:
INIT_GET LD A,1
LD (STATUS),A
CALL INIT_PIX
RET
;The procedure itself went:
GET_BYTE PUSH BC
PUSH DE
PUSH HLLD A,(STATUS)
CP 2
JR Z,GET_BYTE_2
;1st state:
LD C,1 ;counter of identical
CALL GET_PIX
JR C,GET_EXIT
LD(BYTE_B),A
GET_B_3 LD A,C ;already have 63
CP 63 ;same bytes?
JR Z,GET_B_1
CALL GET_PIX ;take another byte
JR C,GET_B_4
LD HL,BYTE_B ;compare...
CP(HL)
JR NZ,GET_B_4
INC C
JR GET_B_3
GET_B_4 CALL PREV_PIX ;shift back
GET_B_2 LD A,(BYTE_B);byte looks like
AND %11000000 ;as
CP %11000000 ;byte counter?
JR Z,GET_B_1
LD A,C ;one
DEC A ;repeat?
JR NZ,GET_B_1
LD A,(BYTE_B)
AND A ;reset flag C
JR GET_EXIT
GET_B_1 LD A,2
LD (STATUS),A
LD A,C
OR %11000000 ;reset. flag C
JR GET_EXIT
;2nd state:
GET_BYTE_2 LD A,1
LD (STATUS),A
LD A,(BYTE_B)
AND A ;reset flag C
GET_EXIT POP HL
POP DE
POP B.C.
RET
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
3 October 2002
Hello, All!
═══════════════════ 35 .C ══════════════════
;---------------------------------------
;Procedure GET_PIX sequentially
;returns pixel data. If
;flag C is set, which means data
;ran out.
;
;BC,DE,HL do not change.
GET_ADR DS 2 ;memory bank address
GET_BANK DS 1 ;bank number in the table
;Initialization:
INIT_PIX LD HL,#C000-1
LD (GET_ADR),HL
XOR A
LD (GET_BANK),A
RET
;The procedure itself went:
GET_PIX PUSH HL
LD HL,(GET_ADR)
INC HL
LD (GET_ADR),HL
LD A,H
OR L
JR NZ,GET_PIX_1
LD HL,#C000
LD (GET_ADR),HL
LD A,(GET_BANK)
INC A
LD (GET_BANK),A
CP 3
JR NZ,GET_PIX_1
POP HL
SCF
RET
GET_PIX_1 PUSH BC
PUSH DE
LD BC,#7FFD
LD HL,N_BANK
LD A,(GET_BANK)
LD D,0
LD E,A
ADD HL,DE
LD A,(HL)
OUT(C),A
LD HL,(GET_ADR)
LD A,(HL)
LD D,#10
OUT(C),D
POP DE
POP B.C.
POP HL
AND A ;reset flag C
RET
;Shift back one pixel:
PREV_PIX PUSH HLLD HL,(GET_ADR)
DEC HL
LD A,H
CP #BF
JR NZ,PREV_PIX_1
LD A,L
CP#FF
JR NZ,PREV_PIX_1
LD HL,#FFFF
LD A,(GET_BANK)
DEC A
LD (GET_BANK),A
PREV_PIX_1 LD (GET_ADR),HL
POP HL
RET
;Data banks with information about pixels:
N_BANK DB #10,#11,#13
;---------------------------------------
;Procedures for byte recording
;file contents with buffering.
IN_BUF DS 1 ;number of bytes in
;buffer
DISK_ADR DS 2 ;sector address
;OPEN_FILE - open a file.
OPEN_FILE XOR A
LD(IN_BUF),A
RET
;PUT_BYTE - write a byte to a file.
;Input: A - value to be written.
;Output: registers not modified.
PUT_BYTE PUSH AF
PUSH BC
PUSH DE
PUSH HL
LD B,A
LD A,(IN_BUF)
LD HL,FILE_BUFER
LD D,0
LD E,A
ADD HL,DE
LD(HL),B
INC A
LD(IN_BUF),A
;If the buffer is full, write it:
CALL Z,CLOSE_FILE
POP HL
POP DE
POP B.C.
POP AF
RET
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
3 October 2002
Hello, All!
═══════════════════ 36.C ══════════════════
;CLOSE_FILE - close the file.
;The contents of the buffer are written to disk.
CLOSE_FILE LD DE,(DISK_ADR)
PUSH DE
LD B,1
LD C,6
LD HL,FILE_BUFER
CALL #3D13
POP DE
INC E
LD A,E
AND 15
LD E,A
JR NZ,CLOSE_EXIT
INC D
CLOSE_EXIT LD (DISK_ADR),DE
RET
;---------------------------------------
;128-byte header:
HEADER
db #0A,#05,#01,#08,#00,#00,#00,#00
db #FF,#00,#BF,#00,#00,#01,#C0,#00
db #60,#61,#62,#63,#64,#65,#66,#67
db #68,#69,#6A,#6B,#6C,#6D,#6E,#6F
db #70,#71,#72,#73,#74,#75,#76,#77
db #78,#79,#7A,#7B,#3F,#00,#00,#00
db #50,#D6,#B0,#01,#14,#01,#00,#00
db #C5,#B9,#F7,#BF,#C4,#30,#92,#81
db #01,#01,#00,#01,#01,#00,#00,#00
db #00,#00,#00,#00,#00,#00,#00,#00
db #00,#00,#00,#00,#00,#00,#00,#00
db #00,#00,#00,#00,#00,#00,#00,#00
db #00,#00,#00,#00,#00,#00,#00,#00
db #00,#00,#00,#00,#00,#00,#00,#00
db #00,#00,#00,#00,#00,#00,#00,#00
db #00,#00,#00,#00,#00,#00,#00,#00
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
3 October 2002
Hello, All!
═══════════════════ 37.C ══════════════════
3. SCR2BANK - image conversion in Spectrum
format in black and white image with 256 gradations of brightness,
located in three memory banks. Using the previous
procedure, the resulting image can be written to a pcx file.
Input: original image loaded from #4000, bank numbers
memory - in N_BANK, brightness gradations - in BRIGHT_0 and
BRIGHT_1.
ORG #8000
SCR2BANK LD HL,N_BANK
LD (PUT_B_1),HL
LD HL,#C000
LD (PUT_B_2),HL
LD DE,0
MAIN CALL GET_PIX
LD B,A
AND 7 ;indication
OUT (254),A ;on the curb
LD A,B
CALL PUT_B
INC D
JR NZ,MAIN
INC E
LD A,E
CP 192
JR NZ,MAIN
RET
;Numbers of memory banks used:
N_BANK DB #10,#11,#13
;---------------------------------------
;The GET_PIX procedure returns the brightness
;the specified pixel of the image.
;
;Input: DE - pixel coordinates.
;Output: A - brightness (0..255).
GET_PIX PUSH DE
;Defining INK, PAPER, BRIGHT:
LD H,0
LD A,E
AND %11111000
LD L,A
ADD HL,HL
ADD HL,HL
LD A,D
RRA
RRA RRA
AND %00011111
LD E,A
LD D,#58
ADD HL,DE
LD A,(HL)
RRA
RRA
AND %00010000
LD (BRIGHT),A
LD A,(HL)
AND 7
LD (INK),A
LD A,(HL)
RRA
RRA
RRA
AND 7
LD (PAPER),A
POP DE
PUSH DE
CALL BYTE
AND (HL)
LD A,0
PAPER EQU $-1
JR Z,GET_PIX_1
LD A,0
INK EQU $-1
GET_PIX_1 ADD A,A
ADD A,0
BRIGHT EQU $-1
LD E,A
LD D,0
LD HL,BRIGHT_0
ADD HL,DE
LD E,(HL)
INC HL
LD D,(HL)
LD HL,#80
ADD HL,DE
LD A,H
POP DE
RET
BYTE LD A,E
AND A
RRA
SCF
RRA
AND A
RRA
XOR E
AND #F8
XOR E
LD H,A
LD A,D
RLCA
RLCA
RLCA
XOR E
AND #C7
XOR E
RLCA
RLCA
LD L,A
LD A,D
AND 7
LD B,A
INC B
LD A,1
LOOP RRCA
DJNZ LOOP
RET
;---------------------------------------
;Brightness gradations in 8.8 format:
BRIGHT_0 DW 0.1112.2139.4412.6062
DW 14555,26811,46556
BRIGHT_1 DW 0.1369.3129.6062.8995
DW 21704,38045,65280
;---------------------------------------
;Procedure PUT_B - writing to memory
;information about the next pixel.
;
;Input: A - pixel value.
PUT_B PUSH AF ;Set
LD A,(0) ;required
PUT_B_1 EQU $-2 ;bank
LD BC,#7FFD ;memory.
OUT(C),A
POP AF
LD HL,0 ;Write.
PUT_B_2 EQU $-2
LD(HL),A
INC HL ;Increase
LD A,H ;address.
OR L
JR NZ,PUT_B_3
LD HL,(PUT_B_1) ;Go to
INC HL ;following
LD (PUT_B_1),HL ;jar.
LD HL,#C000
PUT_B_3 LD (PUT_B_2),HL
RET
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
3 October 2002
Hello, All!
═══════════════════ 37.C ══════════════════
3. SCR2BANK - image conversion in Spectrum
format in black and white image with 256 gradations of brightness,
located in three memory banks. Using the previous
procedure, the resulting image can be written to a pcx file.
Input: original image loaded from #4000, bank numbers
memory - in N_BANK, brightness gradations - in BRIGHT_0 and
BRIGHT_1.
ORG #8000
SCR2BANK LD HL,N_BANK
LD (PUT_B_1),HL
LD HL,#C000
LD (PUT_B_2),HL
LD DE,0
MAIN CALL GET_PIX
LD B,A
AND 7 ;indication
OUT (254),A ;on the curb
LD A,B
CALL PUT_B
INC D
JR NZ,MAIN
INC E
LD A,E
CP 192
JR NZ,MAIN
RET
;Numbers of memory banks used:
N_BANK DB #10,#11,#13
;---------------------------------------
;The GET_PIX procedure returns the brightness
;the specified pixel of the image.
;
;Input: DE - pixel coordinates.
;Output: A - brightness (0..255).
GET_PIX PUSH DE
;Defining INK, PAPER, BRIGHT:
LD H,0
LD A,E
AND %11111000
LD L,A
ADD HL,HL
ADD HL,HL
LD A,D
RRA
RRA RRA
AND %00011111
LD E,A
LD D,#58
ADD HL,DE
LD A,(HL)
RRA
RRA
AND %00010000
LD (BRIGHT),A
LD A,(HL)
AND 7
LD (INK),A
LD A,(HL)
RRA
RRA
RRA
AND 7
LD (PAPER),A
POP DE
PUSH DE
CALL BYTE
AND (HL)
LD A,0
PAPER EQU $-1
JR Z,GET_PIX_1
LD A,0
INK EQU $-1
GET_PIX_1 ADD A,A
ADD A,0
BRIGHT EQU $-1
LD E,A
LD D,0
LD HL,BRIGHT_0
ADD HL,DE
LD E,(HL)
INC HL
LD D,(HL)
LD HL,#80
ADD HL,DE
LD A,H
POP DE
RET
BYTE LD A,E
AND A
RRA
SCF
RRA
AND A
RRA
XOR E
AND #F8
XOR E
LD H,A
LD A,D
RLCA
RLCA
RLCA
XOR E
AND #C7
XOR E
RLCA
RLCA
LD L,A
LD A,D
AND 7
LD B,A
INC B
LD A,1
LOOP RRCA
DJNZ LOOP
RET
;---------------------------------------
;Brightness gradations in 8.8 format:
BRIGHT_0 DW 0.1112.2139.4412.6062
DW 14555,26811,46556
BRIGHT_1 DW 0.1369.3129.6062.8995
DW 21704,38045,65280
;---------------------------------------
;Procedure PUT_B - writing to memory
;information about the next pixel.
;
;Input: A - pixel value.
PUT_B PUSH AF ;Set
LD A,(0) ;required
PUT_B_1 EQU $-2 ;bank
LD BC,#7FFD ;memory.
OUT(C),A
POP AF
LD HL,0 ;Write.
PUT_B_2 EQU $-2
LD(HL),A
INC HL ;Increase
LD A,H ;address.
OR L
JR NZ,PUT_B_3
LD HL,(PUT_B_1) ;Go to
INC HL ;following
LD (PUT_B_1),HL ;jar.
LD HL,#C000
PUT_B_3 LD (PUT_B_2),HL
RET
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.