Monday, January 30, 2017

What if the Amiga had color overlays?

First off, a little bit of background:

One of the things that I've been reading lately is how 8-bit home computers did their graphics.

There was often a tradeoff between color resolution and horizontal resolution.

For example, the Atari 800 had a 320x192 mode that had 2 colors. If you wanted 4 colors, the horizontal resolution would be lowered to 160x192. If you wanted 16 colors, you could lower the resolution to 80x192.

The number of bytes per row would remain constant while the horizontal resolution and the color depth would vary inversely.

Graphics  Antic  Colors   Resolution   Memory (bytes)   Display mode 
      mode     mode 
       8         F      2     320 x 192         8138           Graphics
      15         E      4     160 x 192         8138           Graphics
      11         -     16      80 x 192         8138           Graphics
  
============================================

The c64 had a standard bitmap mode:
http://dustlayer.com/vic-ii/2013/4/26/vic-ii-for-beginners-screen-modes-cheaper-by-the-dozen

https://www.c64-wiki.com/index.php/Standard_Bitmap_Mode

You had 320x200 8k bitmap with the color information for each 8x8 block of pixels stored in 1K of Screen Ram, basically a 40x25 cell color overlay.


==========================================

With the Spectrum ZX you had a 256x192 resolution with each 8x8 block having attributes that defined the foreground and background color out of 16 colors.

https://en.wikipedia.org/wiki/ZX_Spectrum_graphic_modes

============================================


And we have VGA text modes that use 2 bytes: 1 byte to define the character displayed, and another byte to define the foreground and background colors.

==========================================

I always wished that the Amiga had a text mode that would display lots of colors. It was disappointing that there were only 4 colors in a 640x200 Workbench.

It always bothered me that a computer that was supposed to be about colorful graphics couldn't display lots of colors at its highest resolution. The 1.3 workbench colors were pretty garish: blue, white, red and black. Workbench 2.0 colors were so bland: 3 shades of grey (black, grey, white) and blue.


The basic idea here is the same as chroma subsampling: https://en.wikipedia.org/wiki/Chroma_subsampling

"Chroma subsampling is the practice of encoding images by implementing less resolution for chroma information than for luma information, taking advantage of the human visual system's lower acuity for color differences than for luminance."



The big problem with the Amiga was that the chip bandwidth was limited. A 640x200 screen bitmap would take 16K of memory. Each bitplane would be 16K, and a 4 color Workbench would be 32K/frame.


