User Tools

Site Tools


programmable_bitmap_sequencer

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
programmable_bitmap_sequencer [2020/09/28 00:49] laubzegaprogrammable_bitmap_sequencer [2022/07/14 15:23] (current) – [Principles of operation] silverdr
Line 3: Line 3:
 Bitmap sequencer is a VIC-II circuit responsible for feeding non-sprite display data to the video pipeline. It has two modes of operation: direct and indirect, i.e. the plain bitmap mode and the text mode. In the direct mode, the sequencer fetches data from memory and then displays it directly, while in the text mode, every so often it fetches a pointer, which is then used to decide where to get actual display data from. In other words, it first fetches a code of say, letter “T” from the video matrix, and then uses this code to locate the 8 bytes that visually represent the eight rows of eight pixels each, which together form the shape of “T”. As a side note, both modes share parts of chip circuitry, which results in an unusual ordering of display data in the plain bitmap mode. Bitmap sequencer is a VIC-II circuit responsible for feeding non-sprite display data to the video pipeline. It has two modes of operation: direct and indirect, i.e. the plain bitmap mode and the text mode. In the direct mode, the sequencer fetches data from memory and then displays it directly, while in the text mode, every so often it fetches a pointer, which is then used to decide where to get actual display data from. In other words, it first fetches a code of say, letter “T” from the video matrix, and then uses this code to locate the 8 bytes that visually represent the eight rows of eight pixels each, which together form the shape of “T”. As a side note, both modes share parts of chip circuitry, which results in an unusual ordering of display data in the plain bitmap mode.
  
-VASYL provides its own Programmable Bitmap Sequencer (PBS), which is activated by bit ''S_ACTIVE'' of register ''PBS_CONTROL'' (''$40:3'') and can be used alongside or instead of VIC-II’s built-in one. The PBS can override VIC-II's so-called g-accesses to memory, which normally happen during every CPU cycle (here we follow the terminology of [[http://www.zimmers.net/cbmpics/cbm/c64/vic-ii.txt|Christian Bauer's classic VIC-II document]]). For c-accesses you have to continue relying on VIC's built-in circuit.+VASYL provides its own bitmap sequencer, which unlike the VIC-II's internal one is user programmable. Therefore the acronym PBS stands for Programmable Bitmap Sequencer. It is activated by bit ''S_ACTIVE'' of register ''PBS_CONTROL'' (''$40:3'') and can be used alongside or instead of VIC-II’s built-in one. The PBS can override VIC-II's so-called g-accesses to memory, which normally happen during every CPU cycle (here we follow the terminology of [[http://www.zimmers.net/cbmpics/cbm/c64/vic-ii.txt|Christian Bauer's classic VIC-II document]]). For c-accesses you have to continue relying on VIC's built-in circuit.
  
 Here are the main features of PBS: Here are the main features of PBS:
Line 24: Line 24:
 The image below depicts the role of PBS registers in a visual way, please read on for their detailed description. The image below depicts the role of PBS registers in a visual way, please read on for their detailed description.
  
-{{ :staging:pbs1.png?700 |PBS configuration}}+{{ pbs1.png?700 |PBS configuration}}
  
 First, for the PBS to take action, four conditions need to be met:  First, for the PBS to take action, four conditions need to be met: 
Line 34: Line 34:
  
 The first condition is normally met, so no point worrying about it. \\ The first condition is normally met, so no point worrying about it. \\
-As soon as ''S_ACTIVE'' is set, VASYL starts paying attention to the contents of ''S_CYC_START'' and ''S_CYC_STOP'' registers. Normally, after power up or reset these registers are set respectively to 15 and 55, values that correspond to the left and right edges of visible display area. In other words, by default, if ''S_ACTIVE'' is set, PBS overrides all forty memory accesses in any given display line. This is convenient, but by no means required - our window can be as narrow a single cycle. For example, to approximate the configuration visible in the picture, we could set ''S_CYC_START'' to 15+5=20 and ''S_CYC_STOP'' to 55-12=43.+As soon as ''S_ACTIVE'' is set, VASYL starts paying attention to the contents of ''S_CYC_START'' and ''S_CYC_STOP'' registers. Normally, after power up or reset these registers are set respectively to 15 and 55, values that correspond to the left and right edges of visible display area. In other words, by default, if ''S_ACTIVE'' is set, PBS overrides all forty memory accesses in any given display line. This is convenient, but by no means required - our window can be as narrow as a single cycle. For example, to approximate the configuration visible in the picture, we could set ''S_CYC_START'' to 15+5=20 and ''S_CYC_STOP'' to 55-12=43.
  
-Once PBS decides to act, every cycle it performs following operations:+Once PBS decides to act, it performs following operations in every cycle:
  
-  - It uses an internal copy of S_BASE register to determine where to fetch the graphics data from. ''S_BASE'' is a 16-bit register, so the addressable memory is 64KiB - one memory bank. Which of the eight memory banks is used is controlled by bits ''S_RAMBANK'' in register ''PBS_CONTROL'' (''$40:210''). +  - It uses an internal copy of ''S_BASE'' register to determine where to fetch graphics data from. ''S_BASE'' is a 16-bit register, so the addressable memory is 64KiB - one memory bank. Which of the eight memory banks is used is controlled by bits ''S_RAMBANK'' in register ''PBS_CONTROL'' (''$40:210''). 
-  - Once the byte is fetched, VASYL writes it to the VIC-II data bus. +  - Once graphics data byte is fetched, VASYL can do some simple bitwise operations on it (see below), then writes it to the VIC-II data bus. 
-  - Finally, the internal copy of S_BASE is incremented by S_STEP, yielding the address of a byte to use in the next cycle. With a typically organized graphics data, this value is equal to 1.+  - Finally, the internal copy of ''S_BASE'' is incremented by ''S_STEP'', yielding the address of a byte to use in the next cycle. With a typically organized graphics data, this value is equal to 1.
  