What if the Amiga had a "color overlay" scheme similar to the VGA text screen. You could have a 640x200 screen bitmap consisting of 80 bytes, and a color overlay that uses 80 bytes as well (8 pixels per byte and for each cell you could choose 1 of the 16 possible foreground colors in the high nibble (addressing color registers 0-15, and 1 of 16 possible background colors specifying color register 16-31 in the low nibble). Something like this would have been good for a text screen, or ANSI color graphics. A scheme like this would use 24k of total memory, 16k for the foreground/background bitmap and 8k for the color attributes.


Or maybe you could have a "color overlay" with lower resolution bitmaps: A 640x200 bitmap screen, but with multiple 320x200 bitplanes that would provide additional color information. This would fit neatly into the Amiga bitplane design and allow you to have multiple 8K color planes. This would be a "hybrid" high-low resolution screen.

So for 640x200 with effectively 4 bitplanes of color (16 colors)
16K + 8k + 8k + 8k = 40K which is the same bandwidth as a 5 plane 320x200 screen.

And what if you had flexible bitplane sizing: like x1,x2,x4,x8 width pixels, you could reduce the memory requirement at the expense of color resolution on a per-plane basis.

So with x8 width pixels, a 80 pixel wide bitmap would only use 10 bytes/row instead of 80 bytes/row for x1 width pixels.

A 640x200 screen with x8 width pixel overlay of 4 additional color planes could do 80 + (4*10) or 120 bytes/row.
120*200 = 24K bandwidth/frame.

That would get you 32 colors (maxing out the Amiga's color registers), and with less bandwidth than a 4 color regular workbench.

===================================

It's interesting to study how the Amiga generated video. In the Amiga Hardware Reference manual section on the Blitter, it tells you how the chip memory DMA bandwidth is allocated.


For NTSC, there are 226 color clocks per line, each color clock acts as a "slot" for a 16-bit memory access, and having 452 bytes of total DMA bandwidth per line.

200 lines * 452 bytes/line = 90400 bytes per frame * 60 frames per second = 5,424,000 bytes per second.

The 68000 processor is ideally going to have access on the even slots, with the odd slots going to the custom chipset.


160 color clocks are allocated to the display generation. If that sounds familiar, the Atari 2600 TIA had 160 color clocks for the display generation and 68 color clocks for the horizontal blank.


=================================================

http://amigadev.elowar.com/read/ADCD_2.1/Hardware_Manual_guide/node012B.html



"The time-slot allocation per horizontal line is:

4 cycles for memory refresh
3 cycles for disk DMA
4 cycles for audio DMA (2 bytes per channel)
16 cycles for sprite DMA (2 words per channel)
80 cycles for bitplane DMA (even- or odd-numbered slots
according to the display size used)

Figure 6-9 shows one complete horizontal scan line and how the clock
cycles are allocated.

Figure 6-9: http://amigadev.elowar.com/read/ADCD_2.1/Hardware_Manual_guide/node02D4.html

(I took the liberty of fixing some typos in the diagram: 420->320 and d6->d8 and se0->$e0)



If the display contains four or fewer low resolution bitplanes, the 68000
can be granted alternate memory cycles (if it is ready to ask for the
cycle and is the highest priority item at the time). However, if there are
more than four bitplanes, bitplane DMA will begin to steal cycles from the
68000 during the display.

During the display time for a six bitplane display (low resolution, 320
pixels wide), 160 time slots will be taken by bitplane DMA for each
horizontal line. As you can see from Figure 6-11, bitplane DMA steals 50
percent of the open slots that the processor might have used if there were
only four bitplanes displayed.


                          - timing cycle -
      T                                                     T + 7

      +               *               +               *
   _______________________________________________________________
  |       |       |       |       |       |       |       |       |
  |       |   4   |   6   |   2   |       |   3   |   5   |   1   |
  |_______|_______|_______|_______|_______|_______|_______|_______|

       Figure 6-11: Time Slots Used by a Six Bitplane Display


If you specify four high resolution bitplanes (640 pixels wide), bitplane
DMA needs all of the available memory time slots during the display time
just to fetch the 40 data words for each line of the four bitplanes
(40 * 4 = 160 time slots).  This effectively locks out the 68000 (as well
as the blitter or Copper) from any memory access during the display,
except during horizontal and  vertical blanking .


                          - timing cycle -
      T                                                     T + 7

   _______________________________________________________________
  |       |       |       |       |       |       |       |       |
  |   4   |   2   |   3   |   1   |   4   |   2   |   3   |   1   |
  |_______|_______|_______|_______|_______|_______|_______|_______|

      Figure 6-12: Time Slots Used by a High Resolution Display


============================================================


Ideally, a display would use 80 cycles, with every other cycle going to the CPU or the blitter, but display generation that requires more than 160 bytes per line can gobble up additional slots, up to 160 cycles (320 bytes per line).

That's why you only get 4 colors on a standard 640x200 workbench, because that allows the CPU to run on every even chip memory cycle.

A 16 color 640x200 screen would essentially consume the entire chip memory bandwidth during the display, effectively locking out the cpu. That explains why the system gets very slow when using such a screen.

So any scheme that would boost colors and relieve memory bandwidth requirements would help. There would be tradeoffs, surely the hardware and the programming would have more complexity, and you'd have artifacting but you would have more flexibility too. If you were clever with the alignment of windows, graphics and text to fill lo-res pixels it would be less noticeable.


I suppose that it doesn't matter now with all of the memory bandwidth and truecolor modern graphics of today, but it would've been a neat trick back in the day.

Saturday, January 21, 2017

3d graphics in excel 2013 xy scatter chart

So I figured that if I could put lines on an xy scatter chart, why not 3d graphical vectors.



I thought I'd try to make the scout from Stellar 7.

Boy, it's really hard to make a 3d object by hand that looks good. Things are much harder than they are conceptually.

Anyway, the points for the scout are in colums A-D, D being all ones.

We've got 4x4 matrices for Translation, Rotatation XY, Rotation XZ and Rotation YZ and Translation.

a bunch of MMULTs gives you a final matrix to multiply each row of the 3d points by.

then we use a very simple projection: xproj = F * X / Z and yproj = F * Y / Z where F is the focal plane distance.

The biggest problem is that Excel 2013 hates empty cells when you do matrix multiplication and just invalidates the entire operation, so I did separate MMULTs for each point, and then manually removed the error rows where there were blanks in the final calculation.

And for fun, why not use mousemove events to rotate the model.

You can find the excel file at http://ptouchman.weebly.com/blog where I'm putting my files now.

Thursday, January 12, 2017

Pedometer reading from 2015 and 2016

It's interesting to look back on my pedometer readings from 2015 and 2016 using my Omron HJ-720IT.

I thought I was pretty consistent from year to year and month to month.

Each hundred steps equals roughly a minute of walking, so 8000 steps is roughly 80 minutes.

Wednesday, January 11, 2017

Tiger Learning computer based on the Apple IIe


This is really something. I never knew these even existed. I always wished that they would've made an Apple II dtv like the c64 dtv.


Bounty Bob Strikes Back

I used to really love Miner 2049er on my old apple II back in the day. I wish I'd had a c64 or an atari so I could've played Bounty Bob Strikes Back.

These are the classic games I loved: Miner 2049er, Jumpman, Hard Hat Mack, Mr Robot and his Robot Factory. Genius. Give me this over ultra manic 3d games any day.



And an Atari ST remake called Clod Hopper.

TurtleGraphics with the Turtle in Excel

Let's add a turtle to the Graph.





So we put a bunch of parameters on the sheet for TurtleAngle, TurtleXY, TurtleScale, along with a set of points for the turtle:



and set up some matrices to rotate, scale, and translate the points:



And a little bit of code to update the turtle:



Oh and don't forget to add the range of computed points to the graph as another data series.




And the code is pretty short:

If you're using Libreoffice Calc, add

Option VBASupport 1

and comment out the line in colorpoint


Dim xpos, ypos, angle, pencolor As Single
Dim turtlescale As Single
Dim penstatus As Integer
Dim pi As Single
Dim rowpos As Integer

Sub colorpoint(rowpos)
     'comment out for LibreOffice Calc
     ActiveSheet.ChartObjects(1).Chart.SeriesCollection(1).Points(rowpos).Format.Line.ForeColor.RGB = pencolor
End Sub


Sub pc(acolor)
pencolor = acolor
End Sub

Sub initturtle()
rowpos = 1
pi = 3.14
xpos = 0
ypos = 0
angle = 0
pencolor = RGB(255, 0, 0)
penstatus = 1
Cells(1, 1).Resize(500, 2).Clear
turtlescale = 1
updateturtle
End Sub

Sub tsize(x)
  turtlescale = x / 100
  updateturtle
End Sub

Sub updateturtle()
  Cells(1, 6) = angle
  Cells(2, 6) = xpos
  Cells(2, 7) = ypos
  Cells(3, 6) = turtlescale
End Sub


Sub pu()
 penstatus = 0
End Sub

Sub pd()
 penstatus = 1
End Sub

Sub setangle(x)
  angle = x
  updateturtle
End Sub


Sub rt(x)
  angle = angle - x
  updateturtle
End Sub

Sub lt(x)
  rt -x
End Sub


Sub movepos(newxpos, newypos)
  
  oldxpos = xpos
  oldypos = ypos 
    
  If penstatus = 1 Then
    If (rowpos = 1) Then
      ActiveSheet.Cells(rowpos, 1) = oldxpos
      ActiveSheet.Cells(rowpos, 2) = oldypos
      colorpoint rowpos
      rowpos = rowpos + 1
      ActiveSheet.Cells(rowpos, 1) = newxpos
      ActiveSheet.Cells(rowpos, 2) = newypos
      colorpoint rowpos
    ElseIf (ActiveSheet.Cells(rowpos, 1) = oldxpos) And (ActiveSheet.Cells(rowpos, 2) = oldypos) Then
      rowpos = rowpos + 1
      colorpoint rowpos
      ActiveSheet.Cells(rowpos, 1) = newxpos
      ActiveSheet.Cells(rowpos, 2) = newypos
    Else
      rowpos = rowpos + 1
      colorpoint rowpos
      'assigning a cell to empty doesn't actually do anything in LibreOffice
      'LibreOffice likes clear though
      ActiveSheet.Cells(rowpos, 1) = Empty  
      ActiveSheet.Cells(rowpos, 2) = Empty
      ActiveSheet.Cells(rowpos, 1).clear  
      ActiveSheet.Cells(rowpos, 2).clear  
      rowpos = rowpos + 1
      colorpoint rowpos
      ActiveSheet.Cells(rowpos, 1) = oldxpos
      ActiveSheet.Cells(rowpos, 2) = oldypos
      rowpos = rowpos + 1
      colorpoint rowpos
      ActiveSheet.Cells(rowpos, 1) = newxpos
      ActiveSheet.Cells(rowpos, 2) = newypos
  End If
  End If
   xpos = newxpos
   ypos = newypos
   updateturtle
End Sub

Sub fd(dist)
  newxpos = xpos + Cos(angle / 360 * 2 * pi) * dist
  newypos = ypos + Sin(angle / 360 * 2 * pi) * dist
  movepos newxpos, newypos
End Sub
Sub bk(x)
  fd -x
End Sub


Sub refresh()
Application.ScreenUpdating = False
Application.ScreenUpdating = True
End Sub


Sub test1()
Call initturtle: For j = 1 To 6: For i = 1 To 20: call refresh: lt 360 / 20: fd 20: Next i: rt 60: Next j
End Sub

Sub test2()
Call initturtle: For i = 1 To 50: call refresh: Call pu: movepos Rnd() * 50, Rnd() * 50: Call pd: movepos Rnd() * 50, Rnd() * 50: pc RGB(Rnd() * 255, Rnd() * 255, Rnd() * 255): Next i
End Sub

Tuesday, January 10, 2017

Getting excel to refresh the screen

Figured this out today:

just toggle Application.ScreenUpdating with:

Application.ScreenUpdating = False
Application.ScreenUpdating = True

and excel will refresh the screen.

so make a routine and call it refresh:

sub refresh
Application.ScreenUpdating = False
Application.ScreenUpdating = True
end sub



sub test1
Call initturtle: For j = 1 To 6: For i = 1 To 20: lt 360 / 20: fd 20: call refresh : Next i: rt 60: Next j
end sub

Simple rotation matrix in Libreoffice Calc

Just for fun, I thought I'd see how the matrix multiplication works in Libreoffice.

So we have a little set of XY points in a1:b5 to draw a triangular shape.

Put the rotation angle in c1 and convert that to radians in d1.

A rotation matrix in e1:f2 that is transposed from the wikipedia form as it gets multipled rows by columns.

and put a matrix forula with CTRL+SHIFT+ENTER in g1:h5.

Insert a chart with a two data ranges, graphing a1:b5 and g1:h5. It looks best if you set the axes from -20 to 20.










LibreOffice calc redraws the graph on the fly so you get an animation with this:




Monday, January 9, 2017

Turtlegraphics on an Excel xy scatter chart

I was making an xy chart and discovered that if I had gaps in the data, that they would appear as disconnected lines in the chart.

So then I could make vector graphics on a chart.

And do some turtle graphics.


First, make a blank workbook and then insert a xy scatter chart, set the data source as A1:B200, and make sure that gaps will appear for empty lines.

Then have this code in a VBA Module:

Dim xpos, ypos, angle, pencolor As Single
Dim penstatus As Integer
Dim pi As Single
Dim rowpos As Integer



Sub pc(acolor)
pencolor = acolor
End Sub

Sub initturtle()
rowpos = 1
pi = 3.14
xpos = 0
ypos = 0
angle = 0
pencolor = RGB(255, 0, 0)
penstatus = 1
End Sub


Sub pu()
 penstatus = 0
End Sub

Sub pd()
 penstatus = 1
End Sub

Sub rt(x)
  angle = angle - x
End Sub

Sub lt(x)
  rt -x
End Sub

Sub colorpoint(rowpos)
      ActiveSheet.ChartObjects(1).Chart.SeriesCollection(1).Points(rowpos).Format.Line.ForeColor.RGB = pencolor
End Sub

Sub movepos(newxpos, newypos)
  
  oldxpos = xpos
  oldypos = ypos  
  If penstatus = 1 Then
    If (rowpos = 1) Then
      ActiveSheet.Cells(rowpos, 1) = oldxpos
      ActiveSheet.Cells(rowpos, 2) = oldypos
      colorpoint rowpos
      rowpos = rowpos + 1
      ActiveSheet.Cells(rowpos, 1) = newxpos
      ActiveSheet.Cells(rowpos, 2) = newypos
      colorpoint rowpos
   ElseIf (ActiveSheet.Cells(rowpos, 1) = oldxpos) And (ActiveSheet.Cells(rowpos, 2) = oldypos) Then
      rowpos = rowpos + 1
      colorpoint rowpos
      ActiveSheet.Cells(rowpos, 1) = newxpos
      ActiveSheet.Cells(rowpos, 2) = newypos
      'rowpos = rowpos + 1
    Else
      rowpos = rowpos + 1
      colorpoint rowpos
      ActiveSheet.Cells(rowpos, 1) = Empty
      ActiveSheet.Cells(rowpos, 2) = Empty
      rowpos = rowpos + 1
      colorpoint rowpos
      ActiveSheet.Cells(rowpos, 1) = oldxpos
      ActiveSheet.Cells(rowpos, 2) = oldypos
      rowpos = rowpos + 1
      colorpoint rowpos
      ActiveSheet.Cells(rowpos, 1) = newxpos
      ActiveSheet.Cells(rowpos, 2) = newypos
  End If
  End If
   xpos = newxpos
   ypos = newypos
End Sub

Sub fd(x)
  newxpos = xpos + Cos(angle / 360 * 2 * pi) * x
  newypos = ypos + Sin(angle / 360 * 2 * pi) * x
  movepos newxpos, newypos
  
End Sub
Sub bk(x)
  fd -x
End Sub


sub test1
Call initturtle: For j = 1 To 6: For i = 1 To 20: lt 360 / 20: fd 20: Next i: rt 60: Next j
end sub

sub test2
Call initturtle: For i = 1 To 50: Call pu: movepos Rnd() * 50, Rnd() * 50: Call pd: movepos Rnd() * 50, Rnd() * 50: pc RGB(Rnd() * 255, Rnd() * 255, Rnd() * 255): Next i
end sub



If I do this from the immediate window:

pd : fd 20

for some reason I get an error (it doesn't like functions without parameters followed with a colon)

so I have to use

call pd: fd 20

to get them all on one line.

You can put for loops in the immediate window as well:

initturtle:for i = 1 to 180: call pu : rt 1 : fd 5: call pd : rt 1 : fd 5 : next i

I have yet to add a couple of functions, but this is proof of concept.

Oh yeah, to clear the graphics, just select column A and B and clear the contents.

I was thinking I could add the "turtle" by creating another data series in the chart.

It seems to work in libreoffice calc if I just comment out the line in colorpoint (but then you don't get the color you want):

Sub colorpoint(rowpos)
'ActiveSheet.ChartObjects(1).Chart.SeriesCollection(1).Points(rowpos).Format.Line.ForeColor.RGB = pencolor
End Sub

sub test1
Call initturtle: For j = 1 To 6: For i = 1 To 20: lt 360 / 20: fd 20: Next i: rt 60: Next j
end sub






sub test2
Call initturtle: For i = 1 To 50: Call pu: movepos Rnd() * 50, Rnd() * 50: Call pd: movepos Rnd() * 50, Rnd() * 50: pc RGB(Rnd() * 255, Rnd() * 255, Rnd() * 255): Next i
end sub



Wednesday, January 4, 2017

Calibrating a Stanley Power Meter EM100

So I've got one of these Stanley EM100 power meters. It works fine, but it's got a completely inaccurate calibration.

I put it on my fridge and it was telling me that it only costs $17 per year. Either my fridge is awesome or something's quite wrong.


Hooking it up to a Kill a watt is pretty easy as it redirects the plug in the rear to a socket on the side of the EM100.

After a few days, it's pretty easy to find what the multiplier should be and factor that into the cost per kilowatt hour. The only thing is that the EM100 only goes up to 0.50 per kwh, so if electricity goes up much more I won't be able to do this. So instead of say .12 per kwh, I enter .46 per kwh to get correct cost readings.




I've actually got it hooked up to 2 different kill a watts, one of the older ones and a newer kill a watt EZ.

It's been hooked up for 825 days on the EM100 calculating a cost of $57.67 per year, I wonder how many days the EM100 can keep track of?


The kill a watt EZ has gone over 9999 hours and won't display anything timewise except a flashing 9999.

The Stanley EM100 is kind of neat in that it has a small battery so if the power goes out you don't lose your readings. The Kill a watt EZ has a battery back up as well, but the old kill a watt will reset itself in the case of an outage.


PT-D600 mystery screen

I wondered if the PT-D600 had an information mode, and held down the POWER and the preview (MAGNIFYING GLASS) button and got this:

The newer units don't have a CODE key so you can't do CODE+k.



I wonder if it's some kind of flash update mode.