-One cycle after the last active cycle in a rasterline, PBS does one more thing - it increases its internal copy of S_BASE by the value of register S_PADDING, yielding the memory address of the first byte of the next line. If our graphics data is laid out sequentially in memory (i.e. the last byte in a line is directly followed by the first byte of the next line), S_PADDING is going to be 0. But if you wanted to open a virtual screen larger than the display window, you would use S_PADDING to "skip over" the currently invisible (off-screen) part of the virtual display - as it happens to the rightmost part of C= logo in the illustration above.+One cycle after the last active cycle in a rasterline, PBS does one more thing - it increases its internal copy of ''S_BASE'' by the value of register ''S_PADDING'', yielding the memory address of the first byte of the next line. If our graphics data is laid out sequentially in memory (i.e. the last byte in a line is directly followed by the first byte of the next line), ''S_PADDING'' is going to be 0. But if you wanted to open a virtual screen larger than the display window, you would use ''S_PADDING'' to "skip over" the currently invisible (off-screen) part of the virtual display - as it happens to the rightmost part of C= logo in the illustration above.
  
-Ok, example time!+==== Examples ====
  
 <code vasyl> <code vasyl>
Line 71: Line 71:
 If you build and run this code, the screen should look as follows. If you build and run this code, the screen should look as follows.
  
-{{ :staging:seq1.png?600 |}}+{{ seq1.png?600 |}}
  
 Note that we use 0 for step value, thus PBS repeatedly fetches the same byte - $ff. If you have VBASIC loaded, you can quickly inspect VASYL memory using ''VLIST 0'' and see that "bitmap" starts at location ''$1a''. Let's change it using ''VPOKE $1a, $aa'' or, even better  Note that we use 0 for step value, thus PBS repeatedly fetches the same byte - $ff. If you have VBASIC loaded, you can quickly inspect VASYL memory using ''VLIST 0'' and see that "bitmap" starts at location ''$1a''. Let's change it using ''VPOKE $1a, $aa'' or, even better 
Line 108: Line 108:
 This time we advance the bitmap pointer by one at the end of every line. Given that this will cause a new byte to be used in each line, we also add some more data after the ''bitmap'' label. The result: This time we advance the bitmap pointer by one at the end of every line. Given that this will cause a new byte to be used in each line, we also add some more data after the ''bitmap'' label. The result:
  
-{{ :staging:seq2.png?600 |}}+{{ seq2.png?600 |}}
  
 which shows the first line filled with byte ''0'', second with ''1'', and so on. These bytes are again located starting from location ''$1a'', so feel free to ''VPOKE'' them some more. which shows the first line filled with byte ''0'', second with ''1'', and so on. These bytes are again located starting from location ''$1a'', so feel free to ''VPOKE'' them some more.
  
-Let's now try using an actual image. You can find it in [[https://github.com/madhackerslab/beamracer-examples/blob/master/asm/mhl.xbm|MadHackersLab GitHub repository]]. The code needs to be adjust for its different size:+Let's now try using an actual image. You can find it in [[https://github.com/madhackerslab/beamracer-examples/blob/master/asm/mhl.xbm|MadHackersLab GitHub repository]]. The code needs to be adjusted for its different size:
  
 <code vasyl [highlight_lines_extra="2,3,4,6,20"]> <code vasyl [highlight_lines_extra="2,3,4,6,20"]>
Line 139: Line 139:
 Here is the result... Here is the result...
  
-{{ :staging:seq3.png?600 |}}+{{ seq3.png?600 |}}
  
 Ouch! What happened here? Closer inspection reveals that the order of bits in each byte seems reversed. This is because VIC uses MSB to LSB bit order when shifting out pixels from bytes PBS gives it, i.e. it first uses bit 7, then 6, then 5, and so on until bit 0 gets to the screen. However, the file format the MHL image was saved in uses LSB to MSB bit order. This could naturally by trivially fixed by inverting the bit ordering in the file. But why bother, if we have hardware to do that for us? Let's change PBS settings as follows: Ouch! What happened here? Closer inspection reveals that the order of bits in each byte seems reversed. This is because VIC uses MSB to LSB bit order when shifting out pixels from bytes PBS gives it, i.e. it first uses bit 7, then 6, then 5, and so on until bit 0 gets to the screen. However, the file format the MHL image was saved in uses LSB to MSB bit order. This could naturally by trivially fixed by inverting the bit ordering in the file. But why bother, if we have hardware to do that for us? Let's change PBS settings as follows:
Line 168: Line 168:
 and VASYL will automatically flip ("mirror") every byte before passing it to VIC-II. Here's the final result: and VASYL will automatically flip ("mirror") every byte before passing it to VIC-II. Here's the final result:
  
-{{ :staging:seq4.png?600 |}}+{{ seq4.png?600 |}}
  
 Bonus stage. ;) With the above image on screen execute the following one-liner. Bonus stage. ;) With the above image on screen execute the following one-liner.
Line 176: Line 176:
 </code> </code>
  
-Hint: location $d in VASYL memory stores the vertical argument of WAIT instruction.+Hint: location $d in VASYL memory stores the vertical argument of the WAIT instruction.
programmable_bitmap_sequencer.1601279353.txt.gz · Last modified: 2020/09/28 00:49 by laubzega