├── .gitignore ├── README.md ├── assembly.png ├── board.jpg ├── com.serial.spin ├── com.serial.terminal.spin ├── i2c.spin ├── keyboard.spin ├── rc2014-vga-r1.png ├── rc2014-vga-r1.zip ├── string.integer.spin ├── usb-fs-host.spin ├── vt100.spin ├── waitvid.80x25.driver.spin └── waitvid.80x25.nine.driver.spin /.gitignore: -------------------------------------------------------------------------------- 1 | .settings 2 | *.o 3 | *.elf 4 | *.binary 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Propeller ANSI / VT-100 Terminal 2 | 3 | Firmware for a serial terminal add-on board designed for the [RC2014](http://rc2014.co.uk/) computer. It adds VGA video output as 4 | 80x25 text (720x400@70Hz) with ANSI / VT-100 terminal emulation, and USB keyboard input. Using a single 5 | [Parallax Propeller](https://www.parallax.com) microcontroller running at 80MHz. 6 | 7 | ![The board](board.jpg) 8 | 9 | ### Connections 10 | 11 | ![Assembly](assembly.png) 12 | 13 | 1. RC2014 bus 14 | 2. USB Keyboard 15 | 3. VGA 16 | 4. Programming header (optional prop-plug compatible) 17 | 18 | **JP1** and **JP2** can be used to disconnect the board from the standard RX and TX lines on the bus and connect to another serial port (for example to the secondary 19 | port of the dual serial board). Use the top pads to connect the RX and TX lines respectively. 20 | 21 | The **USB** host driver supports __keyboards only__, no other devices are supported including hubs. 22 | 23 | The programming header can be used as an auxiliary serial input, all characters received are sent to the RC2014 as they were typed by the user. 24 | Beware that __the port levels are 3.3 volts only__. 25 | 26 | ### Firmware compile 27 | 28 | The firmware can be compiled using [OpenSpin](https://www.maccasoft.com/downloads/) and uploaded to the EEPROM either using the Prop-Plug serial adapter for in-circuit programming 29 | or a stand alone programmer. 30 | 31 | Compile with the following command: 32 | 33 | `openspin -b -u vt100.spin` 34 | 35 | The compiler generates a file named vt100.binary that can be written to the EEPROM using a stand alone programmer. 36 | 37 | **In-circuit programming** 38 | 39 | The firmware can also be written to the EEPROM using a Prop-Plug serial adapter connected to the board using the programming header as illustrated on the picture above 40 | (be sure to connect the cables properly, the leftmost pin marked 3.3v is not used with the Prop-Plug adapter). 41 | 42 | Download the [Propeller Loader](https://www.maccasoft.com/downloads/) tool, connect the adapter to a USB port and issue the following command: 43 | 44 | `propeller-load -p /dev/ttyUSB0 -e -r vt100.binary` 45 | 46 | The -p parameter specifies the serial port assigned to the Prop-Plug adapter in the notation used by the operating system (/dev/ttyUSBx for Linux, COMxx for Windows). 47 | 48 | After programming the board is reset and the new firmware immediately usable. 49 | 50 | ### Terminal Settings 51 | 52 | Press **CTRL-F10** on the keyboard to switch to the terminal settings screen from which it is possible to configure the keyboard language mapping, cursor style, cursor 53 | keys mapping and more. Press the setting's key to toggle between options. Press **CTRL-F10** again to permanently save the settings and return to the terminal screen. 54 | 55 | Available keyboard languages are: DE (Germany), FR (France, QZERTY), IT (Italy), NO (Norway), UK (United Kingdom) and US (United States). 56 | 57 | Available cursor key mappings are: VT-100 (reset mode sends `\ESC[A`, `\ESC[B`, etc.), VT-100 APPL. (set mode sends `\ESCOA`, `\ESCOB`, etc.) and WordStar which sends control characters compatible 58 | with the WordStar word processor. 59 | 60 | Press **CTRL-F9** to toggle the [National Replacement Character Set](https://en.wikipedia.org/wiki/National_Replacement_Character_Set) option. 61 | 62 | ### Terminal ANSI Codes 63 | 64 | The following escape sequences can be used to control the terminal behaviour 65 | 66 | * **`\ESC[{COUNT}A`** 67 | Moves the cursor up by COUNT rows; the default count is 1. 68 | * **`\ESC[{COUNT}B`** 69 | Moves the cursor down by COUNT rows; the default count is 1. 70 | * **`\ESC[{COUNT}C`** 71 | Moves the cursor forward by COUNT columns; the default count is 1. 72 | * **`\ESC[{COUNT}D`** 73 | Moves the cursor backwards by COUNT columns; the default count is 1. 74 | * **`\ESC[{ROW];{COLUMN}H`** 75 | Sets the cursor position where subsequent text will begin. If no ROW/COLUMN parameters 76 | are provided (ie. `\ESC[H`), the cursor will move to the home position, at the upper left 77 | of the screen. 78 | * **`\ESC[K`** 79 | Clear line from cursor right. 80 | * **`\ESC[1K`** 81 | Clear line from cursor left. 82 | * **`\ESC[2K`** 83 | Clear entire line. 84 | * **`\ESC[J`** 85 | Clear screen from cursor down. 86 | * **`\ESC[1J`** 87 | Clear screen from cursor up. 88 | * **`\ESC[2J`** 89 | Clear entire screen. 90 | * **`\ESC[{ROW];{COLUMN}f`** 91 | Same as `\ESC[{ROW];{COLUMN}H`. 92 | * **`\ESC[{TOP};{BOTTOM}r`** 93 | Selects top and bottom margins, defining the scrolling region. If TOP and BOTTOM are not selected, the complete screen is used (no margins). 94 | * **`\ESC[{NUM1};...;{NUMn}m`** 95 | Sets multiple display attribute settings. The following lists supported attributes: 96 | **`0`** - Reset all attributes 97 | **`1`** - Bright 98 | **`2`** - Dim 99 | **`5`** - Blink 100 | **`7`** - Reverse 101 | **`25`** - Blink off 102 | **`30..37`** - Foreground color (black, red, green, yellow, blue, magenta, cyan, white) 103 | **`38;5;{NUM}`** - Foreground color to {NUM} (0-15) 104 | **`39;{NUM}`** - Default foreground color 105 | **`40..47`** - Background color (black, red, green, yellow, blue, magenta, cyan, white) 106 | **`48;5;{NUM}`** - Background color to {NUM} (0-7) 107 | **`49;{NUM}`** - Default background color 108 | * **`\ESC[5n`** 109 | Terminal status request. Response is always `\ESC[0n` indicating the terminal is in good condition. 110 | * **`\ESC[6n`** 111 | Reports the cursor position to the application as (as though typed at the 112 | keyboard) `\ESC[{ROW];{COLUMN}R` 113 | * **`\ESC[s`** 114 | Save current cursor position. 115 | * **`\ESC[u`** 116 | Restores the saved cursor position. 117 | * **`\ESC[?1h`** 118 | Set cursor keys to application. 119 | * **`\ESC[?1l`** 120 | Set cursor keys to cursor. 121 | * **`\ESC[?12h`** 122 | Enable cursor blinking. 123 | * **`\ESC[?12l`** 124 | Disable cursor blinking. 125 | * **`\ESC[?25h`** 126 | Show cursor. 127 | * **`\ESC[?25l`** 128 | Hide cursor. 129 | * **`\ESC[{NUM}q`** 130 | Set cursor style. {NUM} is 0/1=blinking block, 2=steady block, 3=blinking underline, 4=steady underline. 131 | * **`\ESC[L`** 132 | Insert line. 133 | * **`\ESC[M`** 134 | Delete line. 135 | * **`\ESC[?42h`** 136 | Enable National Replacement Character Set. 137 | * **`\ESC[?42l`** 138 | Disable National Replacement Character Set. 139 | 140 | Where `\ESC` is the binary character `1Bh (or 27)` and `{NUM}`, `{COUNT}`, 141 | `{ROW}`, `{COLUMN}`, `{TOP}`, `{BOTTOM}` is any sequence of numeric characters 142 | like `123`. 143 | 144 | ### Usage from BASIC 145 | 146 | ``` 147 | 10 PRINT CHR$(27);"[1;31m";"TEXT IN RED";CHR$(27);"[0m" 148 | ``` 149 | 150 | ### Schematic 151 | 152 | ![Schematic](rc2014-vga-r1.png) 153 | 154 | | Parts List | | 155 | | --- | --- | 156 | | R1 = 10.000 ohm 1/4 watt| R19 = 10.000 ohm 1/4 watt| 157 | | R2 = 4.700 ohm 1/4 watt| R20 = 22.000 ohm 1/4 watt| 158 | | R3 = 4.700 ohm 1/4 watt| C1 = 10 uF 63v elettr.| 159 | | R4 = 510 ohm 1/4 watt 1%| C2 = 100.000 pF poly.| 160 | | R5 = 240 ohm 1/4 watt 1%| C3 = 100.000 pF poly.| 161 | | R6 = 510 ohm 1/4 watt 1%| C4 = 100.000 pF poly.| 162 | | R7 = 240 ohm 1/4 watt 1%| XTAL1 = 5 MHz crystal| 163 | | R8 = 510 ohm 1/4 watt 1%| IC1 = MCP1700-3302E/TO| 164 | | R9 = 240 ohm 1/4 watt 1%| IC2 = P8X32A-D40| 165 | | R10 = 240 ohm 1/4 watt 1%| IC3 = 24LC256| 166 | | R11 = 240 ohm 1/4 watt 1%| SP1 = Piezoelectric buzzer| 167 | | R12 = 130 ohm 1/4 watt 1%| JP1 = 2 pin male header| 168 | | R13 = 130 ohm 1/4 watt 1%| JP2 = 2 pin male header| 169 | | R14 = 130 ohm 1/4 watt 1%| CN1 = USB-A connector| 170 | | R15 = 47 ohm 1/4 watt| CN2 = 40 pin male header, right angle| 171 | | R16 = 47 ohm 1/4 watt| CN3 = DB15 HD female connector| 172 | | R17 = 47.000 ohm 1/4 watt| CN4 = 5 pin male header| 173 | | R18 = 47.000 ohm 1/4 watt| | 174 | 175 | Board shared on [OSHPark](https://oshpark.com/shared_projects/Utghpucg) and available on [Tindie](https://www.tindie.com/stores/maccasoft/). 176 | 177 | -------------------------------------------------------------------------------- /assembly.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maccasoft/propeller-vt100-terminal/05537ba06abc4c75f5a51b90573f5776aabd7efe/assembly.png -------------------------------------------------------------------------------- /board.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maccasoft/propeller-vt100-terminal/05537ba06abc4c75f5a51b90573f5776aabd7efe/board.jpg -------------------------------------------------------------------------------- /com.serial.spin: -------------------------------------------------------------------------------- 1 | ' Original Authors: Jeff Martin, Andy Lindsay, Chip Gracey 2 | 3 | {{ 4 | This object implements core serial functionality. 5 | 6 | # Usage 7 | 8 | - Call Start, or StartRxTx, first. 9 | - Be sure to set the Parallax Serial Terminal software to the baudrate specified in Start, and the proper COM port. 10 | - At 80 MHz, this object properly receives/transmits at up to 250 Kbaud, or performs transmit-only at up to 1 Mbaud. 11 | }} 12 | 13 | CON 14 | 15 | BUFFER_LENGTH = 32 ' Recommended as 64 or higher, but can be 2, 4, 8, 16, 32, 64, 128 or 256. 16 | BUFFER_MASK = BUFFER_LENGTH - 1 17 | 18 | VAR 19 | 20 | long cog ' Cog flag/id 21 | 22 | long rx_head ' 9 contiguous longs (must keep order) 23 | long rx_tail 24 | long tx_head 25 | long tx_tail 26 | long rx_pin 27 | long tx_pin 28 | long rxtx_mode 29 | long bit_ticks 30 | long buffer_ptr 31 | 32 | byte rx_buffer[BUFFER_LENGTH] ' Receive and transmit buffers 33 | byte tx_buffer[BUFFER_LENGTH] 34 | 35 | PUB Start(baudrate) : okay 36 | {{ 37 | Start communication with the Parallax Serial Terminal using the Propeller's programming connection. 38 | Waits 1 second for connection, then clears screen. 39 | 40 | Parameters: 41 | baudrate - bits per second. Make sure it matches the Parallax Serial Terminal's 42 | Baud Rate field. 43 | 44 | Returns True (non-zero) if cog started, or False (0) if no cog is available. 45 | }} 46 | 47 | okay := StartRxTx(31, 30, 0, baudrate) 48 | 49 | PUB StartRxTx(rxpin, txpin, mode, baudrate) : okay 50 | {{ 51 | Start serial communication with designated pins, mode, and baud. 52 | 53 | Parameters: 54 | rxpin - input pin; receives signals from external device's TX pin. 55 | txpin - output pin; sends signals to external device's RX pin. 56 | mode - signaling mode (4-bit pattern). 57 | bit 0 - inverts rx. 58 | bit 1 - inverts tx. 59 | bit 2 - open drain/source tx. 60 | bit 3 - ignore tx echo on rx. 61 | baudrate - bits per second. 62 | 63 | Returns : True (non-zero) if cog started, or False (0) if no cog is available. 64 | }} 65 | 66 | Stop 67 | longfill(@rx_head, 0, 4) 68 | longmove(@rx_pin, @rxpin, 3) 69 | bit_ticks := clkfreq / baudrate 70 | buffer_ptr := @rx_buffer 71 | okay := cog := cognew(@entry, @rx_head) + 1 72 | 73 | PUB Stop 74 | {{ 75 | Stop serial communication; frees a cog. 76 | }} 77 | 78 | if cog 79 | cogstop(cog~ - 1) 80 | longfill(@rx_head, 0, 9) 81 | 82 | PUB Count 83 | {{ 84 | Get count of characters in receive buffer. 85 | 86 | Returns: number of characters waiting in receive buffer. 87 | }} 88 | 89 | result := rx_head - rx_tail 90 | result -= BUFFER_LENGTH*(Count < 0) 91 | 92 | PUB Flush 93 | {{ 94 | Flush receive buffer. 95 | }} 96 | 97 | repeat while RxCheck => 0 98 | 99 | PUB Char(ch) 100 | {{ 101 | Send single-byte character. Waits for room in transmit buffer if necessary. 102 | 103 | Parameter: 104 | bytechr - character (ASCII byte value) to send. 105 | }} 106 | 107 | repeat until (tx_tail <> ((tx_head + 1) & BUFFER_MASK)) 108 | tx_buffer[tx_head] := ch 109 | tx_head := (tx_head + 1) & BUFFER_MASK 110 | 111 | if rxtx_mode & %1000 112 | CharIn 113 | 114 | PUB CharIn 115 | {{ 116 | Receive single-byte character. Waits until character received. 117 | 118 | Returns: $00..$FF 119 | }} 120 | 121 | repeat while (result := RxCheck) < 0 122 | 123 | PUB RxCheck 124 | { 125 | Check if character received; return immediately. 126 | 127 | Returns: -1 if no byte received, $00..$FF if character received. 128 | } 129 | 130 | result~~ 131 | if rx_tail <> rx_head 132 | result := rx_buffer[rx_tail] 133 | rx_tail := (rx_tail + 1) & BUFFER_MASK 134 | 135 | PUB GetMailbox 136 | 137 | return @rx_head 138 | 139 | DAT 140 | org 141 | 142 | 143 | entry mov t1,par 'get structure address 144 | add t1,#4 << 2 'skip past heads and tails 145 | 146 | rdlong t2,t1 'get rx_pin 147 | mov rxmask,#1 148 | shl rxmask,t2 149 | 150 | add t1,#4 'get tx_pin 151 | rdlong t2,t1 152 | mov txmask,#1 153 | shl txmask,t2 154 | 155 | add t1,#4 'get rxtx_mode 156 | rdlong rxtxmode,t1 157 | 158 | add t1,#4 'get bit_ticks 159 | rdlong bitticks,t1 160 | 161 | add t1,#4 'get buffer_ptr 162 | rdlong rxbuff,t1 163 | mov txbuff,rxbuff 164 | add txbuff,#BUFFER_LENGTH 165 | 166 | test rxtxmode,#%100 wz 'init tx pin according to mode 167 | test rxtxmode,#%010 wc 168 | if_z_ne_c or outa,txmask 169 | if_z or dira,txmask 170 | 171 | mov txcode,#transmit 'initialize ping-pong multitasking 172 | 173 | idle jmpret rxcode,txcode 'run chunk of tx code, then return 174 | 175 | test rxtxmode,#%001 wz 'wait for idle rx line 176 | test rxmask,ina wc 177 | if_z_ne_c jmp #idle 178 | 179 | 180 | receive jmpret rxcode,txcode 'run chunk of tx code, then return 181 | 182 | test rxtxmode,#%001 wz 'wait for start bit on rx pin 183 | test rxmask,ina wc 184 | if_z_eq_c jmp #receive 185 | 186 | mov rxbits,#9 'ready to receive byte 187 | mov rxcnt,bitticks 188 | shr rxcnt,#1 189 | add rxcnt,cnt 190 | 191 | :start jmpret rxcode,txcode 'run chunk of tx code, then return 192 | 193 | test rxtxmode,#%001 wz 'check start bit on rx pin 194 | test rxmask,ina wc 195 | if_z_eq_c jmp #receive 196 | 197 | mov t1,rxcnt 'check if bit receive period done 198 | sub t1,cnt 199 | cmps t1,#0 wc 200 | if_nc jmp #:start 201 | 202 | :bit add rxcnt,bitticks 'ready next bit period 203 | 204 | :wait jmpret rxcode,txcode 'run chunk of tx code, then return 205 | 206 | mov t1,rxcnt 'check if bit receive period done 207 | sub t1,cnt 208 | cmps t1,#0 wc 209 | if_nc jmp #:wait 210 | 211 | test rxmask,ina wc 'receive bit on rx pin 212 | rcr rxdata,#1 213 | djnz rxbits,#:bit 214 | 215 | shr rxdata,#32-9 'justify and trim received byte 216 | and rxdata,#$FF 217 | test rxtxmode,#%001 wz 'if rx inverted, invert byte 218 | if_nz xor rxdata,#$FF 219 | 220 | rdlong t2,par 'save received byte and inc head 221 | add t2,rxbuff 222 | wrbyte rxdata,t2 223 | sub t2,rxbuff 224 | add t2,#1 225 | and t2,#BUFFER_MASK 226 | wrlong t2,par 227 | 228 | jmp #idle 'byte done, receive next byte 229 | 230 | 231 | 232 | transmit jmpret txcode,rxcode 'run chunk of rx code, then return 233 | 234 | mov t1,par 'check for head <> tail 235 | add t1,#2 << 2 236 | rdlong t2,t1 237 | add t1,#1 << 2 238 | rdlong t3,t1 239 | cmp t2,t3 wz 240 | 241 | if_z jmp #transmit 242 | 243 | add t3,txbuff 'get byte and inc tail 244 | rdbyte txdata,t3 245 | sub t3,txbuff 246 | add t3,#1 247 | and t3,#BUFFER_MASK 248 | wrlong t3,t1 249 | 250 | or txdata,#$100 'ready byte to transmit 251 | shl txdata,#2 252 | or txdata,#1 253 | mov txbits,#11 254 | mov txcnt,cnt 255 | 256 | :bit test rxtxmode,#%100 wz 'output bit on tx pin 257 | test rxtxmode,#%010 wc 'according to mode 258 | if_z_and_c xor txdata,#1 259 | shr txdata,#1 wc 260 | if_z muxc outa,txmask 261 | if_nz muxnc dira,txmask 262 | add txcnt,bitticks 'ready next cnt 263 | 264 | :wait jmpret txcode,rxcode 'run chunk of rx code, then return 265 | 266 | mov t1,txcnt 'check if bit transmit period done 267 | sub t1,cnt 268 | cmps t1,#0 wc 269 | if_nc jmp #:wait 270 | 271 | djnz txbits,#:bit 'another bit to transmit? 272 | 273 | jmp #transmit 'byte done, transmit next byte 274 | 275 | 276 | 277 | t1 res 1 278 | t2 res 1 279 | t3 res 1 280 | 281 | rxtxmode res 1 282 | bitticks res 1 283 | 284 | rxmask res 1 285 | rxbuff res 1 286 | rxdata res 1 287 | rxbits res 1 288 | rxcnt res 1 289 | rxcode res 1 290 | 291 | txmask res 1 292 | txbuff res 1 293 | txdata res 1 294 | txbits res 1 295 | txcnt res 1 296 | txcode res 1 297 | -------------------------------------------------------------------------------- /com.serial.terminal.spin: -------------------------------------------------------------------------------- 1 | ' Original Authors: Jeff Martin, Andy Lindsay, Chip Gracey 2 | 3 | {{ 4 | This object adds extra features specific to Parallax serial terminals. 5 | 6 | This object is heavily based on FullDuplexSerialPlus (by Andy Lindsay), which is itself 7 | heavily based on FullDuplexSerial (by Chip Gracey). 8 | 9 | # Usage 10 | 11 | - Call Start, or StartRxTx, first. 12 | - Be sure to set the Parallax Serial Terminal software to the baudrate specified in Start, and the proper COM port. 13 | - At 80 MHz, this object properly receives/transmits at up to 250 Kbaud, or performs transmit-only at up to 1 Mbaud. 14 | }} 15 | 16 | CON 17 | 18 | '' Control Character Constants 19 | 20 | CS = 16 ' Clear Screen 21 | CE = 11 ' Clear to End of line 22 | CB = 12 ' Clear lines Below 23 | 24 | HM = 1 ' HoMe cursor 25 | PC = 2 ' Position Cursor in x,y 26 | PX = 14 ' Position cursor in X 27 | PY = 15 ' Position cursor in Y 28 | 29 | NL = 13 ' New Line 30 | LF = 10 ' Line Feed 31 | ML = 3 ' Move cursor Left 32 | MR = 4 ' Move cursor Right 33 | MU = 5 ' Move cursor Up 34 | MD = 6 ' Move cursor Down 35 | TB = 9 ' TaB 36 | BS = 8 ' BackSpace 37 | 38 | CON 39 | 40 | MAXSTR_LENGTH = 16 ' Maximum length of received numerical string (not including zero terminator). 41 | 42 | OBJ 43 | 44 | ser : "com.serial" 45 | num : "string.integer" 46 | 47 | VAR 48 | 49 | byte str_buffer[MAXSTR_LENGTH+1] ' String buffer for numerical strings 50 | 51 | PUB Start(baudrate) : okay 52 | {{ 53 | Start communication with the Parallax Serial Terminal using the Propeller's programming connection. 54 | Waits 1 second for connection, then clears screen. 55 | 56 | Parameters: 57 | baudrate - bits per second. Make sure it matches the Parallax Serial Terminal's 58 | Baud Rate field. 59 | 60 | Returns True (non-zero) if cog started, or False (0) if no cog is available. 61 | }} 62 | 63 | okay := ser.Start(baudrate) 64 | Clear 65 | return okay 66 | 67 | PUB StartRxTx(rxpin, txpin, mode, baudrate) 68 | {{ 69 | Start serial communication with designated pins, mode, and baud. 70 | 71 | Parameters: 72 | rxpin - input pin; receives signals from external device's TX pin. 73 | txpin - output pin; sends signals to external device's RX pin. 74 | mode - signaling mode (4-bit pattern). 75 | bit 0 - inverts rx. 76 | bit 1 - inverts tx. 77 | bit 2 - open drain/source tx. 78 | bit 3 - ignore tx echo on rx. 79 | baudrate - bits per second. 80 | 81 | Returns : True (non-zero) if cog started, or False (0) if no cog is available. 82 | }} 83 | 84 | return ser.StartRxTx(rxpin, txpin, mode, baudrate) 85 | 86 | PUB Stop 87 | {{ 88 | Stop serial communication; frees a cog. 89 | }} 90 | 91 | ser.Stop 92 | 93 | PUB Count 94 | {{ 95 | Get count of characters in receive buffer. 96 | }} 97 | 98 | return ser.Count 99 | 100 | PUB Flush 101 | {{ 102 | Flush receive buffer. 103 | }} 104 | 105 | ser.Flush 106 | 107 | PUB Char(ch) 108 | {{ 109 | Send single-byte character. Waits for room in transmit buffer if necessary. 110 | }} 111 | 112 | ser.Char(ch) 113 | 114 | PUB Chars(ch, size) 115 | {{ 116 | Send string of size `size` filled with `bytechr`. 117 | }} 118 | 119 | repeat size 120 | ser.Char(ch) 121 | 122 | PUB CharIn 123 | {{ 124 | Receive single-byte character. Waits until character received. 125 | }} 126 | 127 | return ser.CharIn 128 | 129 | PUB RxCheck 130 | { 131 | Check if character received; return immediately. 132 | 133 | Returns: -1 if no byte received, $00..$FF if character received. 134 | } 135 | 136 | return ser.RxCheck 137 | 138 | PUB Str(stringptr) 139 | {{ 140 | Send zero-terminated string. 141 | Parameter: 142 | stringptr - pointer to zero terminated string to send. 143 | }} 144 | 145 | repeat strsize(stringptr) 146 | ser.Char(byte[stringptr++]) 147 | 148 | PUB StrIn(stringptr) 149 | {{ 150 | Receive a string (carriage return terminated) and stores it (zero terminated) starting at stringptr. 151 | Waits until full string received. 152 | 153 | Parameter: 154 | stringptr - pointer to memory in which to store received string characters. 155 | Memory reserved must be large enough for all string characters plus a zero terminator. 156 | }} 157 | 158 | StrInMax(stringptr, -1) 159 | 160 | PUB StrInMax(stringptr, maxcount) 161 | {{ 162 | Receive a string of characters (either carriage return terminated or maxcount in 163 | length) and stores it (zero terminated) starting at stringptr. Waits until either 164 | full string received or maxcount characters received. 165 | 166 | Parameters: 167 | stringptr - pointer to memory in which to store received string characters. 168 | Memory reserved must be large enough for all string characters plus a zero terminator (maxcount + 1). 169 | maxcount - maximum length of string to receive, or -1 for unlimited. 170 | }} 171 | 172 | repeat while (maxcount--) 'While maxcount not reached 173 | if (byte[stringptr++] := ser.CharIn) == NL 'Get chars until NL 174 | quit 175 | byte[stringptr+(byte[stringptr-1] == NL)]~ 'Zero terminate string; overwrite NL or append 0 char 176 | 177 | PUB Dec(value) 178 | {{ 179 | Send value as decimal characters. 180 | Parameter: 181 | value - byte, word, or long value to send as decimal characters. 182 | }} 183 | 184 | Str(num.Dec(value)) 185 | 186 | PUB DecIn 187 | {{ 188 | Receive carriage return terminated string of characters representing a decimal value. 189 | 190 | Returns: the corresponding decimal value. 191 | }} 192 | 193 | StrInMax(@str_buffer, MAXSTR_LENGTH) 194 | return num.StrToBase(@str_buffer, 10) 195 | 196 | PUB Bin(value, digits) 197 | {{ 198 | Send value as binary characters up to digits in length. 199 | 200 | Parameters: 201 | value - byte, word, or long value to send as binary characters. 202 | digits - number of binary digits to send. Will be zero padded if necessary. 203 | }} 204 | 205 | Str(num.Bin(value,digits)) 206 | 207 | PUB BinIn 208 | {{ 209 | Receive carriage return terminated string of characters representing a binary value. 210 | 211 | Returns: the corresponding binary value. 212 | }} 213 | 214 | StrInMax(@str_buffer, MAXSTR_LENGTH) 215 | return num.StrToBase(@str_buffer, 2) 216 | 217 | PUB Hex(value, digits) 218 | {{ 219 | Send value as hexadecimal characters up to digits in length. 220 | Parameters: 221 | value - byte, word, or long value to send as hexadecimal characters. 222 | digits - number of hexadecimal digits to send. Will be zero padded if necessary. 223 | }} 224 | 225 | Str(num.Hex(value, digits)) 226 | 227 | PUB HexIn 228 | {{ 229 | Receive carriage return terminated string of characters representing a hexadecimal value. 230 | 231 | Returns: the corresponding hexadecimal value. 232 | }} 233 | 234 | StrInMax(@str_buffer, MAXSTR_LENGTH) 235 | return num.StrToBase(@str_buffer, 16) 236 | 237 | PUB Clear 238 | {{ 239 | Clear screen and place cursor at top-left. 240 | }} 241 | 242 | ser.Char(CS) 243 | 244 | PUB NewLine 245 | {{ 246 | Clear screen and place cursor at top-left. 247 | }} 248 | 249 | ser.Char(NL) 250 | 251 | PUB Position(x, y) 252 | {{ 253 | Position cursor at column x, row y (from top-left). 254 | }} 255 | 256 | ser.Char(PC) 257 | ser.Char(x) 258 | ser.Char(y) 259 | 260 | PUB PositionX(x) 261 | {{ 262 | Position cursor at column x of current row. 263 | }} 264 | 265 | ser.Char(PX) 266 | ser.Char(x) 267 | 268 | PUB PositionY(y) 269 | {{ 270 | Position cursor at row y of current column. 271 | }} 272 | ser.Char(PY) 273 | ser.Char(y) 274 | 275 | PUB MoveLeft(x) 276 | {{ 277 | Move cursor left x characters. 278 | }} 279 | 280 | repeat x 281 | ser.Char(ML) 282 | 283 | PUB MoveRight(x) 284 | {{ 285 | Move cursor right x characters. 286 | }} 287 | 288 | repeat x 289 | ser.Char(MR) 290 | 291 | PUB MoveUp(y) 292 | {{ 293 | Move cursor up y lines. 294 | }} 295 | 296 | repeat y 297 | ser.Char(MU) 298 | 299 | PUB MoveDown(y) 300 | {{ 301 | Move cursor down y lines. 302 | }} 303 | 304 | repeat y 305 | ser.Char(MD) 306 | 307 | PUB ReadLine(line, maxline) : size | c 308 | 309 | repeat 310 | case c := CharIn 311 | BS: if size 312 | size-- 313 | Char(c) 314 | NL, LF: byte[line][size] := 0 315 | Char(c) 316 | quit 317 | other: if size < maxline 318 | byte[line][size++] := c 319 | Char(c) 320 | 321 | PUB GetMailbox 322 | 323 | return ser.GetMailbox 324 | -------------------------------------------------------------------------------- /i2c.spin: -------------------------------------------------------------------------------- 1 | '' ****************************************************************************** 2 | '' * I2C SPIN Object * 3 | '' * James Burrows May 2006 * 4 | '' * Version 1.3 * 5 | '' ****************************************************************************** 6 | '' 7 | '' Synchronous Serial adapted from the ShiftIO object by Jon Williams @ Parallax 8 | '' 9 | '' for reference look at: www.semiconductors.philips.com/ acrobat/literature/9398/39340011.pdf 10 | '' 11 | '' this is adapted from the Parallax Javalin i2c library, and the Jon's BS2 bit bang routine. 12 | '' 13 | '' this object provides the PUBLIC functions: 14 | '' -> Init - sets up the address and inits sub-objects such 15 | '' -> Stop - stop the object 16 | '' -> Start - start the object 17 | '' -> getError - gets the error flag 18 | '' -> clearError - clear the error 19 | '' -> isStarted - returns true/false for the object started state 20 | '' -> testBus - allows the pullups to raise the bus to pinHIGH's. Sets errorlevel 21 | '' -> devicePresent - sents and address byte and looks for the device to ACK 22 | '' -> readLocation - high level READ functions using the four low level functions 23 | '' -> writeLocation - high level WRITE functions using the four low level functions 24 | '' -> i2cStart - performs a bus start 25 | '' -> i2cStop - performs a bus stop 26 | '' -> i2cRead - performs a read 27 | '' -> i2cWrite - performs a write 28 | '' 29 | '' this object provides the PRIVATE functions: 30 | '' -> None 31 | '' 32 | '' this object uses the following sub OBJECTS: 33 | '' -> None 34 | '' 35 | '' Revision History: 36 | '' -> V1 - Release 37 | '' -> V1.1 - Documentation update, slight code tidy-up 38 | '' -> V1.2 - More Documentation update, slight code tidy-up 39 | '' i2cWrite changes - see below. 40 | '' -> V1.3 - additional parameter added to the init to allow for SCL lines that 41 | '' don't have resistor pull-ups. This allows for the Propeller Dev board SCL and SDA pins 42 | '' 28/29 if you use them. Note that if the i2cObject code is set to drive the 43 | '' i2c SCL line - this will not work if more than one cog attempts to run it (obviously) 44 | '' not at the same time!!) (thanks to Michael Green on the forums) 45 | '' Normally set to FALSE if you have pull-ups, and TRUE if you dont. 46 | '' 47 | '' ++ i2cBits parameter removed from the i2cRead function as it was not used!! 48 | '' 49 | '' 50 | '' i2c Address's: 51 | '' -> Specify the full 8 bit address when using the high level object, they will add the R/W 1/0 bit in the LSB 52 | '' -> Low level objects - specify it as you want to come out! 53 | '' 54 | '' ACK/NAK bit return: 55 | '' -> Implemented to allow the calling application to see the last 32 ack/naks returned in a multi-write operation. 56 | '' For example, if a call makes three (3) writes and produces no errors (acks) you will get a 0 returned. 57 | '' If the middle write is NAK'd by the device, you'll get (in binary) %010 back. So if you get a >0 back 58 | '' as a return a NAK as occurred somewhere. Look at it in binary to see which! 59 | '' 60 | '' Bus Error handing: 61 | '' -> Partially implemented. When its completed each method can set error flags appropriate to the condition 62 | '' -> External routines can use getError and clear error to access the error states. Could be expanded to include 63 | '' -> bus arbiration errors in the future. 64 | '' 65 | '' i2cWrite - changed (v1.2) : i2cwrite now always outputs the top 8 bits of a the LONG data, i.e. 66 | '' %xxxxxxxx_00000000_00000000_00000000 67 | '' The i2cbits parameter controlls an shift, so if you pass i2cWrite (10, 8) it will shift the low byte left 24 bits 68 | '' %00000000_00000000_00000000_00001010 is shifted to become... 69 | '' %00001010_00000000_00000000_00000000 then the top 8 bits are output! 70 | '' 71 | 72 | CON 73 | ' i2c bus contants 74 | _i2cNAK = 1 75 | _i2cACK = 0 76 | _pinHigh = 1 77 | _pinLow = 0 78 | _i2cByteAddress = 8 79 | _i2cWordAddress = 16 80 | 81 | ' arbitory error constants 82 | _None = 1000 83 | _ObjNotInit = 1001 84 | _SCLHold = 1002 85 | _i2cSDAFault = 1003 86 | _i2cSCLFault = 1004 87 | _i2cStopFault = 1005 88 | 89 | ' device constants 90 | EEPROM = $A0 91 | 92 | VAR 93 | word i2cSDA, i2cSCL 94 | long ErrorState 95 | long i2cStarted 96 | long lastAckBit 97 | byte driveLines 98 | 99 | '' ****************************************************************************** 100 | '' * These are the high level routines * 101 | '' ****************************************************************************** 102 | 103 | PUB Start : okay 104 | ' start the object 105 | if i2cStarted == false 106 | i2cStarted := true 107 | ' init both i2c lines as inputs. 108 | dira[i2cSDA] ~ 109 | dira[i2cSCL] ~ 110 | ' init the last ack bit 111 | lastAckBit := 1 ' default to NAK 112 | ' init no error state 113 | ErrorState := _None 114 | 115 | 116 | PUB Stop : okay 117 | ' stop the object and release the pins 118 | if i2cStarted == true 119 | i2cStarted := false 120 | ' release both i2c lines as inputs. 121 | dira[i2cSDA] ~ 122 | dira[i2cSCL] ~ 123 | ' init no error state 124 | ErrorState := _None 125 | 126 | 127 | PUB getLastAckBit : ackbit 128 | ' return the last ack bit 129 | return lastAckBit 130 | 131 | 132 | PUB init(_i2cSDA, _i2cSCL, _driveSCLLine): okay 133 | if lookdown(_i2cSDA : 0..31) > 0 and lookdown(_i2cSCL : 0..31) > 0 134 | ' init the I2C Object 135 | i2cSDA := _i2cSDA 136 | i2cSCL := _i2cSCL 137 | ' init the drive'n parameter for SCL lines 138 | driveLines := _driveSCLLine 139 | ' init both i2c lines as inputs. 140 | if driveLines == false 141 | dira[i2cSDA] ~ 142 | dira[i2cSCL] ~ 143 | else 144 | dira[i2cSDA] ~~ 145 | dira[i2cSCL] ~~ 146 | 147 | ' init no error state 148 | ErrorState := _None 149 | i2cStarted := true 150 | else 151 | ErrorState := _ObjNotInit 152 | i2cStarted := false 153 | 154 | ' return true if init was OK 155 | return i2cStarted 156 | 157 | 158 | PUB getError : errorCode 159 | ' return the error state variable 160 | return(ErrorState) 161 | 162 | 163 | PUB clearError 164 | ' clear the error state variable 165 | ErrorState := _None 166 | 167 | 168 | PUB isStarted : i2cState 169 | ' return the i2cStarted flag (true/false) 170 | return i2cStarted 171 | 172 | 173 | PUB testBus : errorCode 174 | ' put both lines to input 175 | ' they should both go high from the resistor pullup's 176 | ErrorState := _None 177 | dira[i2cSDA] ~ 178 | dira[i2cSCL] ~ 179 | if ina[i2cSDA] <> _pinHigh 180 | ErrorState := _i2cSDAFault 181 | if ina[i2cSCL] <> _pinHigh 182 | ErrorState := _i2cSCLFault 183 | return ErrorState 184 | 185 | 186 | PUB devicePresent(deviceAddress) : ackbit 187 | ' send the deviceAddress and listen for the ACK 188 | ackbit := _i2cNAK 189 | if i2cStarted == true 190 | i2cStart 191 | ackbit := i2cWrite(deviceAddress | 0,8) 192 | i2cStop 193 | if ackbit == _i2cACK 194 | ackbit := true 195 | else 196 | ackbit := false 197 | return ackbit 198 | 199 | 200 | PUB eeprom_read(deviceRegister, dataBuffer, dataSize) : ackbit 201 | ' do a standard i2c address, then read 202 | ' read a device's register 203 | ackbit := _i2cACK 204 | 205 | if i2cStarted == true 206 | i2cStart 207 | ackbit := (ackbit << 1) | i2cWrite(EEPROM | 0, 8) 208 | 209 | ' send a 16 bit deviceRegister 210 | ackbit := (ackbit << 1) | i2cWrite(deviceRegister << 16, 0) 211 | ackbit := (ackbit << 1) | i2cWrite(deviceRegister << 24, 0) 212 | 213 | i2cStart 214 | ackbit := (ackbit << 1) | i2cWrite(EEPROM | 1, 8) 215 | 216 | repeat dataSize - 1 217 | BYTE[dataBuffer] := i2cRead(_i2cACK) 218 | dataBuffer := dataBuffer + 1 219 | 220 | BYTE[dataBuffer] := i2cRead(_i2cNAK) 221 | 222 | i2cStop 223 | else 224 | ackbit := _i2cNAK 225 | ' set the last i2cACK bit 226 | lastAckBit := ackbit 227 | return ackbit 228 | 229 | 230 | PUB eeprom_write(deviceRegister, dataBuffer, dataSize) : ackbit 231 | ' do a standard i2c address, then write 232 | ' return the ACK/NAK bit from the device address 233 | ackbit := _i2cACK 234 | 235 | if i2cStarted == true 236 | i2cStart 237 | ackbit := (ackbit << 1) | i2cWrite(EEPROM | 0, 8) 238 | 239 | ' send a 16 bit deviceRegister 240 | ackbit := (ackbit << 1) | i2cWrite(deviceRegister << 16, 0) 241 | ackbit := (ackbit << 1) | i2cWrite(deviceRegister << 24, 0) 242 | 243 | repeat dataSize 244 | ackbit := (ackbit << 1) | i2cWrite(BYTE[dataBuffer], 8) 245 | dataBuffer := dataBuffer + 1 246 | 247 | i2cStop 248 | else 249 | ackbit := _i2cNAK 250 | return ackbit 251 | 252 | 253 | PUB readLocation(deviceAddress, deviceRegister, addressbits, databits) : i2cData | ackbit 254 | ' do a standard i2c address, then read 255 | ' read a device's register 256 | ackbit := _i2cACK 257 | 258 | if i2cStarted == true 259 | i2cStart 260 | ackbit := (ackbit << 1) | i2cWrite(deviceAddress | 0,8) 261 | 262 | ' cope with bigger than 8 bit deviceRegisters, i.e. EEPROM's use 16 bit or more 263 | case addressbits 264 | 8: ' send a 8 bit deviceRegister. (i2cWrite will shift left 24 bits) 265 | ackbit := (ackbit << 1) | i2cWrite(deviceRegister << 24, 0) 266 | 16: ' send a 16 bit deviceRegister 267 | ackbit := (ackbit << 1) | i2cWrite(deviceRegister << 16, 0) 268 | ackbit := (ackbit << 1) | i2cWrite(deviceRegister << 24, 0) 269 | 24: ' send a 24 bit deviceRegister 270 | ackbit := (ackbit << 1) | i2cWrite(deviceRegister << 8, 0) 271 | ackbit := (ackbit << 1) | i2cWrite(deviceRegister << 16, 0) 272 | ackbit := (ackbit << 1) | i2cWrite(deviceRegister << 24, 0) 273 | 32: ' send a 32 bit deviceRegister 274 | ackbit := (ackbit << 1) | i2cWrite(deviceRegister << 0, 0) 275 | ackbit := (ackbit << 1) | i2cWrite(deviceRegister << 8, 0) 276 | ackbit := (ackbit << 1) | i2cWrite(deviceRegister << 16, 0) 277 | ackbit := (ackbit << 1) | i2cWrite(deviceRegister << 24, 0) 278 | other: ' any other value passed! 279 | ackbit := (ackbit << 1) | i2cWrite(deviceRegister << 24, 0) 280 | 281 | i2cStart 282 | ackbit := (ackbit << 1) | i2cWrite(deviceAddress | 1, 8) 283 | i2cData := i2cRead(_i2cNAK) 284 | i2cStop 285 | else 286 | ackbit := _i2cNAK 287 | ' set the last i2cACK bit 288 | lastAckBit := ackbit 289 | ' return the data 290 | return i2cData 291 | 292 | 293 | PUB writeLocation(deviceAddress, deviceRegister, i2cDataValue, addressbits,databits) : ackbit 294 | ' do a standard i2c address, then write 295 | ' return the ACK/NAK bit from the device address 296 | ackbit := _i2cACK 297 | 298 | if i2cStarted == true 299 | i2cStart 300 | ackbit := (ackbit << 1) | i2cWrite(deviceAddress | 0,8) 301 | 302 | ' cope with bigger than 8 bit deviceRegisters, i.e. EEPROM's use 16 bit or more 303 | case addressbits 304 | 8: ' send a 8 bit deviceRegister 305 | ackbit := (ackbit << 1) | i2cWrite(deviceRegister << 24, 0) 306 | 16: ' send a 16 bit deviceRegister 307 | ackbit := (ackbit << 1) | i2cWrite(deviceRegister << 16, 0) 308 | ackbit := (ackbit << 1) | i2cWrite(deviceRegister << 24, 0) 309 | 24: ' send a 24 bit deviceRegister 310 | ackbit := (ackbit << 1) | i2cWrite(deviceRegister << 8, 0) 311 | ackbit := (ackbit << 1) | i2cWrite(deviceRegister << 16, 0) 312 | ackbit := (ackbit << 1) | i2cWrite(deviceRegister << 24, 0) 313 | 32: ' send a 32 bit deviceRegister 314 | ackbit := (ackbit << 1) | i2cWrite(deviceRegister << 0, 0) 315 | ackbit := (ackbit << 1) | i2cWrite(deviceRegister << 8, 0) 316 | ackbit := (ackbit << 1) | i2cWrite(deviceRegister << 16, 0) 317 | ackbit := (ackbit << 1) | i2cWrite(deviceRegister << 24, 0) 318 | other: ' any other value passed! 319 | ackbit := (ackbit << 1) | i2cWrite(deviceRegister << 24, 0) 320 | 321 | ackbit := (ackbit << 1) | i2cWrite(i2cDataValue,8) 322 | i2cStop 323 | else 324 | ackbit := _i2cNAK 325 | return ackbit 326 | 327 | 328 | ' ****************************************************************************** 329 | ' * These are the low level routines * 330 | ' ****************************************************************************** 331 | 332 | PUB i2cStop 333 | ' i2c stop sequence - the SDA goes LOW to HIGH while SCL is HIGH 334 | dira[i2cSCL] ~ 335 | dira[i2cSDA] ~ 336 | 337 | 338 | PUB i2cStart 339 | ' i2c Start sequence - the SDA goes HIGH to LOW while SCL is HIGH 340 | if i2cStarted == true 341 | if driveLines == false 342 | ' if the SDA and SCL lines are correctly pulled up to VDD 343 | dira[i2cSDA] ~ 344 | dira[i2cSCL] ~ 345 | dira[i2cSDA] ~~ 346 | outa[i2cSDA] := _pinLow 347 | repeat until ina[i2cSCL] == _pinHigh 348 | else 349 | ' if the SDA and SCL lines are left floating 350 | dira[i2cSDA] ~ 351 | dira[i2cSCL] ~~ 352 | outa[i2cSCL] := _pinHigh 353 | outa[i2cSDA] := _pinHigh 354 | outa[i2cSDA] := _pinLow 355 | 356 | 357 | PUB i2cWrite(i2cData, i2cBits) : ackbit 358 | ' Write i2c data. Data byte is output MSB first, SDA data line is valid 359 | ' only while the SCL line is HIGH 360 | ackbit := _i2cNAK 361 | 362 | if i2cStarted == true 363 | ' set the i2c lines as outputs 364 | dira[i2cSDA] ~~ 365 | dira[i2cSCL] ~~ 366 | 367 | ' init the clock line 368 | outa[i2cSCL] := _pinLow 369 | 370 | ' send the data 371 | i2cData <<= (32 - i2cBits) 372 | repeat 8 373 | ' set the SDA while the SCL is LOW 374 | outa[i2cSDA] := (i2cData <-= 1) & 1 375 | ' toggle SCL HIGH 376 | outa[i2cSCL] := _pinHigh 377 | ' toogle SCL LOW 378 | outa[i2cSCL] := _pinLow 379 | 380 | ' setup for ACK - pin to input 381 | dira[i2cSDA] ~ 382 | 383 | ' read in the ACK 384 | outa[i2cSCL] := _pinHigh 385 | ackbit := ina[i2cSDA] 386 | outa[i2cSCL] := _pinLow 387 | 388 | ' leave the SDA pin LOW 389 | dira[i2cSDA] ~~ 390 | outa[i2cSDA] := _pinLow 391 | 392 | ' return the ackbit 393 | return ackbit 394 | 395 | 396 | PUB i2cRead(ackbit): i2cData 397 | ' Read in i2c data, Data byte is output MSB first, SDA data line is valid 398 | ' only while the SCL line is HIGH 399 | if i2cStarted == true 400 | ' set the SCL to output and the SDA to input 401 | dira[i2cSCL] ~~ 402 | dira[i2cSDA] ~ 403 | outa[i2cSCL] := _pinLow 404 | 405 | ' clock in the byte 406 | i2cData := 0 407 | repeat 8 408 | outa[i2cSCL] := _pinHigh 409 | i2cData := (i2cData << 1) | ina[i2cSDA] 410 | outa[i2cSCL] := _pinLow 411 | 412 | ' send the ACK or NAK 413 | dira[i2cSDA] ~~ 414 | outa[i2cSCL] := _pinHigh 415 | outa[i2cSDA] := ackbit 416 | outa[i2cSCL] := _pinLow 417 | 418 | ' return the data 419 | return i2cData 420 | -------------------------------------------------------------------------------- /rc2014-vga-r1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maccasoft/propeller-vt100-terminal/05537ba06abc4c75f5a51b90573f5776aabd7efe/rc2014-vga-r1.png -------------------------------------------------------------------------------- /rc2014-vga-r1.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maccasoft/propeller-vt100-terminal/05537ba06abc4c75f5a51b90573f5776aabd7efe/rc2014-vga-r1.zip -------------------------------------------------------------------------------- /string.integer.spin: -------------------------------------------------------------------------------- 1 | ' Original authors: Chip Gracey, Jon Williams 2 | {{ 3 | Provides simple numeric conversion methods; all methods return a pointer to 4 | a string. 5 | }} 6 | CON 7 | 8 | MAX_LEN = 64 ' 63 chars + zero terminator 9 | 10 | VAR 11 | 12 | long idx ' pointer into string 13 | byte nstr[MAX_LEN] ' string for numeric data 14 | 15 | PUB Dec(value) 16 | {{ 17 | Returns pointer to signed-decimal string 18 | }} 19 | 20 | ClearStr(@nstr, MAX_LEN) ' clear output string 21 | return DecToStr(value) ' return pointer to numeric string 22 | 23 | PUB DecPadded(value, width) | t_val, field 24 | {{ 25 | Returns pointer to signed-decimal, fixed-width (space padded) string 26 | }} 27 | 28 | ClearStr(@nstr, MAX_LEN) 29 | width := 1 #> width <# constant(MAX_LEN - 1) ' qualify field width 30 | 31 | t_val := ||value ' work with absolute 32 | field~ ' clear field 33 | 34 | repeat while t_val > 0 ' count number of digits 35 | field++ 36 | t_val /= 10 37 | 38 | field #>= 1 ' min field width is 1 39 | if value < 0 ' if value is negative 40 | field++ ' bump field for neg sign indicator 41 | 42 | if field < width ' need padding? 43 | repeat (width - field) ' yes 44 | nstr[idx++] := " " ' pad with space(s) 45 | 46 | return DecToStr(value) 47 | 48 | PUB DecZeroed(value, digits) | div 49 | {{ 50 | Returns pointer to zero-padded, signed-decimal string 51 | 52 | -- if value is negative, field width is digits+1 53 | }} 54 | 55 | ClearStr(@nstr, MAX_LEN) 56 | digits := 1 #> digits <# 10 57 | 58 | if (value < 0) ' negative value? 59 | -value ' yes, make positive 60 | nstr[idx++] := "-" ' and print sign indicator 61 | 62 | div := 1_000_000_000 ' initialize divisor 63 | if digits < 10 ' less than 10 digits? 64 | repeat (10 - digits) ' yes, adjust divisor 65 | div /= 10 66 | 67 | value //= (div * 10) ' truncate unused digits 68 | 69 | repeat digits 70 | nstr[idx++] := (value / div + "0") ' convert digit to ASCII 71 | value //= div ' update value 72 | div /= 10 ' update divisor 73 | 74 | return @nstr 75 | 76 | PUB Hex(value, digits) 77 | {{ 78 | Returns pointer to a digits-wide hexadecimal string 79 | }} 80 | 81 | ClearStr(@nstr, MAX_LEN) 82 | return HexToStr(value, digits) 83 | 84 | PUB HexIndicated(value, digits) 85 | {{ 86 | Returns pointer to a digits-wide, indicated (with $) hexadecimal string 87 | }} 88 | 89 | ClearStr(@nstr, MAX_LEN) 90 | nstr[idx++] := "$" 91 | return HexToStr(value, digits) 92 | 93 | PUB Bin(value, digits) 94 | {{ 95 | Returns pointer to a digits-wide binary string 96 | }} 97 | 98 | ClearStr(@nstr, MAX_LEN) 99 | return BinToStr(value, digits) 100 | 101 | PUB BinIndicated(value, digits) 102 | {{ 103 | Returns pointer to a digits-wide, indicated (with %) binary string 104 | }} 105 | 106 | ClearStr(@nstr, MAX_LEN) 107 | nstr[idx++] := "%" ' preface with binary indicator 108 | return BinToStr(value, digits) 109 | 110 | PRI ClearStr(strAddr, size) 111 | {{ 112 | Clears string at strAddr 113 | 114 | -- also resets global character pointer (idx) 115 | }} 116 | 117 | bytefill(strAddr, 0, size) ' clear string to zeros 118 | idx~ ' reset index 119 | 120 | PRI DecToStr(value) | div, z_pad 121 | {{ 122 | Converts value to signed-decimal string equivalent 123 | -- characters written to current position of idx 124 | -- returns pointer to nstr 125 | }} 126 | 127 | if (value < 0) ' negative value? 128 | -value ' yes, make positive 129 | nstr[idx++] := "-" ' and print sign indicator 130 | 131 | div := 1_000_000_000 ' initialize divisor 132 | z_pad~ ' clear zero-pad flag 133 | 134 | repeat 10 135 | if (value => div) ' printable character? 136 | nstr[idx++] := (value / div + "0") ' yes, print ASCII digit 137 | value //= div ' update value 138 | z_pad~~ ' set zflag 139 | elseif z_pad or (div == 1) ' printing or last column? 140 | nstr[idx++] := "0" 141 | div /= 10 142 | 143 | return @nstr 144 | 145 | PRI HexToStr(value, digits) 146 | {{ 147 | Converts value to digits-wide hexadecimal string equivalent 148 | -- characters written to current position of idx 149 | -- returns pointer to nstr 150 | }} 151 | 152 | digits := 1 #> digits <# 8 ' qualify digits 153 | value <<= (8 - digits) << 2 ' prep most significant digit 154 | repeat digits 155 | nstr[idx++] := lookupz((value <-= 4) & $F : "0".."9", "A".."F") 156 | 157 | return @nstr 158 | 159 | PRI BinToStr(value, digits) 160 | {{ 161 | Converts value to digits-wide binary string equivalent 162 | -- characters written to current position of idx 163 | -- returns pointer to nstr 164 | }} 165 | 166 | digits := 1 #> digits <# 32 ' qualify digits 167 | value <<= 32 - digits ' prep MSB 168 | repeat digits 169 | nstr[idx++] := (value <-= 1) & 1 + "0" ' move digits (ASCII) to string 170 | 171 | return @nstr 172 | 173 | PUB StrToBase(stringptr, base) : value | chr, index 174 | {{ 175 | Converts a zero terminated string representation of a number to a value in the designated base. 176 | 177 | Ignores all non-digit characters (except negative (-) when base is decimal (10)). 178 | }} 179 | 180 | value := index := 0 181 | repeat until ((chr := byte[stringptr][index++]) == 0) 182 | chr := -15 + --chr & %11011111 + 39*(chr > 56) ' Make "0"-"9","A"-"F","a"-"f" be 0 - 15, others out of range 183 | if (chr > -1) and (chr < base) ' Accumulate valid values into result; ignore others 184 | value := value * base + chr 185 | if (base == 10) and (byte[stringptr] == "-") ' If decimal, address negative sign; ignore otherwise 186 | value := - value 187 | -------------------------------------------------------------------------------- /vt100.spin: -------------------------------------------------------------------------------- 1 | {{ 2 | ANSI / VT-100 Terminal Emulator 3 | Copyright (c) 2017-20 Marco Maccaferri and others 4 | 5 | TERMS OF USE: MIT License 6 | }} 7 | 8 | CON 9 | 10 | _XINFREQ = 5_000_000 11 | _CLKMODE = XTAL1 + PLL16X 12 | 13 | scrn_columns = 80 ' screen columns 14 | scrn_rows = 25 ' screen rows 15 | scrn_bcnt = scrn_columns * scrn_rows 16 | 17 | vgrp = 2 ' video pin group 18 | mode = 0 ' 0: FG on/off, 1: FG :==: BG 19 | 20 | video = (vgrp << 9 | mode << 8 | %%333_0) << 21 21 | 22 | CURSOR_ON = vga#CURSOR_ON 23 | CURSOR_OFF = vga#CURSOR_OFF 24 | CURSOR_ULINE = vga#CURSOR_ULINE 25 | CURSOR_BLOCK = vga#CURSOR_BLOCK 26 | CURSOR_FLASH = vga#CURSOR_FLASH 27 | CURSOR_SOLID = vga#CURSOR_SOLID 28 | 29 | CURSOR_MASK = vga#CURSOR_MASK 30 | 31 | #0, CM, CX, CY 32 | 33 | ' USB HID 34 | 35 | REQUEST_OUT = 0 36 | REQUEST_CLASS = $20 37 | REQUEST_TO_INTERFACE = 1 38 | 39 | REQ_SET_REPORT = REQUEST_OUT | REQUEST_CLASS | REQUEST_TO_INTERFACE | $0900 40 | 41 | REPORT_TYPE_OUTPUT = $0200 42 | 43 | LED_NUM_LOCK = $01 44 | LED_CAPS_LOCK = $02 45 | LED_SCROLL_LOCK = $04 46 | 47 | BELL_PINA = 14 48 | BELL_PINB = 15 49 | BELL_FREQ = 800 50 | BELL_MS = 200 51 | 52 | EEPROM_CONFIG = $7FE0 53 | 54 | VAR 55 | 56 | long scrn[scrn_bcnt / 2] ' screen buffer 57 | long scrn1[scrn_bcnt / 2] ' settings screen buffer 58 | long link[vga#res_m] ' mailbox 59 | 60 | long cursor ' text cursor 61 | long cursor_save 62 | 63 | byte usb_buf[64] 64 | byte usb_report[8] 65 | byte usb_led 66 | 67 | word kb_delay 68 | word kb_repeat 69 | 70 | long kb_last 71 | long kb_timer 72 | long kb_mod 73 | long kb_str_table 74 | long kb_map 75 | long kb_settings 76 | 77 | long kb_nrcs_table 78 | 79 | byte ee_config[32] ' 0-1 ID = "P", "X" 80 | ' 2 = keyboard map (0-5) 81 | ' 3 = led status 82 | ' 4 = cursor style 83 | ' 5 = cursor keys (vt-100, app, ws) 84 | ' 6 = app. cursor keys (vt-100, app, ws) 85 | 86 | OBJ 87 | 88 | hc : "usb-fs-host" 89 | ser : "com.serial" 90 | vga : "waitvid.80x25.nine.driver" 91 | kb : "keyboard" 92 | i2c : "i2c" 93 | 94 | PUB start | retval, ifd, epd 95 | 96 | ser.StartRxTx(8, 9, 0, 115200) 97 | 98 | ' user configuration 99 | 100 | i2c.init(29, 28, false) 101 | i2c.eeprom_read(EEPROM_CONFIG, @ee_config, 32) 102 | if ee_config[0] <> "P" or ee_config[1] <> "X" 103 | bytefill(@ee_config, $00, 32) 104 | ee_config[0] := "P" 105 | ee_config[1] := "X" 106 | ee_config[4] := constant(CURSOR_ULINE | CURSOR_FLASH) 107 | ee_config[6] := 1 108 | ee_config[8] := %00_00100 ' 250 ms / 20 cps 109 | 'i2c.eeprom_write(EEPROM_CONFIG, @ee_config, 32) 110 | 111 | ' initialize vga 112 | 113 | wordfill(@scrn, $20_70, scrn_bcnt) 114 | cursor.byte[CX] := 0 115 | cursor.byte[CY] := 0 116 | cursor.byte{CM} := (cursor.byte{CM} & constant(!CURSOR_MASK)) | CURSOR_ON | ee_config[4] 117 | 118 | link{0} := video | @scrn{0} 119 | link[2] := @cursor 120 | vga.init(-1, @link{0}) 121 | 122 | ' keyboard maps 123 | 124 | kb_map := kb.get_map(ee_config[2]) 125 | 126 | case ee_config[5] 127 | 0: 128 | kb_str_table_1 := @strTable 129 | 1: 130 | kb_str_table_1 := @strTableApp 131 | 2: 132 | kb_str_table_1 := @strTableWS 133 | 134 | case ee_config[6] 135 | 0: 136 | kb_str_table_2 := @strTable 137 | 1: 138 | kb_str_table_2 := @strTableApp 139 | 2: 140 | kb_str_table_2 := @strTableWS 141 | 142 | kb_str_table := kb_str_table_1 143 | kb_str_table_ptr := @kb_str_table 144 | 145 | kb_nrcs_table_1 := @nrcs 146 | kb_nrcs_table_2 := get_nrcs_map(ee_config[2]) 147 | kb_nrcs_table := kb_nrcs_table_1 148 | kb_nrcs_table_ptr := @kb_nrcs_table 149 | 150 | ' settings screen setup 151 | 152 | wordfill(@scrn1, $20_70, scrn_bcnt) 153 | 154 | wordfill(@scrn1 + constant((scrn_bcnt - (2 * scrn_columns + 0)) * 2), $C4_F0, 80) 155 | wordfill(@scrn1 + constant((scrn_bcnt - (24 * scrn_columns + 0)) * 2), $C4_F0, 80) 156 | 157 | printAt(0, 31, $F0, string("TERMINAL SETTINGS")) 158 | 159 | printAt(5, 22, $70, string("1 - Keyboard Mapping:")) 160 | printAt(7, 22, $70, string("2 - Cursor Keys:")) 161 | printAt(9, 22, $70, string("3 - Application Cursor Keys:")) 162 | printAt(11, 22, $70, string("4 - Cursor Style:")) 163 | printAt(13, 22, $70, string("5 - Num. Lock:")) 164 | printAt(15, 22, $70, string("6 - Caps Lock:")) 165 | printAt(17, 22, $70, string("7 - Key Repeat Delay:")) 166 | printAt(19, 22, $70, string("8 - Key Repeat Rate:")) 167 | updateSettings 168 | 169 | printAt(24, 55, $F0, string("CTRL-F10 - Save and Exit")) 170 | kb_settings := 0 171 | 172 | ' initialize terminal emulation 173 | 174 | retval := ser.GetMailbox 175 | rx_head := retval 176 | rx_tail := retval + 4 177 | rx_buffer := LONG[retval][8] 178 | tx_head := retval + 8 179 | tx_tail := retval + 12 180 | tx_buffer := rx_buffer + ser#BUFFER_LENGTH 181 | 182 | txt_cursor := @cursor 183 | txt_scrn := @scrn + constant(scrn_bcnt << 1) 184 | 185 | esc_overlay_par := OverlayParams(@_esc, @_esc_end) 186 | attr_overlay_par := OverlayParams(@_attr, @_attr_end) 187 | vt_overlay_par := OverlayParams(@_vt, @_vt_end) 188 | 189 | cognew(@vt100_entry, 0) 190 | 191 | ' USB loop 192 | 193 | kb_delay := word[@repeatDelay][(ee_config[8] & %11_00000) >> 5] 194 | kb_repeat := word[@repeatPeriod][ee_config[8] & %00_11111] 195 | 196 | repeat 197 | if \hc.Enumerate < 0 198 | waitcnt(CNT + CLKFREQ) 199 | next 200 | 201 | if \hc.Configure < 0 202 | repeat 203 | waitcnt(CNT + CLKFREQ) 204 | while hc.GetPortConnection <> hc#PORTC_NO_DEVICE 205 | next 206 | 207 | if not (ifd := hc.FindInterface(3)) 208 | repeat 209 | waitcnt(CNT + CLKFREQ) 210 | while hc.GetPortConnection <> hc#PORTC_NO_DEVICE 211 | next 212 | 213 | ' First endpoint on the first HID interface 214 | epd := hc.NextEndpoint(ifd) 215 | 216 | ' Blink LEDs 217 | usb_led := LED_NUM_LOCK|LED_CAPS_LOCK|LED_SCROLL_LOCK 218 | hc.ControlWrite(REQ_SET_REPORT, REPORT_TYPE_OUTPUT, 0, @usb_led, 1) 219 | waitcnt(CNT + CLKFREQ / 2) 220 | usb_led := ee_config[3] 221 | hc.ControlWrite(REQ_SET_REPORT, REPORT_TYPE_OUTPUT, 0, @usb_led, 1) 222 | 223 | kb_last := 0 224 | kb_timer := 0 225 | 226 | repeat while hc.GetPortConnection <> hc#PORTC_NO_DEVICE 227 | retval := \hc.InterruptRead(epd, @usb_buf, 64) 228 | 229 | if retval == hc#E_TIMEOUT 230 | ' No data available. Try again later. 231 | 232 | elseifnot retval < 0 233 | ' Successful transfer 234 | decode(@usb_buf) 235 | 236 | if kb_last <> 0 and kb_timer <> 0 237 | if (kb_timer - CNT) =< 0 238 | keyPressed(kb_last, kb_mod) 239 | kb_timer := CNT + (CLKFREQ / 1000 * kb_repeat) 240 | 241 | waitcnt(CNT + CLKFREQ) 242 | 243 | PRI decode(buffer) | i, k, mod 244 | 245 | usb_report[0] := BYTE[buffer][0] 246 | usb_report[1] := BYTE[buffer][1] 247 | 248 | if (usb_report[0] & %00100010) <> 0 ' SHIFT 249 | if (usb_report[0] & %01000000) <> 0 ' SHIFT+ALT GR ? 250 | mod := 3 251 | else 252 | mod := 1 253 | elseif (usb_report[0] & %01000000) <> 0 ' ALT GR 254 | mod := 2 255 | else 256 | mod := 0 257 | 258 | repeat i from 2 to 7 259 | k := BYTE[buffer][i] 260 | if k <> 0 and lookdown(k : usb_report[2], usb_report[3], usb_report[4], usb_report[5], usb_report[6], usb_report[7]) == 0 261 | keyPressed(k, mod) 262 | 263 | if k <> kb_last 264 | kb_last := k 265 | kb_mod := mod 266 | kb_timer := CNT + (CLKFREQ / 1000 * kb_delay) 267 | 268 | usb_report[i] := k 269 | 270 | if kb_last <> 0 and lookdown(kb_last : BYTE[buffer][2], BYTE[buffer][3], BYTE[buffer][4], BYTE[buffer][5], BYTE[buffer][6], BYTE[buffer][7]) == 0 271 | kb_last := 0 272 | 273 | PRI keyPressed(k, mod) | c, i, ptr 274 | 275 | if (usb_report[0] & %00010001) and k == $43 ' CTRL-F10 276 | if kb_settings == 0 277 | cursor_save := cursor 278 | 279 | cursor.byte[CX] := 0 280 | cursor.byte[CY] := 0 281 | cursor.byte{CM} := (cursor.byte{CM} & constant(!CURSOR_MASK)) | CURSOR_OFF 282 | link{0} := @scrn1{0} 283 | 284 | kb_settings := 1 285 | else 286 | i2c.eeprom_write(EEPROM_CONFIG, @ee_config, 32) 287 | 288 | cursor := cursor_save 289 | cursor.byte := (cursor.byte & constant(!CURSOR_MASK)) | CURSOR_ON | ee_config[4] 290 | link{0} := @scrn{0} 291 | 292 | kb_map := kb.get_map(ee_config[2]) 293 | 294 | case ee_config[5] 295 | 0: 296 | kb_str_table_1 := @strTable 297 | 1: 298 | kb_str_table_1 := @strTableApp 299 | 2: 300 | kb_str_table_1 := @strTableWS 301 | 302 | case ee_config[6] 303 | 0: 304 | kb_str_table_2 := @strTable 305 | 1: 306 | kb_str_table_2 := @strTableApp 307 | 2: 308 | kb_str_table_2 := @strTableWS 309 | 310 | kb_str_table := kb_str_table_1 311 | 312 | kb_nrcs_table_2 := get_nrcs_map(ee_config[2]) 313 | if kb_nrcs_table <> kb_nrcs_table_1 314 | kb_nrcs_table := kb_nrcs_table_2 315 | 316 | kb_delay := word[@repeatDelay][(ee_config[8] & %11_00000) >> 5] 317 | kb_repeat := word[@repeatPeriod][ee_config[8] & %00_11111] 318 | 319 | kb_settings := 0 320 | return 321 | 322 | if (usb_report[0] & %00010001) and k == $42 ' CTRL-F9 323 | if kb_nrcs_table == kb_nrcs_table_1 324 | kb_nrcs_table := kb_nrcs_table_2 325 | else 326 | kb_nrcs_table := kb_nrcs_table_1 327 | return 328 | 329 | if (usb_led & LED_NUM_LOCK) and k => $59 and k =< $63 330 | c := WORD[kb_map][k * 4 + 1] 331 | else 332 | c := WORD[kb_map][k * 4 + mod] 333 | 334 | 335 | if kb_settings == 1 336 | case c 337 | "1": 338 | ee_config[2]++ 339 | if ee_config[2] => 6 340 | ee_config[2] := 0 341 | updateSettings 342 | "2": 343 | ee_config[5]++ 344 | if ee_config[5] => 3 345 | ee_config[5] := 0 346 | updateSettings 347 | "3": 348 | ee_config[6]++ 349 | if ee_config[6] => 3 350 | ee_config[6] := 0 351 | updateSettings 352 | "4": 353 | if ee_config[4] == constant(CURSOR_ULINE | CURSOR_FLASH) 354 | ee_config[4] := constant(CURSOR_BLOCK | CURSOR_FLASH) 355 | elseif ee_config[4] == constant(CURSOR_BLOCK | CURSOR_FLASH) 356 | ee_config[4] := constant(CURSOR_ULINE | CURSOR_SOLID) 357 | elseif ee_config[4] == constant(CURSOR_ULINE | CURSOR_SOLID) 358 | ee_config[4] := constant(CURSOR_BLOCK | CURSOR_SOLID) 359 | else 360 | ee_config[4] := constant(CURSOR_ULINE | CURSOR_FLASH) 361 | updateSettings 362 | "5": 363 | ee_config[3] ^= LED_NUM_LOCK 364 | updateSettings 365 | "6": 366 | ee_config[3] ^= LED_CAPS_LOCK 367 | updateSettings 368 | "7": 369 | ee_config[8] += %01_00000 370 | ee_config[8] &= %11_11111 371 | updateSettings 372 | "8": 373 | i := (ee_config[8] + 1) & %00_11111 374 | ee_config[8] := (ee_config[8] & %11_00000) | i 375 | updateSettings 376 | return 377 | 378 | 379 | case c 380 | "A".."Z": 381 | if (usb_report[0] & %00010001) ' CTRL 382 | ser.Char(c - "A" + 1) 383 | elseif (usb_led & LED_CAPS_LOCK) 384 | ser.Char(c ^ $20) 385 | else 386 | ser.Char(c) 387 | "a".."z": 388 | if (usb_report[0] & %00010001) ' CTRL 389 | ser.Char(c - "a" + 1) 390 | elseif (usb_led & LED_CAPS_LOCK) 391 | ser.Char(c ^ $20) 392 | else 393 | ser.Char(c) 394 | 0..$FF: 395 | repeat i from 0 to 11 396 | if c == byte[kb_nrcs_table][i] 397 | c := byte[@nrcs][i] 398 | ser.Char(c) 399 | 400 | kb#KeyNumLock: 401 | usb_led ^= LED_NUM_LOCK 402 | hc.ControlWrite(REQ_SET_REPORT, REPORT_TYPE_OUTPUT, 0, @usb_led, 1) 403 | kb#KeyCapsLock: 404 | usb_led ^= LED_CAPS_LOCK 405 | hc.ControlWrite(REQ_SET_REPORT, REPORT_TYPE_OUTPUT, 0, @usb_led, 1) 406 | kb#KeyScrollLock: 407 | usb_led ^= LED_SCROLL_LOCK 408 | hc.ControlWrite(REQ_SET_REPORT, REPORT_TYPE_OUTPUT, 0, @usb_led, 1) 409 | 410 | kb#KeySpace..kb#KeyMaxCode: 411 | ptr := @@word[kb_str_table][c - kb#KeySpace] 412 | repeat strsize(ptr) 413 | ser.Char(byte[ptr]) 414 | ptr++ 415 | 416 | 417 | PRI updateSettings | i 418 | 419 | case ee_config[2] 420 | 0: 421 | printAt(5, 44, $F0, string("US")) 422 | 1: 423 | printAt(5, 44, $F0, string("IT")) 424 | 2: 425 | printAt(5, 44, $F0, string("UK")) 426 | 3: 427 | printAt(5, 44, $F0, string("FR")) 428 | 4: 429 | printAt(5, 44, $F0, string("DE")) 430 | 5: 431 | printAt(5, 44, $F0, string("NO")) 432 | 433 | case ee_config[5] 434 | 0: 435 | printAt(7, 39, $F0, string("VT-100 ")) 436 | 1: 437 | printAt(7, 39, $F0, string("VT-100 APPL.")) 438 | 2: 439 | printAt(7, 39, $F0, string("WordStar ")) 440 | 441 | case ee_config[6] 442 | 0: 443 | printAt(9, 51, $F0, string("VT-100 ")) 444 | 1: 445 | printAt(9, 51, $F0, string("VT-100 APPL.")) 446 | 2: 447 | printAt(9, 51, $F0, string("WordStar ")) 448 | 449 | case ee_config[4] 450 | CURSOR_ULINE: 451 | printAt(11, 40, $F0, string("ULINE ")) 452 | CURSOR_BLOCK: 453 | printAt(11, 40, $F0, string("BLOCK ")) 454 | CURSOR_ULINE | CURSOR_FLASH: 455 | printAt(11, 40, $F0, string("BLINK ULINE")) 456 | CURSOR_BLOCK | CURSOR_FLASH: 457 | printAt(11, 40, $F0, string("BLINK BLOCK")) 458 | 459 | if (ee_config[3] & LED_NUM_LOCK) 460 | printAt(13, 37, $F0, string("ON ")) 461 | else 462 | printAt(13, 37, $F0, string("OFF")) 463 | 464 | if (ee_config[3] & LED_CAPS_LOCK) 465 | printAt(15, 37, $F0, string("ON ")) 466 | else 467 | printAt(15, 37, $F0, string("OFF")) 468 | 469 | case ee_config[8] & %11_00000 470 | %00_00000: 471 | printAt(17, 44, $F0, string("250 ms")) 472 | %01_00000: 473 | printAt(17, 44, $F0, string("500 ms")) 474 | %10_00000: 475 | printAt(17, 44, $F0, string("750 ms")) 476 | %11_00000: 477 | printAt(17, 44, $F0, string("1 s ")) 478 | 479 | i := printDecAt(19, 43, $F0, 10000 / word[@repeatPeriod][ee_config[8] & %00_11111], 1) 480 | printAt(19, i, $F0, string(" cps ")) 481 | 482 | 483 | PRI printAt(row, column, attr, stringptr) | xy 484 | 485 | xy := scrn_bcnt - (row * scrn_columns + column) 486 | 487 | repeat strsize(stringptr) 488 | WORD[@scrn1][xy--] := (BYTE[stringptr++] << 8) | attr 489 | 490 | PRI printDecAt(row, column, attr, value, decimals) | div, z_pad, xy 491 | 492 | xy := scrn_bcnt - (row * scrn_columns + column) 493 | 494 | div := 100_000 ' initialize divisor 495 | z_pad~ ' clear zero-pad flag 496 | 497 | repeat 6 - decimals 498 | if (value => div) ' printable character? 499 | WORD[@scrn1][xy--] := ((value / div + "0") << 8) | attr ' yes, print ASCII digit 500 | column++ 501 | value //= div ' update value 502 | z_pad~~ ' set zflag 503 | elseif z_pad or (div == 1) ' printing or last column? 504 | WORD[@scrn1][xy--] := constant("0" << 8) | attr 505 | column++ 506 | div /= 10 507 | 508 | if decimals > 0 509 | WORD[@scrn1][xy--] := constant("." << 8) | attr 510 | column++ 511 | 512 | repeat decimals 513 | if (value => div) ' printable character? 514 | WORD[@scrn1][xy--] := ((value / div + "0") << 8) | attr ' yes, print ASCII digit 515 | value //= div ' update value 516 | else 517 | WORD[@scrn1][xy--] := constant("0" << 8) | attr 518 | column++ 519 | div /= 10 520 | 521 | return column 522 | 523 | PRI get_nrcs_map(i) 524 | case i 525 | 1: return @nrcs_map_it 526 | 2: return @nrcs_map_uk 527 | 3: return @nrcs_map_fr 528 | 4: return @nrcs_map_de 529 | 5: return @nrcs_map_no 530 | return @nrcs 531 | 532 | 533 | PRI OverlayParams (o_start, o_end) : params | len, hubend, cogend 534 | ' This code sets up the parameters for an overlay (in a format to increase overlay loading speed) 535 | len := o_end - o_start 536 | hubend := o_start + len - 1 537 | cogend := ((@overlay_start - @vt100_entry + len) / 4) - 1 538 | params := hubend << 16 + cogend 539 | 540 | 541 | DAT 542 | 543 | org 0 544 | 545 | vt100_entry mov DIRA, bell_mask 546 | 547 | mov scroll_top, txt_scrn 548 | mov scroll_count, txt_bcnt 549 | sub scroll_count, #scrn_columns 550 | 551 | jmp #_bell 552 | 553 | _loop call #charIn 554 | cmp ch, #$07 wz ' bell 555 | if_z jmp #_bell 556 | cmp ch, #$08 wz ' backspace 557 | if_z jmp #_bs 558 | cmp ch, #$09 wz ' tab 559 | if_z jmp #_tab 560 | cmp ch, #$0A wz ' line feed 561 | if_z jmp #_lf 562 | cmp ch, #$0C wz ' form feed 563 | if_z jmp #_ff 564 | cmp ch, #$0D wz ' carriage return 565 | if_z jmp #_cr 566 | cmp ch, #$1B wz ' esc 567 | if_z mov overlay_par, esc_overlay_par 568 | if_z jmp #overlay_load 569 | 570 | ' NRCS 571 | 572 | _print rdlong t2, kb_nrcs_table_ptr 573 | mov t1, kb_nrcs_table_1 574 | mov t3, #12 575 | rdbyte a, t1 576 | add t1, #1 577 | cmp a, ch wz 578 | rdbyte a, t2 579 | add t2, #1 580 | if_nz djnz t3, #$-5 581 | if_z mov ch, a 582 | 583 | ' write ch to vga buffer 584 | 585 | cmpsub x, #scrn_columns wc 586 | if_nc jmp #:l1 587 | cmp y, #scrn_rows-1 wc,wz 588 | if_c add y, #1 589 | if_nc call #scroll 590 | 591 | :l1 mov t1, y ' t2 := y * 80 592 | shl t1, #4 593 | mov t2, t1 594 | shl t2, #2 595 | add t2, t1 596 | add t2, x ' t2 := t1 + x 597 | shl t2, #1 598 | 599 | mov t1, txt_scrn 600 | sub t1, t2 601 | sub t1, #2 602 | mov a, ch 603 | shl a, #8 604 | or a, txt_attr 605 | wrword a, t1 606 | 607 | add x, #1 608 | 609 | _done mov t1, txt_cursor ' updates cursor position 610 | add t1, #CX 611 | mov a,x 612 | cmp a, #scrn_columns wz,wc 613 | if_nc mov a, #scrn_columns-1 614 | wrbyte a, t1 615 | add t1, #1 616 | wrbyte y, t1 617 | jmp #_loop 618 | 619 | _bell mov FRQA, bell_frq 620 | mov CTRA, bell_ctr 621 | mov a, CNT 622 | add a, bell_duration 623 | waitcnt a, #0 624 | mov CTRA, #0 625 | jmp #_loop 626 | 627 | _bs cmpsub x, #1 628 | jmp #_done 629 | 630 | _tab cmpsub x, #scrn_columns 631 | andn x, #7 632 | add x, #8 633 | jmp #_done 634 | 635 | _lf cmp y, #scrn_rows-1 wc,wz 636 | if_c add y, #1 637 | if_nc call #scroll 638 | jmp #_done 639 | 640 | _ff mov x, #0 641 | mov y, #0 642 | _cls mov t1, #$20 643 | shl t1, #8 644 | or t1, txt_attr 645 | mov a, t1 646 | shl a, #16 647 | or a, t1 648 | mov t1, txt_scrn 649 | sub t1, #4 650 | mov t3, txt_bcnt 651 | shr t3, #1 652 | :l1 wrlong a, t1 653 | sub t1, #4 654 | djnz t3, #:l1 655 | jmp #_done 656 | 657 | _cr mov x, #0 658 | jmp #_done 659 | 660 | ' resident code 661 | 662 | charIn rdlong t1, rx_head 663 | rdlong t2, rx_tail 664 | cmp t1, t2 wz 665 | if_z jmp #charIn 666 | 667 | mov t1, rx_buffer 668 | add t1, t2 669 | rdbyte ch, t1 670 | add t2, #1 671 | and t2, #ser#BUFFER_MASK 672 | wrlong t2, rx_tail 673 | charIn_ret ret 674 | 675 | scroll mov t1, scroll_top 676 | sub t1, #4 677 | mov t2, t1 678 | sub t2, #scrn_columns << 1 679 | mov t3, scroll_count 680 | shr t3, #1 681 | :l1 rdlong a, t2 682 | sub t2, #4 683 | wrlong a, t1 684 | sub t1, #4 685 | djnz t3, #:l1 686 | 687 | mov t2, #$20 688 | shl t2, #8 689 | or t2, txt_attr 690 | mov a, t2 691 | shl a, #16 692 | or a, t2 693 | mov t3, #scrn_columns >> 1 694 | :l2 wrlong a, t1 695 | sub t1, #4 696 | djnz t3, #:l2 697 | 698 | scroll_ret ret 699 | 700 | scroll_top long 0 701 | scroll_count long 0 702 | 703 | ' initialised data and/or presets 704 | 705 | incdst long 1 << 9 706 | 707 | rx_buffer long 0 708 | rx_head long 0 709 | rx_tail long 0 710 | 711 | tx_buffer long 0 712 | tx_head long 0 713 | tx_tail long 0 714 | 715 | txt_cursor long 0 716 | txt_scrn long 0 717 | txt_bcnt long scrn_bcnt 718 | txt_attr long $70 719 | txt_cursor_s long 0 720 | 721 | bell_mask long (1 << BELL_PINA) | (1 << BELL_PINB) 722 | bell_ctr long (%00100 << 26) | (BELL_PINB << 9) | BELL_PINA 723 | bell_frq long trunc(53.6870912 * float(BELL_FREQ)) 724 | bell_duration long (80000000 / 1000) * BELL_MS 725 | 726 | kb_nrcs_table_1 long 0 727 | kb_nrcs_table_ptr long 0 728 | 729 | x long 0 730 | y long 0 731 | 732 | esc_overlay_par long 0 733 | attr_overlay_par long 0 734 | vt_overlay_par long 0 735 | 736 | ' uninitialised data and/or temporaries 737 | 738 | a long 0 739 | ch long 0 740 | ch_mod long 0 741 | t1 long 0 742 | t2 long 0 743 | t3 long 0 744 | 745 | argc long 0 746 | args long 0[8] 747 | 748 | ' overlay loader 749 | 750 | overlay_par long 0-0 751 | 752 | _0x400 long $0000_0400 753 | _djnz0 djnz overlay_par, #_cp2 754 | 755 | overlay_load mov overlay_start, _djnz0 756 | movd _cp2, overlay_par 757 | sub overlay_par, #1 758 | movd _cp1, overlay_par 759 | shr overlay_par, #16 760 | _cp2 rdlong 0-0, overlay_par 761 | sub overlay_par, #7 762 | sub _cp2, _0x400 763 | _cp1 rdlong 0-0, overlay_par 764 | sub _cp1, _0x400 765 | 766 | overlay_start 767 | 768 | fit $1F0 769 | 770 | org overlay_start 771 | 772 | _esc mov argc, #0 773 | mov args, #0 774 | mov args+1, #0 775 | mov ch_mod, #0 776 | 777 | call #charIn 778 | cmp ch, #$1B wz ' esc (again, print it) 779 | if_z jmp #_print 780 | 781 | cmp ch, #"A" wz ' VT-52 compatibility 782 | if_z jmp #_up 783 | cmp ch, #"B" wz 784 | if_z jmp #_down 785 | cmp ch, #"C" wz 786 | if_z jmp #_right 787 | cmp ch, #"D" wz 788 | if_z jmp #_left 789 | cmp ch, #"H" wz 790 | if_z jmp #_cup 791 | 792 | cmp ch, #"J" wz 793 | if_nz cmp ch, #"K" wz 794 | if_nz cmp ch, #"7" wz 795 | if_nz cmp ch, #"8" wz 796 | if_z mov overlay_par, vt_overlay_par 797 | if_z jmp #overlay_load 798 | 799 | cmp ch, #"[" wz 800 | if_nz jmp #_done 801 | 802 | call #charIn 803 | cmp ch, #"?" wz ' check private prefix after "[" 804 | if_nz jmp #:l2+1 805 | mov ch_mod, ch 806 | 807 | :l2 call #charIn 808 | cmp ch, #"0" wc,wz 809 | if_c jmp #:l1 810 | cmp ch, #"9"+1 wc,wz 811 | if_nc jmp #:l1 812 | :s1 mov t1, args ' multiply x 10 813 | shl t1, #1 814 | mov t2, t1 815 | shl t2, #2 816 | add t2, t1 817 | sub ch, #"0" ' adds digit 818 | add t2, ch 819 | :d1 mov args, t2 820 | jmp #:l2 821 | :l1 cmp ch, #";" wz 822 | if_nz jmp #:l3 823 | add argc, #1 824 | add :d1, incdst 825 | add :d2, incdst 826 | add :s1, #1 827 | :d2 mov args, #0 828 | jmp #:l2 829 | 830 | :l3 cmp ch, #"A" wz 831 | if_z jmp #_up 832 | cmp ch, #"B" wz 833 | if_z jmp #_down 834 | cmp ch, #"C" wz 835 | if_z jmp #_right 836 | cmp ch, #"D" wz 837 | if_z jmp #_left 838 | cmp ch, #"H" wz 839 | if_z jmp #_cup 840 | cmp ch, #"f" wz 841 | if_z jmp #_cup 842 | cmp ch, #"r" wz 843 | if_z jmp #_stbm 844 | cmp ch, #"m" wz 845 | if_z mov overlay_par, attr_overlay_par 846 | if_z jmp #overlay_load 847 | 848 | mov overlay_par, vt_overlay_par 849 | jmp #overlay_load 850 | 851 | _up cmp args, #0 wz 852 | if_z add args, #1 853 | sub y, args wc 854 | if_c mov y, #0 855 | jmp #_done 856 | 857 | _down cmp args, #0 wz 858 | if_z add args, #1 859 | add y, args 860 | cmp y, #scrn_rows wc 861 | if_nc mov y, #scrn_rows-1 862 | jmp #_done 863 | 864 | _right cmp args, #0 wz 865 | if_z add args, #1 866 | add x, args 867 | cmp x, #scrn_columns wc 868 | if_nc mov x, #scrn_columns-1 869 | jmp #_done 870 | 871 | _left cmp args, #0 wz 872 | if_z add args, #1 873 | sub x, args wc 874 | if_c mov x, #0 875 | jmp #_done 876 | 877 | _cup mov y, args 878 | cmp y, #scrn_rows wc 879 | if_nc mov y, #scrn_rows 880 | cmpsub y, #1 881 | mov x, args+1 882 | cmp x, #scrn_columns wc 883 | if_nc mov x, #scrn_columns 884 | cmpsub x, #1 885 | jmp #_done 886 | 887 | _stbm cmp argc, #0 wz 888 | if_z jmp #:stbm1 889 | cmp args, #1 wc,wz 890 | if_c jmp #_done 891 | cmp args, #25-1 wc,wz 892 | if_nc jmp #_done 893 | cmp args + 1, #25+1 wc,wz 894 | if_nc jmp #_done 895 | cmp args + 1, args wc,wz 896 | if_c_or_z jmp #_done 897 | 898 | :stbm1 mov scroll_top, txt_scrn 899 | mov scroll_count, txt_bcnt 900 | sub scroll_count, #scrn_columns 901 | cmp argc, #0 wz 902 | if_z jmp #_done 903 | 904 | mov y, args 905 | sub y, #1 wc,wz 906 | if_nz sub scroll_top, #scrn_columns << 1 907 | if_nz sub scroll_count, #scrn_columns 908 | if_nz djnz y, #$-2 909 | 910 | mov y, #25 911 | sub y, args + 1 wc,wz 912 | if_nz sub scroll_count, #scrn_columns 913 | if_nz djnz y, #$-1 914 | jmp #_done 915 | 916 | long $0[($ - overlay_start) // 2] 917 | _esc_end fit $1F0 918 | 919 | org overlay_start 920 | 921 | _attr mov a, args 922 | cmp a, #0 wz ' reset attr 923 | if_z jmp #:reset 924 | cmp a, #1 wz ' bright 925 | if_z jmp #:bright 926 | cmp a, #5 wz ' blink 927 | if_z jmp #:blink 928 | cmp a, #7 wz ' reverse 929 | if_z jmp #:reverse 930 | cmp a, #30 wc ' foreground 931 | if_c jmp #:l2 932 | cmp a, #38 wc,wz 933 | if_c jmp #:fg 934 | if_z jmp #:ext_fg 935 | :l2 cmp a, #39 wz ' reset foreground 936 | if_z jmp #:res_fg 937 | cmp a, #40 wc ' background 938 | if_c jmp #:l3 939 | cmp a, #48 wc,wz 940 | if_c jmp #:bg 941 | if_z jmp #:ext_bg 942 | :l3 cmp a, #49 wz ' reset background 943 | if_z jmp #:res_bg 944 | 945 | :l1 add _attr, #1 946 | sub argc, #1 wc 947 | if_nc jmp #_attr 948 | jmp #_done 949 | 950 | :reset mov txt_attr, #$70 951 | jmp #:l1 952 | :bright or txt_attr, #$80 953 | jmp #:l1 954 | :blink or txt_attr, #$01 955 | jmp #:l1 956 | :reverse mov t1, txt_attr 957 | and t1, #$0E 958 | shl t1, #3 959 | and txt_attr, #$70 960 | shr txt_attr, #3 961 | or txt_attr, t1 962 | jmp #:l1 963 | :fg sub a, #30 964 | shl a, #4 965 | and txt_attr, #$8F 966 | or txt_attr, a 967 | jmp #:l1 968 | :ext_fg add _attr, #1 969 | movs :xs1, _attr 970 | add _attr, #1 971 | movs :xs2, _attr 972 | sub argc, #2 973 | :xs1 mov a, 0-0 974 | cmp a, #2 wz ' 38;2;r;g;b not supported, skip 975 | if_z jmp #:xs3 976 | cmp a, #5 wz ' 38;5;n supported for n <= 15 977 | if_nz jmp #:l1 978 | :xs2 mov a, 0-0 979 | cmp a, #16 wc 980 | if_nc jmp #:l1 981 | shl a, #4 982 | and txt_attr, #$0F 983 | or txt_attr, a 984 | jmp #:l1 985 | :xs3 add _attr, #2 986 | sub argc, #2 987 | jmp #:l1 988 | :res_fg and txt_attr, #$0F 989 | or txt_attr, #$70 990 | jmp #:l1 991 | :bg sub a, #40 992 | shl a, #1 993 | and txt_attr, #$F1 994 | or txt_attr, a 995 | jmp #:l1 996 | :ext_bg add _attr, #1 997 | movs :xs4, _attr 998 | add _attr, #1 999 | movs :xs5, _attr 1000 | sub argc, #2 1001 | :xs4 mov a, 0-0 1002 | cmp a, #2 wz ' 48;2;r;g;b not supported, skip 1003 | if_z jmp #:xs6 1004 | cmp a, #5 wz ' 48;5;n supported for n <= 7 1005 | if_nz jmp #:l1 1006 | :xs5 mov a, 0-0 1007 | cmp a, #8 wc 1008 | if_nc jmp #:l1 1009 | shl a, #1 1010 | and txt_attr, #$F1 1011 | or txt_attr, a 1012 | jmp #:l1 1013 | :xs6 add _attr, #2 1014 | sub argc, #2 1015 | jmp #:l1 1016 | jmp #:l1 1017 | :res_bg and txt_attr, #$F1 1018 | jmp #:l1 1019 | 1020 | long $0[($ - overlay_start) // 2] 1021 | _attr_end fit $1F0 1022 | 1023 | org overlay_start 1024 | 1025 | _vt cmp ch_mod, #"?" wz 1026 | if_z jmp #_pvt 1027 | 1028 | cmp ch, #"J" wz 1029 | if_z jmp #_ed 1030 | cmp ch, #"K" wz 1031 | if_z jmp #_el 1032 | cmp ch, #"L" wz 1033 | if_z jmp #_ins_line 1034 | cmp ch, #"M" wz 1035 | if_z jmp #_del_line 1036 | cmp ch, #"n" wz 1037 | if_z jmp #_dev_status 1038 | cmp ch, #"q" wz 1039 | if_z jmp #_cursor_style 1040 | cmp ch, #"s" wz 1041 | if_z jmp #_save 1042 | cmp ch, #"u" wz 1043 | if_z jmp #_restore 1044 | jmp #_done 1045 | 1046 | _pvt cmp ch, #"h" wz ' private escape sequences 1047 | if_z jmp #_toggles 1048 | cmp ch, #"l" wz 1049 | if_z jmp #_toggles 1050 | jmp #_done 1051 | 1052 | _save mov txt_cursor_s, y 1053 | shl txt_cursor_s, #16 1054 | or txt_cursor_s, x 1055 | jmp #_done 1056 | 1057 | _restore mov x, txt_cursor_s 1058 | and x, #$1FF 1059 | mov y, txt_cursor_s 1060 | shr y, #16 1061 | jmp #_done 1062 | 1063 | _ed cmp args, #2 wz ' clear entire screen 1064 | if_z jmp #_cls 1065 | 1066 | mov t1, y ' t2 := y * 80 1067 | shl t1, #4 1068 | mov t2, t1 1069 | shl t2, #2 1070 | add t2, t1 1071 | add t2, x ' t2 := t2 + x 1072 | mov t1, txt_scrn 1073 | sub t1, t2 1074 | sub t1, t2 ' t1 := pointer to cursor location 1075 | 1076 | mov a, #$20 1077 | shl a, #8 1078 | or a, txt_attr 1079 | 1080 | cmp args, #1 wz 1081 | if_z jmp #:ed1 1082 | cmp args, #0 wz 1083 | if_z jmp #:ed0 1084 | jmp #_done 1085 | :ed0 mov t3, txt_bcnt 1086 | sub t3, t2 wz 1087 | if_nz sub t1, #2 ' clear screen from cursor down 1088 | if_nz wrword a, t1 1089 | if_nz djnz t3, #$-2 1090 | jmp #_done 1091 | :ed1 mov t3, t2 ' clear screen from cursor up 1092 | add t3, #1 1093 | sub t1, #2 1094 | wrword a, t1 1095 | add t1, #2 1096 | djnz t3, #$-2 1097 | jmp #_done 1098 | 1099 | _ins_line mov t1, y 1100 | shl t1, #4 1101 | mov t2, t1 1102 | shl t2, #2 1103 | add t2, t1 1104 | 1105 | mov t3, txt_bcnt 1106 | sub t3, #scrn_columns 1107 | sub t3, t2 1108 | 1109 | mov t1, txt_scrn 1110 | sub t1, txt_bcnt 1111 | sub t1, txt_bcnt 1112 | 1113 | cmp y, #scrn_rows-1 wz 1114 | if_z jmp #:l3 1115 | 1116 | mov t2, t1 1117 | add t2, #scrn_columns << 1 1118 | 1119 | :l1 rdword a, t2 1120 | add t2, #2 1121 | wrword a, t1 1122 | add t1, #2 1123 | djnz t3, #:l1 1124 | 1125 | :l3 mov t2, #$20 1126 | shl t2, #8 1127 | or t2, txt_attr 1128 | mov a, t2 1129 | shl a, #16 1130 | or a, t2 1131 | mov t3, #scrn_columns >> 1 1132 | :l2 wrlong a, t1 1133 | add t1, #4 1134 | djnz t3, #:l2 1135 | 1136 | jmp #_done 1137 | 1138 | _del_line mov t1, y 1139 | shl t1, #4 1140 | mov t2, t1 1141 | shl t2, #2 1142 | add t2, t1 1143 | 1144 | mov t1, txt_scrn 1145 | sub t1, t2 1146 | sub t1, t2 1147 | sub t1, #2 1148 | 1149 | cmp y, #scrn_rows-1 wz 1150 | if_z jmp #:l3 1151 | 1152 | mov t3, txt_bcnt 1153 | sub t3, t2 1154 | sub t3, #scrn_columns 1155 | 1156 | mov t2, t1 1157 | sub t2, #scrn_columns << 1 1158 | 1159 | :l1 rdword a, t2 1160 | sub t2, #2 1161 | wrword a, t1 1162 | sub t1, #2 1163 | djnz t3, #:l1 1164 | 1165 | :l3 mov t2, #$20 1166 | shl t2, #8 1167 | or t2, txt_attr 1168 | mov a, t2 1169 | shl a, #16 1170 | or a, t2 1171 | mov t3, #scrn_columns >> 1 1172 | :l2 wrlong a, t1 1173 | sub t1, #4 1174 | djnz t3, #:l2 1175 | 1176 | jmp #_done 1177 | 1178 | _el mov t1, y ' t1 := y * 80 1179 | shl t1, #4 1180 | mov t2, t1 1181 | shl t2, #2 1182 | add t2, t1 1183 | mov t1, txt_scrn 1184 | sub t1, t2 1185 | sub t1, t2 ' t1 := pointer to begin of line at cursor 1186 | 1187 | mov a, #$20 1188 | shl a, #8 1189 | or a, txt_attr 1190 | 1191 | cmp args, #0 wz 1192 | if_z jmp #:el0 1193 | cmp args, #1 wz 1194 | if_z jmp #:el1 1195 | cmp args, #2 wz 1196 | if_z jmp #:el2 1197 | jmp #_done 1198 | :el0 sub t1, x ' clear line from cursor right 1199 | sub t1, x 1200 | mov t3, #scrn_columns 1201 | sub t3, x wz 1202 | if_nz sub t1, #2 1203 | if_nz wrword a, t1 1204 | if_nz djnz t3, #$-2 1205 | jmp #_done 1206 | :el1 mov t3, x ' clear line from cursor left 1207 | add t3, #1 1208 | sub t1, #2 1209 | wrword a, t1 1210 | djnz t3, #$-2 1211 | jmp #_done 1212 | :el2 mov t3, #scrn_columns ' clear entire line 1213 | sub t1, #2 1214 | wrword a, t1 1215 | djnz t3, #$-2 1216 | jmp #_done 1217 | 1218 | _cursor_style rdbyte t1, txt_cursor 1219 | and t1, #CURSOR_ON 1220 | 1221 | cmp args, #0 wz 1222 | if_nz cmp args, #1 wz 1223 | if_z or t1, #CURSOR_FLASH 1224 | 1225 | cmp args, #3 wz 1226 | if_z or t1, #CURSOR_FLASH 1227 | if_nz cmp args, #4 wz 1228 | if_z or t1, #CURSOR_ULINE 1229 | 1230 | wrbyte t1, txt_cursor 1231 | jmp #_done 1232 | 1233 | _toggles cmp args, #1 wz 1234 | if_z jmp #_key_mode 1235 | cmp args, #12 wz 1236 | if_z jmp #_blink 1237 | cmp args, #25 wz 1238 | if_z jmp #_display 1239 | cmp args, #42 wz 1240 | if_z jmp #_nrcs 1241 | jmp #_done 1242 | 1243 | _key_mode cmp ch, #"l" wz ' cursor key mode 1244 | if_z wrlong kb_str_table_1, kb_str_table_ptr 1245 | if_nz wrlong kb_str_table_2, kb_str_table_ptr 1246 | jmp #_done 1247 | 1248 | kb_str_table_ptr long 0 1249 | kb_str_table_1 long 0 1250 | kb_str_table_2 long 0 1251 | 1252 | _blink rdbyte t1, txt_cursor ' enable / disable blinking 1253 | cmp ch, #"l" wz 1254 | if_z andn t1, #CURSOR_FLASH 1255 | if_nz or t1, #CURSOR_FLASH 1256 | wrbyte t1, txt_cursor 1257 | jmp #_done 1258 | 1259 | _display rdbyte t1, txt_cursor ' show / hide cursor 1260 | cmp ch, #"l" wz 1261 | if_z andn t1, #CURSOR_ON 1262 | if_nz or t1, #CURSOR_ON 1263 | wrbyte t1, txt_cursor 1264 | jmp #_done 1265 | 1266 | _nrcs cmp ch, #"l" wz ' cursor key mode 1267 | if_z wrlong kb_nrcs_table_1, kb_nrcs_table_ptr 1268 | if_nz wrlong kb_nrcs_table_2, kb_nrcs_table_ptr 1269 | jmp #_done 1270 | 1271 | kb_nrcs_table_2 long 0 1272 | 1273 | _dev_status cmp args, #5 wz 1274 | if_nz jmp #_cursor_report 1275 | mov ch, #$1B 1276 | call #charOut 1277 | mov ch, #"[" 1278 | call #charOut 1279 | mov ch, #"0" 1280 | call #charOut 1281 | mov ch, #"n" 1282 | call #charOut 1283 | jmp #_done 1284 | 1285 | _cursor_report cmp args, #6 wz 1286 | if_nz jmp #_done 1287 | mov ch, #$1B 1288 | call #charOut 1289 | mov ch, #"[" 1290 | call #charOut 1291 | mov a, y 1292 | add a, #1 1293 | call #decOut 1294 | mov ch, #";" 1295 | call #charOut 1296 | mov a, x 1297 | add a, #1 1298 | call #decOut 1299 | mov ch, #"R" 1300 | call #charOut 1301 | jmp #_done 1302 | 1303 | charOut rdlong t2, tx_head 1304 | mov t1, tx_buffer 1305 | add t1, t2 1306 | wrbyte ch, t1 1307 | add t2, #1 1308 | and t2, #ser#BUFFER_MASK 1309 | wrlong t2, tx_head 1310 | charOut_ret ret 1311 | 1312 | decOut mov ch, #"0" 1313 | cmpsub a, #10 wc 1314 | if_c add ch, #1 1315 | if_c jmp #$-2 1316 | cmp ch, #"0" wz 1317 | if_nz call #charOut 1318 | mov ch, #"0" 1319 | add ch, a 1320 | call #charOut 1321 | decOut_ret ret 1322 | 1323 | long $0[($ - overlay_start) // 2] 1324 | _vt_end fit $1F0 1325 | 1326 | 1327 | CON 1328 | 1329 | #1 1330 | CTRL_A 1331 | CTRL_B 1332 | CTRL_C 1333 | CTRL_D 1334 | CTRL_E 1335 | CTRL_F 1336 | CTRL_G 1337 | CTRL_H 1338 | CTRL_I 1339 | CTRL_J 1340 | CTRL_K 1341 | CTRL_L 1342 | CTRL_M 1343 | CTRL_N 1344 | CTRL_O 1345 | CTRL_P 1346 | CTRL_Q 1347 | CTRL_R 1348 | CTRL_S 1349 | CTRL_T 1350 | CTRL_U 1351 | CTRL_V 1352 | CTRL_W 1353 | CTRL_X 1354 | CTRL_Y 1355 | CTRL_Z 1356 | ESC 1357 | 1358 | DAT 1359 | 1360 | ' National Code Replacement System maps 1361 | 1362 | nrcs byte $23, $40, $5B, $5C, $5D, $5E, $5F, $60, $7B, $7C, $7D, $7E 1363 | 1364 | nrcs_map_it byte $9C, $15, $F8, $87, $82, $5E, $5F, $97, $85, $95, $8A, $8D 1365 | 1366 | nrcs_map_uk byte $9C, $40, $5B, $5C, $5D, $5E, $5F, $60, $7B, $7C, $7D, $7E 1367 | 1368 | nrcs_map_fr byte $9C, $85, $F8, $87, $15, $5E, $5F, $60, $82, $97, $8A, $7E 1369 | 1370 | nrcs_map_de byte $23, $15, $8E, $99, $9A, $5E, $5F, $60, $84, $94, $81, $E1 1371 | 1372 | nrcs_map_no byte $23, $8E, $92, $E9, $8F, $9A, $5F, $84, $91, $E9, $86, $81 1373 | 1374 | ' Key repeat timings 1375 | 1376 | repeatDelay word 250, 500, 750, 1000 1377 | 1378 | repeatPeriod word 33, 37, 42, 46, 50, 54, 58, 63, 67, 75, 83, 92 1379 | word 100, 109, 116, 125, 133, 149, 167, 182, 200, 217, 232, 250 1380 | word 270, 303, 333, 370, 400, 435, 470, 500 1381 | 1382 | ' Default (cursor) mode keys table 1383 | 1384 | strTable word @strKeySpace 1385 | word @strKeyEscape 1386 | word @strKeyBackspace 1387 | word @strKeyTabulator 1388 | word @strKeyReturn 1389 | word @strKeyInsert 1390 | word @strKeyHome 1391 | word @strKeyPageUp 1392 | word @strKeyDelete 1393 | word @strKeyEnd 1394 | word @strKeyPageDown 1395 | word @strKeyUp 1396 | word @strKeyDown 1397 | word @strKeyLeft 1398 | word @strKeyRight 1399 | word @strKeyF1 1400 | word @strKeyF2 1401 | word @strKeyF3 1402 | word @strKeyF4 1403 | word @strKeyF5 1404 | word @strKeyF6 1405 | word @strKeyF7 1406 | word @strKeyF8 1407 | word @strKeyF9 1408 | word @strKeyF10 1409 | word @strKeyF11 1410 | word @strKeyF12 1411 | word @strKeyApplication 1412 | word @strKeyCapsLock 1413 | word @strKeyPrintScreen 1414 | word @strKeyScrollLock 1415 | word @strKeyPause 1416 | word @strKeyNumLock 1417 | word @strKeyKP_Divide 1418 | word @strKeyKP_Multiply 1419 | word @strKeyKP_Subtract 1420 | word @strKeyKP_Add 1421 | word @strKeyKP_Enter 1422 | word @strKeyKP_1 1423 | word @strKeyKP_2 1424 | word @strKeyKP_3 1425 | word @strKeyKP_4 1426 | word @strKeyKP_5 1427 | word @strKeyKP_6 1428 | word @strKeyKP_7 1429 | word @strKeyKP_8 1430 | word @strKeyKP_9 1431 | word @strKeyKP_0 1432 | word @strKeyKP_Center 1433 | word @strKeyKP_Comma 1434 | word @strKeyKP_Period 1435 | word @strKeyShiftLeft 1436 | word @strKeyShiftRight 1437 | 1438 | ' Application mode keys table 1439 | 1440 | strTableApp word @strKeySpace 1441 | word @strKeyEscape 1442 | word @strKeyBackspace 1443 | word @strKeyTabulator 1444 | word @strKeyReturn 1445 | word @strAppKeyInsert 1446 | word @strAppKeyHome 1447 | word @strAppKeyPageUp 1448 | word @strAppKeyDelete 1449 | word @strAppKeyEnd 1450 | word @strAppKeyPageDown 1451 | word @strAppKeyUp 1452 | word @strAppKeyDown 1453 | word @strAppKeyLeft 1454 | word @strAppKeyRight 1455 | word @strKeyF1 1456 | word @strKeyF2 1457 | word @strKeyF3 1458 | word @strKeyF4 1459 | word @strKeyF5 1460 | word @strKeyF6 1461 | word @strKeyF7 1462 | word @strKeyF8 1463 | word @strKeyF9 1464 | word @strKeyF10 1465 | word @strKeyF11 1466 | word @strKeyF12 1467 | word @strKeyApplication 1468 | word @strKeyCapsLock 1469 | word @strKeyPrintScreen 1470 | word @strKeyScrollLock 1471 | word @strKeyPause 1472 | word @strKeyNumLock 1473 | word @strKeyKP_Divide 1474 | word @strKeyKP_Multiply 1475 | word @strKeyKP_Subtract 1476 | word @strKeyKP_Add 1477 | word @strKeyKP_Enter 1478 | word @strKeyKP_1 1479 | word @strKeyKP_2 1480 | word @strKeyKP_3 1481 | word @strKeyKP_4 1482 | word @strKeyKP_5 1483 | word @strKeyKP_6 1484 | word @strKeyKP_7 1485 | word @strKeyKP_8 1486 | word @strKeyKP_9 1487 | word @strKeyKP_0 1488 | word @strKeyKP_Center 1489 | word @strKeyKP_Comma 1490 | word @strKeyKP_Period 1491 | word @strKeyShiftLeft 1492 | word @strKeyShiftRight 1493 | 1494 | ' WordStar mode keys table 1495 | 1496 | strTableWS word @strKeySpace 1497 | word @strKeyEscape 1498 | word @strKeyBackspace 1499 | word @strKeyTabulator 1500 | word @strKeyReturn 1501 | word @strWSKeyInsert 1502 | word @strWSKeyHome 1503 | word @strWSKeyPageUp 1504 | word @strWSKeyDelete 1505 | word @strWSKeyEnd 1506 | word @strWSKeyPageDown 1507 | word @strWSKeyUp 1508 | word @strWSKeyDown 1509 | word @strWSKeyLeft 1510 | word @strWSKeyRight 1511 | word @strKeyF1 1512 | word @strKeyF2 1513 | word @strKeyF3 1514 | word @strKeyF4 1515 | word @strKeyF5 1516 | word @strKeyF6 1517 | word @strKeyF7 1518 | word @strKeyF8 1519 | word @strKeyF9 1520 | word @strKeyF10 1521 | word @strKeyF11 1522 | word @strKeyF12 1523 | word @strKeyApplication 1524 | word @strKeyCapsLock 1525 | word @strKeyPrintScreen 1526 | word @strKeyScrollLock 1527 | word @strKeyPause 1528 | word @strKeyNumLock 1529 | word @strKeyKP_Divide 1530 | word @strKeyKP_Multiply 1531 | word @strKeyKP_Subtract 1532 | word @strKeyKP_Add 1533 | word @strKeyKP_Enter 1534 | word @strKeyKP_1 1535 | word @strKeyKP_2 1536 | word @strKeyKP_3 1537 | word @strKeyKP_4 1538 | word @strKeyKP_5 1539 | word @strKeyKP_6 1540 | word @strKeyKP_7 1541 | word @strKeyKP_8 1542 | word @strKeyKP_9 1543 | word @strKeyKP_0 1544 | word @strKeyKP_Center 1545 | word @strKeyKP_Comma 1546 | word @strKeyKP_Period 1547 | word @strKeyShiftLeft 1548 | word @strKeyShiftRight 1549 | 1550 | ' Common default (cursor) and application mode keys 1551 | 1552 | strKeySpace byte " ", 0 1553 | strKeyEscape byte $1B, 0 1554 | strKeyBackspace byte $08, 0 1555 | strKeyTabulator byte $09, 0 1556 | strKeyReturn byte $0D, 0 1557 | 1558 | strKeyF1 byte $1B, "OP", 0 1559 | strKeyF2 byte $1B, "OQ", 0 1560 | strKeyF3 byte $1B, "OR", 0 1561 | strKeyF4 byte $1B, "OS", 0 1562 | strKeyF5 byte $1B, "OT", 0 1563 | strKeyF6 byte $1B, "OU", 0 1564 | strKeyF7 byte $1B, "OV", 0 1565 | strKeyF8 byte $1B, "OW", 0 1566 | strKeyF9 byte $1B, "OX", 0 1567 | strKeyF10 byte $1B, "OY", 0 1568 | strKeyF11 byte 0 1569 | strKeyF12 byte 0 1570 | 1571 | strKeyApplication byte 0 1572 | strKeyCapsLock byte 0 1573 | strKeyPrintScreen byte 0 1574 | strKeyScrollLock byte 0 1575 | strKeyPause byte 0 1576 | strKeyNumLock byte 0 1577 | 1578 | strKeyKP_Divide byte "/", 0 1579 | strKeyKP_Multiply byte "*", 0 1580 | strKeyKP_Subtract byte "-", 0 1581 | strKeyKP_Add byte "+", 0 1582 | strKeyKP_Enter byte $0D, 0 1583 | strKeyKP_1 byte "1", 0 1584 | strKeyKP_2 byte "2", 0 1585 | strKeyKP_3 byte "3", 0 1586 | strKeyKP_4 byte "4", 0 1587 | strKeyKP_5 byte "5", 0 1588 | strKeyKP_6 byte "6", 0 1589 | strKeyKP_7 byte "7", 0 1590 | strKeyKP_8 byte "8", 0 1591 | strKeyKP_9 byte "9", 0 1592 | strKeyKP_0 byte "0", 0 1593 | strKeyKP_Center byte 0 1594 | strKeyKP_Comma byte ",", 0 1595 | strKeyKP_Period byte ".", 0 1596 | 1597 | ' Default (cursor) mode keys 1598 | 1599 | strKeyInsert byte 0 1600 | strKeyHome byte $1B, "[H", 0 1601 | strKeyPageUp byte 0 1602 | strKeyDelete byte $7F, 0 1603 | strKeyEnd byte $1B, "[K", 0 1604 | strKeyPageDown byte 0 1605 | strKeyUp byte $1B, "[A", 0 1606 | strKeyDown byte $1B, "[B", 0 1607 | strKeyLeft byte $1B, "[D", 0 1608 | strKeyRight byte $1B, "[C", 0 1609 | strKeyShiftLeft byte 0 1610 | strKeyShiftRight byte 0 1611 | 1612 | ' Application mode keys 1613 | 1614 | strAppKeyInsert byte 0 1615 | strAppKeyHome byte $1B, "OH", 0 1616 | strAppKeyPageUp byte 0 1617 | strAppKeyDelete byte $7F, 0 1618 | strAppKeyEnd byte $1B, "OK", 0 1619 | strAppKeyPageDown byte 0 1620 | strAppKeyUp byte $1B, "OA", 0 1621 | strAppKeyDown byte $1B, "OB", 0 1622 | strAppKeyLeft byte $1B, "OD", 0 1623 | strAppKeyRight byte $1B, "OC", 0 1624 | strAppKeyShiftLeft byte 0 1625 | strAppKeyShiftRight byte 0 1626 | 1627 | ' WordStar mode keys 1628 | 1629 | strWSKeyInsert byte CTRL_V, 0 1630 | strWSKeyHome byte CTRL_Q, "S", 0 1631 | strWSKeyPageUp byte CTRL_R, 0 1632 | strWSKeyDelete byte CTRL_G, 0 1633 | strWSKeyEnd byte CTRL_Q, "D", 0 1634 | strWSKeyPageDown byte CTRL_C, 0 1635 | strWSKeyUp byte CTRL_E, 0 1636 | strWSKeyDown byte CTRL_X, 0 1637 | strWSKeyLeft byte CTRL_S, 0 1638 | strWSKeyRight byte CTRL_D, 0 1639 | strWSKeyShiftLeft byte CTRL_A, 0 1640 | strWSKeyShiftRight byte CTRL_F, 0 1641 | 1642 | {{ 1643 | 1644 | TERMS OF USE: MIT License 1645 | 1646 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 1647 | associated documentation files (the "Software"), to deal in the Software without restriction, including 1648 | without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 1649 | copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the 1650 | following conditions: 1651 | 1652 | The above copyright notice and this permission notice shall be included in all copies or substantial 1653 | portions of the Software. 1654 | 1655 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT 1656 | LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 1657 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 1658 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 1659 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 1660 | 1661 | }} 1662 | -------------------------------------------------------------------------------- /waitvid.80x25.driver.spin: -------------------------------------------------------------------------------- 1 | '' 2 | '' VGA display 80x25 (dual cog) - video driver and pixel generator 3 | '' 4 | '' Author: Marko Lukat 5 | '' Last modified: 2015/06/15 6 | '' Version: 0.15 7 | '' 8 | '' long[par][0]: vgrp:mode:vpin:[!Z]:addr = 2:1:8:5:16 -> zero (accepted) screen buffer (4n) 9 | '' long[par][1]: [!Z]:addr = 16:16 -> zero (accepted) font descriptor (2n) 10 | '' long[par][2]: addr:addr = 16:16 -> zero (accepted) cursor location (4n) 11 | '' long[par][3]: frame indicator/sync lock 12 | '' 13 | '' - character entries are words, i.e. ASCII << 8 | attribute 14 | '' - top left corner is at highest screen memory address 15 | '' 16 | '' - cursor location: $00000000: both cursors off 17 | '' $BBBBAAAA: AAAA == BBBB, one cursor 18 | '' $DDDDCCCC: CCCC <> DDDD, two cursors 19 | '' 20 | '' - cursor format: %00000000_yyyyyyyy_xxxxxxxx_00000_mmm (see below for mode flags) 21 | '' 22 | '' acknowledgements 23 | '' - loader code based on work done by Phil Pilgrim (PhiPi) 24 | '' 25 | '' 20140912: initial version (720x400@70Hz timing, %10 sync locked) 26 | '' 20140914: dual cog timing sorted (colours are under our control) 27 | '' 20140915: pixel loader, use internal 128 entry palette (fixed) 28 | '' 20140916: enabled blink mode (0/1) 29 | '' 20140918: blink mode is now an init time parameter 30 | '' 20140919: cursor, WIP 31 | '' 20140920: cursor implementation complete 32 | '' 20140926: added cursor mask constant 33 | '' 20150615: full character range (9th column is always background) 34 | '' 20171027: modified colors table (Marco Maccaferri) 35 | '' 20171104: added conditional compile and timings for 640x400@70Hz (Marco Maccaferri) 36 | '' 20180507: modified cursor mask to act on pixels only (Marco Maccaferri) 37 | '' 38 | CON 39 | CURSOR_ON = %100 40 | CURSOR_OFF = %000 41 | CURSOR_ULINE = %010 42 | CURSOR_BLOCK = %000 43 | CURSOR_FLASH = %001 44 | CURSOR_SOLID = %000 45 | 46 | CURSOR_MASK = %111 47 | 48 | PUB null 49 | '' This is not a top level object. 50 | 51 | PUB init(ID, mailbox) 52 | 53 | long[mailbox][1] := (16 << 24) | @vga_font 54 | long[mailbox][3] := 0 55 | 56 | cognew(@driver, mailbox) 57 | cognew(@driver, mailbox | $8000) 58 | 59 | repeat 60 | until long[mailbox][3] == $0000FFFF ' OK (secondary/primary) 61 | 62 | long[mailbox][3] := 0 ' release sync lock 63 | 64 | DAT org 0 ' video driver 65 | 66 | driver jmpret $, #setup ' -4 once 67 | 68 | ' %00_11_11_10 69 | 70 | ' Palette entries holds two pairs of FG/BG colours (high word: blink colours, low word: normal colours). 71 | 72 | long $82020282, $22020222, $A20202A2, $0A02020A, $8A02028A, $2A02022A, $AA0202AA 73 | long $02828202, $82828282, $22828222, $A28282A2, $0A82820A, $8A82828A, $2A82822A, $AA8282AA 74 | long $02222202, $82222282, $22222222, $A22222A2, $0A22220A, $8A22228A, $2A22222A, $AA2222AA 75 | long $02A2A202, $82A2A282, $22A2A222, $A2A2A2A2, $0AA2A20A, $8AA2A28A, $2AA2A22A, $AAA2A2AA 76 | long $020A0A02, $820A0A82, $220A0A22, $A20A0AA2, $0A0A0A0A, $8A0A0A8A, $2A0A0A2A, $AA0A0AAA 77 | long $028A8A02, $828A8A82, $228A8A22, $A28A8AA2, $0A8A8A0A, $8A8A8A8A, $2A8A8A2A, $AA8A8AAA 78 | long $022A2A02, $822A2A82, $222A2A22, $A22A2AA2, $0A2A2A0A, $8A2A2A8A, $2A2A2A2A, $AA2A2AAA 79 | long $02AAAA02, $82AAAA82, $22AAAA22, $A2AAAAA2, $0AAAAA0A, $8AAAAA8A, $2AAAAA2A, $AAAAAAAA 80 | long $02565602, $82565682, $22565622, $A25656A2, $0A56560A, $8A56568A, $2A56562A, $AA5656AA 81 | long $02C2C202, $82C2C282, $22C2C222, $A2C2C2A2, $0AC2C20A, $8AC2C28A, $2AC2C22A, $AAC2C2AA 82 | long $02323202, $82323282, $22323222, $A23232A2, $0A32320A, $8A32328A, $2A32322A, $AA3232AA 83 | long $02F6F602, $82F6F682, $22F6F622, $A2F6F6A2, $0AF6F60A, $8AF6F68A, $2AF6F62A, $AAF6F6AA 84 | long $020E0E02, $820E0E82, $220E0E22, $A20E0EA2, $0A0E0E0A, $8A0E0E8A, $2A0E0E2A, $AA0E0EAA 85 | long $02CECE02, $82CECE82, $22CECE22, $A2CECEA2, $0ACECE0A, $8ACECE8A, $2ACECE2A, $AACECEAA 86 | long $023E3E02, $823E3E82, $223E3E22, $A23E3EA2, $0A3E3E0A, $8A3E3E8A, $2A3E3E2A, $AA3E3EAA 87 | long $02FEFE02, $82FEFE82, $22FEFE22, $A2FEFEA2, $0AFEFE0A, $8AFEFE8A, $2AFEFE2A, $AAFEFEAA 88 | 89 | ' horizontal timing 640(640) 1(16) 6(96) 3(48) 90 | ' vertical timing 400(400) 12(12) 2(2) 35(35) 91 | 92 | ' +---------------- front porch 93 | ' | +-------------- sync 94 | ' | | +--------- back porch 95 | ' | | | 96 | vsync 97 | mov ecnt, #12+2+(35-2) 98 | 99 | cmp ecnt, #35 wz 100 | if_ne cmp ecnt, #33 wz 101 | if_e xor sync, #$0101 ' in/active 102 | 103 | call #blank 104 | djnz ecnt, #vsync+1 105 | 106 | ' While still in sync, figure out the blink mode (used to be based on cnt) and cursor. 107 | ' hsync offers 31 hub windows. 108 | 109 | add fcnt, #1 ' next frame 110 | cmpsub fcnt, #36 wz ' N frames per phase (on/off) 111 | if_z rev rcnt, #{32}-0 ' toggle colours 112 | 113 | cmp locn, #0 wz ' check cursor availability 114 | mov crs0, #0 ' default is disabled 115 | if_ne rdlong crs0, locn ' override 116 | if_ne rol locn, #16 117 | mov crs1, #0 ' default is disabled 118 | if_ne rdlong crs1, locn ' override 119 | 120 | mov vier, crs0 ' | 121 | call #prep ' process cursor 0 122 | mov crs0, vier ' | 123 | 124 | mov vier, crs1 ' | 125 | call #prep ' process cursor 1 126 | mov crs1, vier ' | 127 | 128 | rdlong temp, scrn_ wz ' get screen address 129 | if_nz mov scrn, temp 130 | if_nz wrlong zero, scrn_ ' acknowledge screen buffer setup 131 | if_nz add scrn, $+1 ' scrn now points to last byte 132 | long 160*25 -1 133 | 134 | if_nc call #blank ' | 135 | if_nc call #blank ' back porch remainder (primary only) 136 | 137 | ' Vertical sync chain done, do visible area. 138 | 139 | mov zwei, scrn ' screen base address 140 | mov rows, #res_y/16 ' row count 141 | 142 | :scan mov scnt, #16/2/2 ' 16 double scanlines (split between primary and secondary) 143 | mov eins, font ' font base 144 | if_nc add eins, dst1 ' interleaved 145 | 146 | :line mov vscl, many ' two lines we don't use 147 | waitvid zero, #0 ' 317 hub windows 148 | 149 | call #load ' load pixels and colours for the next two lines 150 | 151 | call #chars ' | 152 | call #chars ' display scanlines 153 | 154 | add eins, dst2{512+512} ' skip 4 scanlines 155 | djnz scnt, #:line ' for all character scanlines 156 | sub zwei, #80*2 ' next row 157 | djnz rows, #:scan ' for all rows 158 | 159 | 160 | if_c call #blank ' secondary finishes early so 161 | if_c call #blank ' let him do some blank lines 162 | 163 | if_nc wrlong cnt, fcnt_ ' announce vertical blank (primary) 164 | 165 | jmp #vsync ' next frame 166 | 167 | 168 | blank mov vscl, line ' 256/640 169 | waitvid sync, #%0000 ' latch blank line 170 | call #hsync 171 | blank_ret ret 172 | 173 | 174 | chars movd :one, #pix+0 ' | 175 | movd :two, #col+0 ' restore initial settings 176 | movs :two, #pix+0 ' | 177 | 178 | mov vscl, hvis ' 1/8, speed up (one pixel per frame clock) 179 | mov ecnt, #80 ' character count 180 | 181 | :one ror 1-1, #8 ' $0000AABB -> $BB0000AA -> $000000BB 182 | add $-1, dst1 ' advance 183 | add $+1, d1s1 ' advance (pipeline) 184 | :two waitvid 0-0, 1-1 ' emit pixels (9th column is background) 185 | djnz ecnt, #$-4 186 | 187 | xor :one, swap ' ror #8 vs shr #24 188 | 189 | ' Horizontal sync embedded here due to timing constraints, only 18 clocks are allowed between waitvids. 190 | 191 | hsync mov vscl, wrap ' | 192 | waitvid sync, #%0001111110 ' horizontal sync pulse (1/6/3 reverse) 193 | mov cnt, cnt ' record sync point 194 | hsync_ret 195 | chars_ret ret 196 | 197 | 198 | load muxnc flag, $ ' preserve carry flag 199 | 200 | movd :ld_0, #pix+0 ' | 201 | movd :ld_1, #pix+1 ' | 202 | movd :ld_2, #pix+2 ' re/store initial settings 203 | movd :ld_3, #pix+3 ' | 204 | 205 | movd :wr_0, #col+0 ' | 206 | movd :wr_1, #col+1 ' | 207 | movd :wr_2, #col+2 ' same for colours 208 | movd :wr_3, #col+3 ' | 209 | 210 | mov addr, zwei ' current screen base 211 | movi addr, #80{units} -4 ' add magic marker 212 | 213 | ' Fetch pixel data and colour for four characters. Only character 4n is documented. 214 | 215 | :loop rdword frqb, addr ' +0 = {p.0.0} read ASCII + colour 216 | ror frqb, #8 ' +8 {p.0.1} select ASCII for indexed read 217 | mov phsb, eins ' -4 {p.0.2} current font address 218 | :ld_0 rdword 0-0, phsb ' +0 = {p.0.3} two scanlines worth of character data 219 | add $-1, dst4 ' +8 {p.0.4} advance dst 220 | 221 | test frqb, bt24 wz ' -4 {c.0.0} check blink mode 222 | shr frqb, #25 ' +0 = {c.0.1} palette index 223 | movs :rd_0, frqb ' +4 {c.0.2} prepare palette-to-temp 224 | 225 | sub addr, #3 ' +8 {p.0.5} advance src 226 | :rd_0 mov col0, 0-0 ' -4 {c.0.3} read palette entry 227 | 228 | 229 | rdword frqb, addr ' +0 = {p.1.0} 230 | ror frqb, #8 ' +8 {p.1.1} 231 | mov phsb, eins ' -4 {p.1.2} 232 | :ld_1 rdword 1-1, phsb ' +0 = {p.1.3} 233 | add $-1, dst4 ' +8 {p.1.4} 234 | sub addr, #1 ' -4 {p.1.5} 235 | 236 | if_nz rol col0, rcnt ' +0 = {c.0.4} optionally select alternate colour 237 | 238 | test frqb, bt24 wz ' +4 {c.1.0} 239 | shr frqb, #25 ' +8 {c.1.1} 240 | movs :rd_1, frqb ' -4 {c.1.2} 241 | 242 | 243 | rdword frqb, addr ' +0 = {p.2.0} 244 | 245 | :wr_0 mov 0-0, col0 ' +4 {c.0.5} transfer temp to colour array 246 | add $-1, dst4 ' +8 {c.0.6} advance destination 247 | :rd_1 mov col1, 1-1 ' -4 {c.1.3} 248 | if_nz rol col1, rcnt ' +0 = {c.1.4} 249 | 250 | ror frqb, #8 ' +8 {p.2.1} 251 | mov phsb, eins ' -4 {p.2.2} 252 | :ld_2 rdword 2-2, phsb ' +0 = {p.2.3} 253 | add $-1, dst4 ' +8 {p.2.4} 254 | 255 | test frqb, bt24 wz ' -4 {c.2.0} 256 | shr frqb, #25 ' +0 = {c.2.1} 257 | movs :rd_2, frqb ' +4 {c.2.2} 258 | 259 | sub addr, i4s3 wc ' +8 {p.2.5} 260 | :rd_2 mov col2, 2-2 ' -4 {c.2.3} 261 | 262 | 263 | rdword frqb, addr ' +0 = {p.3.0} 264 | ror frqb, #8 ' +8 {p.3.1} 265 | mov phsb, eins ' -4 {p.3.2} 266 | :ld_3 rdword 3-3, phsb ' +0 = {p.3.3} 267 | add $-1, dst4 ' +8 {p.3.4} 268 | 269 | :wr_1 mov 1-1, col1 ' -4 {c.1.5} 270 | add $-1, dst4 ' +0 = {c.1.6} 271 | if_nz rol col2, rcnt ' +4 {c.2.4} 272 | 273 | test frqb, bt24 wz ' +8 {c.3.0} 274 | shr frqb, #25 ' -4 {c.3.1} 275 | movs :rd_3, frqb ' +0 = {c.3.2} 276 | 277 | :wr_2 mov 2-2, col2 ' +4 {c.2.5} 278 | add $-1, dst4 ' +8 {c.2.6} 279 | 280 | :rd_3 mov col3, 3-3 ' -4 {c.3.3} 281 | if_nz rol col3, rcnt ' +0 = {c.3.4} 282 | :wr_3 mov 3-3, col3 ' +4 {c.3.5} 283 | add $-1, dst4 ' +8 {c.3.6} 284 | 285 | if_nc djnz addr, #:loop ' -4 {p.3.5} for all characters 286 | 287 | mov vier, crs0 288 | call #cursor 289 | 290 | cmp crs0, crs1 wz 291 | if_ne mov vier, crs1 292 | if_ne call #cursor 293 | 294 | load_ret jmpret flag, #0-0 nr,wc ' restore carry flag 295 | 296 | 297 | cursor test vier, #%100 wz ' cursor enabled? 298 | 299 | mov temp, vier ' local copy 300 | sar temp, #1+16 ' extract y - 25 301 | if_z add temp, rows wz ' rows = {25..1} 302 | if_nz jmp cursor_ret ' wrong row/disabled 303 | 304 | test vier, #%010 wz,wc ' underscore(1)/block(0) 305 | if_nz cmp scnt, #1 wz 306 | if_nz jmp cursor_ret ' wrong scanline pair 307 | 308 | ror vier, #8 wc ' carry: blink on/off 309 | movd :set, vier 310 | if_c cmp fcnt, #18 wc 311 | :set if_nc xor 0-0, cmsk 312 | 313 | cursor_ret ret 314 | 315 | 316 | prep mov temp, vier ' working copy 317 | shr temp, #16 ' | 318 | and temp, #255 ' extract y 319 | sub temp, #25 ' y - 25 320 | shl temp, #1+16 ' 2(y - 25) 321 | 322 | and vier, xmsk ' get rid of y 323 | max vier, xlim ' limit x to park position (auto off) 324 | xor vier, #%100 ' invert on/off 325 | or vier, temp ' reinsert y 326 | 327 | ror vier, #8 ' align x for add 328 | add vier, #pix 329 | rol vier, #8 ' restore cursor descriptor 330 | 331 | prep_ret ret 332 | 333 | ' initialised data and/or presets 334 | 335 | xmsk long $0000FF07 ' covers mode/x 336 | xlim long 80 << 8 ' park position 337 | 338 | rcnt long 8 ' palette bit rotation count (16/8/0) 339 | fcnt long 0 ' blink frame count 340 | bt24 long |< 24 ' blink indicator 341 | 342 | flag long 0 ' loader flag storage 343 | swap long %000010 << 26 | 16 ' ror #8 vs shr #24 344 | sync long hv_idle ^ $0200 345 | 346 | wrap long 16 << 12 | 160 ' 16/160 347 | hvis long 1 << 12 | 8 ' 1/8 348 | line long 0 << 12 | 640 ' 256/640 349 | many long 0 << 12 | 1600 ' 256/1600 350 | 351 | scrn_ long $00000000 -12 ' | 352 | font_ long $00000004 -12 ' | 353 | locn_ long $00000008 -12 ' | 354 | fcnt_ long $0000000C -12 ' mailbox addresses (local copy) (##) 355 | 356 | dst1 long 1 << 9 ' dst +/-= 1 357 | dst2 long 2 << 9 ' dst +/-= 2 358 | dst4 long 4 << 9 ' dst +/-= 4 359 | d1s1 long 1 << 9 | 1 ' dst/src +/-= 1 360 | i4s3 long 4 << 23 | 3 361 | 362 | cmsk long %%3333_3333 ' xor mask for cursor 363 | 364 | ' Stuff below is re-purposed for temporary storage. 365 | 366 | setup add trap, par wc ' carry set -> secondary 367 | and trap, hram ' confine to hub RAM 368 | 369 | add scrn_, trap ' @long[par][0] 370 | add font_, trap ' @long[par][1] 371 | add locn_, trap ' @long[par][2] 372 | add fcnt_, trap ' @long[par][3] 373 | 374 | addx trap, #%00 ' add secondary offset 375 | wrbyte hram, trap ' up and running 376 | 377 | rdlong temp, trap wz ' | (%%) 378 | if_nz jmp #$-1 ' synchronized start 379 | 380 | ' primary: cnt + 0 381 | ' secondary: cnt + 2 382 | 383 | rdlong scrn, scrn_ ' get screen address (4n) (%%) 384 | wrlong zero, scrn_ ' acknowledge screen buffer setup 385 | 386 | rdlong font, font_ ' get font definition (2n) (%%) 387 | wrlong zero, font_ ' acknowledge font definition setup 388 | 389 | rdlong locn, locn_ wz ' get cursor location (%%) 390 | if_nz wrlong zero, locn_ ' acknowledge cursor location 391 | 392 | ' Perform pending setup. 393 | 394 | add scrn, $+1 ' scrn now points to last byte 395 | long 160*25 -1 396 | 397 | ' Upset video h/w and relatives. 398 | 399 | rdlong temp, #0 ' clkfreq 400 | shr temp, #10 ' ~1ms 401 | if_nc waitpne $, #0 ' adjust primary 402 | 403 | ' primary: cnt + 0 + 6 404 | ' secondary: cnt + 2 + 4 405 | 406 | add temp, cnt 407 | 408 | movi ctrb, #%0_11111_000 ' LOGIC always (loader support) 409 | movi ctra, #%0_00001_101 ' PLL, VCO/4 410 | mov frqa, frqx ' 25.175MHz 411 | 412 | mov vscl, #1 ' reload as fast as possible 413 | mov zwei, scrn ' vgrp:mode:vpin:[!Z]:scrn = 2:1:8:5:16 (%%) 414 | shr zwei, #5+16 ' | 415 | or zwei, #%%000_3 ' | 416 | mov vcfg, zwei ' set vgrp and vpin 417 | movi vcfg, #%0_01_0_00_000 ' VGA, 2 colour mode 418 | 419 | test zwei, #%1_00000000 wz ' | 420 | if_nz xor rcnt, #16|8 ' blink mode setup (default is 8) 421 | 422 | waitcnt temp, #0 ' PLL settled, frame counter flushed 423 | 424 | ror vcfg, #1 ' freeze video h/w 425 | mov vscl, line ' transfer user value 426 | rol vcfg, #1 ' unfreeze 427 | waitpne $, #0 ' get some distance 428 | waitvid zero, #0 ' latch user value 429 | 430 | and mask, vcfg ' transfer vpin 431 | mov temp, vcfg ' | 432 | shr temp, #9 ' extract vgrp 433 | shl temp, #3 ' 0..3 >> 0..24 434 | shl mask, temp ' finalise mask 435 | 436 | max dira, mask ' drive outputs 437 | mov $000, pal0 ' restore colour entry 0 438 | jmp #vsync ' return 439 | 440 | ' Local data, used only once. 441 | 442 | pal0 long dcolour|hv_idle ' first palette entry 443 | frqx long $1423D70A ' 25.175MHz 444 | mask long %11111111 445 | 446 | hram long $00007FFF ' hub RAM mask 447 | trap long $FFFF8000 +12 ' primary/secondary trap (##) 448 | 449 | EOD{ata} fit 450 | 451 | ' uninitialised data and/or temporaries 452 | 453 | org setup 454 | 455 | scrn res 1 ' screen buffer < setup +10 (%%) 456 | font res 1 ' font definition < setup +12 (%%) 457 | locn res 1 ' cursor location < setup +14 (%%) 458 | ecnt res 1 ' element count 459 | scnt res 1 ' scanlines (per char) 460 | 461 | temp res alias ' < setup + 8 (%%) 462 | addr res 1 ' current screen base 463 | rows res 1 ' display row count 464 | crs0 res 1 ' cursor 0 location and mode 465 | crs1 res 1 ' cursor 1 location and mode 466 | 467 | eins res 1 468 | zwei res 1 ' < setup < 26 (%%) 469 | vier res 1 470 | 471 | col2 res alias 472 | col0 res 1 473 | col3 res alias 474 | col1 res 1 475 | 476 | pix res 80 +1 ' emitter pixel array | 477 | col res 80 +1 ' emitter colour data | + park position 478 | 479 | tail fit 480 | 481 | CON 482 | zero = $1F0 ' par (dst only) 483 | hv_idle = $01010101 * %10 {%hv} ' h/v sync inactive 484 | dcolour = %%0000_0000_0000_0000 ' default colour 485 | 486 | res_x = 640 ' | 487 | res_y = 400 ' | 488 | res_m = 4 ' UI support 489 | 490 | alias = 0 491 | 492 | DAT 493 | 494 | vga_font 495 | word $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $FFFF, $0000, $FFFF, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000 496 | word $0000, $0000, $0000, $0000, $0018, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000 497 | word $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0008, $0000 498 | word $000C, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000 499 | word $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $001C, $0018, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000 500 | word $0000, $0000, $0000, $0000, $0000, $006E, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $2288, $55AA, $EEBB, $1818, $1818, $1818, $6C6C, $0000, $0000, $6C6C, $6C6C, $0000, $6C6C, $6C6C, $1818, $0000 501 | word $1818, $1818, $0000, $1818, $0000, $1818, $1818, $6C6C, $6C6C, $0000, $6C6C, $0000, $6C6C, $0000, $6C6C, $1818, $6C6C, $0000, $0000, $6C6C, $1818, $0000, $0000, $6C6C, $1818, $1818, $0000, $FFFF, $0000, $0F0F, $F0F0, $FFFF 502 | word $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $1818, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000 503 | 504 | word $0000, $007E, $007E, $0000, $0000, $0018, $0018, $0000, $FFFF, $0000, $FFFF, $0078, $003C, $00FC, $00FE, $0018, $0001, $0040, $0018, $0066, $00FE, $3E63, $0000, $0018, $0018, $0018, $0000, $0000, $0000, $0000, $0000, $0000 505 | word $0000, $0018, $6666, $0036, $183E, $0000, $001C, $0C0C, $0030, $000C, $0000, $0000, $0000, $0000, $0000, $0040, $003E, $0018, $003E, $003E, $0030, $007F, $001C, $007F, $003E, $003E, $0000, $0000, $0060, $0000, $0006, $003E 506 | word $003E, $0008, $003F, $003C, $001F, $007F, $007F, $003C, $0063, $003C, $0078, $0067, $000F, $0063, $0063, $001C, $003F, $003E, $003F, $003E, $007E, $0063, $0063, $0063, $0063, $0066, $007F, $003C, $0001, $003C, $1C36, $0000 507 | word $0C18, $0000, $0007, $0000, $0038, $0000, $001C, $0000, $0007, $0018, $0060, $0007, $001C, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0008, $0000, $0000, $0000, $0000, $0000, $0000, $0070, $0018, $000E, $006E, $0000 508 | word $003C, $0033, $3018, $081C, $0033, $060C, $1C36, $0000, $081C, $0033, $060C, $0066, $183C, $060C, $6363, $361C, $0C06, $0000, $007C, $081C, $0063, $060C, $0C1E, $060C, $0063, $6363, $6363, $1818, $1C36, $0066, $1F33, $70D8 509 | word $180C, $3018, $180C, $180C, $006E, $3B00, $3C36, $1C36, $000C, $0000, $0000, $0303, $0303, $0018, $0000, $0000, $2288, $55AA, $EEBB, $1818, $1818, $1818, $6C6C, $0000, $0000, $6C6C, $6C6C, $0000, $6C6C, $6C6C, $1818, $0000 510 | word $1818, $1818, $0000, $1818, $0000, $1818, $1818, $6C6C, $6C6C, $0000, $6C6C, $0000, $6C6C, $0000, $6C6C, $1818, $6C6C, $0000, $0000, $6C6C, $1818, $0000, $0000, $6C6C, $1818, $1818, $0000, $FFFF, $0000, $0F0F, $F0F0, $FFFF 511 | word $0000, $0000, $007F, $0000, $007F, $0000, $0000, $0000, $007E, $001C, $001C, $0078, $0000, $00C0, $0038, $0000, $0000, $0000, $000C, $0030, $0070, $1818, $0000, $0000, $1C36, $0000, $0000, $F030, $1B36, $0E1B, $0000, $0000 512 | 513 | word $0000, $81A5, $FFDB, $367F, $081C, $3C3C, $3C7E, $0000, $FFFF, $003C, $FFC3, $7058, $6666, $CCFC, $C6FE, $18DB, $0307, $6070, $3C7E, $6666, $DBDB, $061C, $0000, $3C7E, $3C7E, $1818, $0018, $000C, $0000, $0014, $081C, $7F7F 514 | word $0000, $3C3C, $6624, $367F, $6343, $0043, $3636, $0C06, $180C, $1830, $0066, $0018, $0000, $0000, $0000, $6030, $6373, $1C1E, $6360, $6360, $383C, $0303, $0603, $6360, $6363, $6363, $1818, $1818, $3018, $0000, $0C18, $6363 515 | word $6363, $1C36, $6666, $6643, $3666, $6646, $6646, $6643, $6363, $1818, $3030, $6636, $0606, $777F, $676F, $3663, $6666, $6363, $6666, $6363, $7E5A, $6363, $6363, $6363, $6336, $6666, $6331, $0C0C, $0307, $3030, $6300, $0000 516 | word $0000, $0000, $0606, $0000, $3030, $0000, $3626, $0000, $0606, $1800, $6000, $0606, $1818, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0C0C, $0000, $0000, $0000, $0000, $0000, $0000, $1818, $1818, $1818, $3B00, $0008 517 | word $6643, $3300, $0C00, $3600, $3300, $1800, $1C00, $003C, $3600, $3300, $1800, $6600, $6600, $1800, $081C, $001C, $007F, $0033, $3633, $3600, $6300, $1800, $3300, $1800, $6300, $1C36, $0063, $3C66, $2606, $663C, $331F, $1818 518 | word $0600, $0C00, $0600, $0600, $3B00, $6367, $367C, $361C, $0C00, $0000, $0000, $6333, $6333, $1800, $006C, $001B, $2288, $55AA, $EEBB, $1818, $1818, $1818, $6C6C, $0000, $0000, $6C6C, $6C6C, $0000, $6C6C, $6C6C, $1818, $0000 519 | word $1818, $1818, $0000, $1818, $0000, $1818, $1818, $6C6C, $6C6C, $0000, $6C6C, $0000, $6C6C, $0000, $6C6C, $1818, $6C6C, $0000, $0000, $6C6C, $1818, $0000, $0000, $6C6C, $1818, $1818, $0000, $FFFF, $0000, $0F0F, $F0F0, $FFFF 520 | word $0000, $003E, $6363, $007F, $6306, $0000, $0066, $006E, $183C, $3663, $3663, $0C18, $0000, $607E, $0C06, $3E63, $7F00, $1818, $1830, $180C, $D8D8, $1818, $1818, $006E, $361C, $0000, $0000, $3030, $3636, $0C06, $003E, $0000 521 | 522 | word $0000, $8181, $FFFF, $7F7F, $3E7F, $E7E7, $FFFF, $183C, $E7C3, $6642, $99BD, $4C1E, $663C, $0C0C, $C6C6, $3CE7, $1F7F, $7C7F, $1818, $6666, $DBDE, $3663, $0000, $1818, $1818, $1818, $307F, $067F, $0303, $367F, $1C3E, $3E3E 523 | word $0000, $3C18, $0000, $3636, $033E, $6330, $1C6E, $0000, $0C0C, $3030, $3CFF, $187E, $0000, $007F, $0000, $180C, $7B6F, $1818, $3018, $603C, $3633, $033F, $033F, $3018, $633E, $637E, $0000, $0000, $0C06, $7E00, $3060, $3018 524 | word $7B7B, $6363, $663E, $0303, $6666, $161E, $161E, $0303, $637F, $1818, $3030, $361E, $0606, $7F6B, $7F7B, $6363, $663E, $6363, $663E, $061C, $1818, $6363, $6363, $636B, $1C1C, $663C, $180C, $0C0C, $0E1C, $3030, $0000, $0000 525 | word $0000, $1E30, $1E36, $3E63, $3C36, $3E63, $060F, $6E33, $366E, $1C18, $7060, $6636, $1818, $377F, $3B66, $3E63, $3B66, $6E33, $3B6E, $3E63, $3F0C, $3333, $6666, $6363, $6336, $6363, $7F33, $180E, $1800, $1870, $0000, $1C36 526 | word $0303, $3333, $3E63, $1E30, $1E30, $1E30, $1E30, $6606, $3E63, $3E63, $3E63, $1C18, $1C18, $1C18, $3663, $3663, $6606, $6E6C, $337F, $3E63, $3E63, $3E63, $3333, $3333, $6363, $6363, $6363, $0606, $0F06, $187E, $2333, $187E 527 | word $1E30, $1C18, $3E63, $3333, $3B66, $6F7F, $007E, $003E, $0C0C, $007F, $007F, $1B0C, $1B0C, $1818, $361B, $366C, $2288, $55AA, $EEBB, $1818, $1818, $1F18, $6C6C, $0000, $1F18, $6F60, $6C6C, $7F60, $6F60, $6C6C, $1F18, $0000 528 | word $1818, $1818, $0000, $1818, $0000, $1818, $F818, $6C6C, $EC0C, $FC0C, $EF00, $FF00, $EC0C, $FF00, $EF00, $FF00, $6C6C, $FF00, $0000, $6C6C, $F818, $F818, $0000, $6C6C, $FF18, $1818, $0000, $FFFF, $0000, $0F0F, $F0F0, $FFFF 529 | word $6E3B, $633F, $0303, $3636, $0C18, $7E1B, $6666, $3B18, $6666, $637F, $6363, $307C, $7EDB, $DBDB, $063E, $6363, $007F, $7E18, $6030, $060C, $1818, $1818, $007E, $3B00, $0000, $0018, $0000, $3030, $3636, $131F, $3E3E, $0000 530 | 531 | word $0000, $BD99, $C3E7, $7F3E, $3E1C, $E718, $7E18, $3C18, $C3E7, $4266, $BD99, $3333, $187E, $0C0E, $C6E6, $3CDB, $1F07, $7C70, $187E, $6600, $D8D8, $6336, $007F, $187E, $1818, $187E, $3018, $060C, $037F, $3614, $3E7F, $1C1C 532 | word $0000, $1800, $0000, $367F, $6061, $180C, $3B33, $0000, $0C0C, $3030, $3C66, $1818, $0018, $0000, $0000, $0603, $6763, $1818, $0C06, $6060, $7F30, $6060, $6363, $0C0C, $6363, $6060, $0018, $0018, $0C18, $007E, $3018, $1800 533 | word $7B3B, $7F63, $6666, $0343, $6666, $1646, $1606, $7B63, $6363, $1818, $3033, $3636, $0646, $6363, $7363, $6363, $0606, $6B7B, $3666, $3063, $1818, $6363, $6336, $6B7F, $1C36, $1818, $0643, $0C0C, $3870, $3030, $0000, $0000 534 | word $0000, $3E33, $6666, $0303, $3333, $7F03, $0606, $3333, $6666, $1818, $6060, $1E36, $1818, $6B6B, $6666, $6363, $6666, $3333, $6606, $0E38, $0C0C, $3333, $6666, $6B6B, $1C1C, $6363, $180C, $1818, $1818, $1818, $0000, $6363 535 | word $4366, $3333, $7F03, $3E33, $3E33, $3E33, $3E33, $663C, $7F03, $7F03, $7F03, $1818, $1818, $1818, $637F, $637F, $3E06, $7E1B, $3333, $6363, $6363, $6363, $3333, $3333, $6363, $6363, $6363, $663C, $0606, $187E, $7B33, $1818 536 | word $3E33, $1818, $6363, $3333, $6666, $7B73, $0000, $0000, $0663, $0303, $6060, $063B, $6673, $3C3C, $366C, $361B, $2288, $55AA, $EEBB, $1818, $1F18, $1F18, $6F6C, $7F6C, $1F18, $6F6C, $6C6C, $6F6C, $7F00, $7F00, $1F00, $1F18 537 | word $F800, $FF00, $FF18, $F818, $FF00, $FF18, $F818, $EC6C, $FC00, $EC6C, $FF00, $EF6C, $EC6C, $FF00, $EF6C, $FF00, $FF00, $FF18, $FF6C, $FC00, $F800, $F818, $FC6C, $FF6C, $FF18, $1F00, $F818, $FFFF, $FFFF, $0F0F, $F0F0, $0000 538 | word $1B1B, $6363, $0303, $3636, $0C06, $1B1B, $663E, $1818, $663C, $6363, $3636, $6666, $DB7E, $CF7E, $0606, $6363, $0000, $1800, $180C, $1830, $1818, $181B, $0018, $6E3B, $0000, $1800, $1800, $3736, $0000, $0000, $3E3E, $0000 539 | 540 | word $0000, $817E, $FF7E, $1C08, $0800, $183C, $183C, $0000, $FFFF, $3C00, $C3FF, $331E, $1818, $0F07, $E767, $1818, $0301, $6040, $3C18, $6666, $D8D8, $1C30, $7F7F, $3C18, $1818, $3C18, $0000, $0000, $0000, $0000, $7F00, $0800 541 | word $0000, $1818, $0000, $3636, $633E, $6663, $336E, $0000, $1830, $180C, $0000, $0000, $1818, $0000, $1818, $0100, $633E, $187E, $637F, $633E, $3078, $633E, $633E, $0C0C, $633E, $301E, $1800, $180C, $3060, $0000, $0C06, $1818 542 | word $033E, $6363, $663F, $663C, $361F, $667F, $060F, $665C, $6363, $183C, $331E, $6667, $667F, $6363, $6363, $361C, $060F, $3E30, $6667, $633E, $183C, $633E, $1C08, $3E36, $6363, $183C, $637F, $0C3C, $6040, $303C, $0000, $0000 543 | word $0000, $336E, $663E, $633E, $336E, $633E, $060F, $3E30, $6667, $183C, $6066, $6667, $183C, $6B63, $6666, $633E, $3E06, $3E30, $060F, $633E, $6C38, $336E, $3C18, $7F36, $3663, $7E60, $667F, $1870, $1818, $180E, $0000, $7F00 544 | word $3C30, $336E, $633E, $336E, $336E, $336E, $336E, $3060, $633E, $633E, $633E, $183C, $183C, $183C, $6363, $6363, $667F, $1B76, $3373, $633E, $633E, $633E, $336E, $336E, $7E60, $361C, $633E, $1818, $673F, $1818, $3363, $1818 545 | word $336E, $183C, $633E, $336E, $6666, $6363, $0000, $0000, $633E, $0300, $6000, $6130, $797C, $3C18, $0000, $0000, $2288, $55AA, $EEBB, $1818, $1818, $1818, $6C6C, $6C6C, $1818, $6C6C, $6C6C, $6C6C, $0000, $0000, $0000, $1818 546 | word $0000, $0000, $1818, $1818, $0000, $1818, $1818, $6C6C, $0000, $6C6C, $0000, $6C6C, $6C6C, $0000, $6C6C, $0000, $0000, $1818, $6C6C, $0000, $0000, $1818, $6C6C, $6C6C, $1818, $0000, $1818, $FFFF, $FFFF, $0F0F, $F0F0, $0000 547 | word $3B6E, $3F03, $0303, $3636, $637F, $1B0E, $0606, $1818, $187E, $361C, $3677, $663C, $0000, $0603, $0C38, $6363, $7F00, $00FF, $007E, $007E, $1818, $1B0E, $1800, $0000, $0000, $0000, $0000, $3C38, $0000, $0000, $3E00, $0000 548 | 549 | word $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $FFFF, $0000, $FFFF, $0000, $0000, $0000, $0300, $0000, $0000, $0000, $0000, $0000, $0000, $633E, $0000, $7E00, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000 550 | word $0000, $0000, $0000, $0000, $1818, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0C00, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000 551 | word $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $7000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $00FF 552 | word $0000, $0000, $0000, $0000, $0000, $0000, $0000, $331E, $0000, $0000, $663C, $0000, $0000, $0000, $0000, $0000, $060F, $3078, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $301F, $0000, $0000, $0000, $0000, $0000, $0000 553 | word $603E, $0000, $0000, $0000, $0000, $0000, $0000, $3C00, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $301E, $0000, $0000, $0000, $0000, $0000, $0000, $1B0E 554 | word $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $187C, $6060, $0000, $0000, $0000, $2288, $55AA, $EEBB, $1818, $1818, $1818, $6C6C, $6C6C, $1818, $6C6C, $6C6C, $6C6C, $0000, $0000, $0000, $1818 555 | word $0000, $0000, $1818, $1818, $0000, $1818, $1818, $6C6C, $0000, $6C6C, $0000, $6C6C, $6C6C, $0000, $6C6C, $0000, $0000, $1818, $6C6C, $0000, $0000, $1818, $6C6C, $6C6C, $1818, $0000, $1818, $FFFF, $FFFF, $0F0F, $F0F0, $0000 556 | word $0000, $0302, $0000, $0000, $0000, $0000, $0300, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $1818, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000 557 | 558 | word $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $FFFF, $0000, $FFFF, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000 559 | word $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000 560 | word $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000 561 | word $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000 562 | word $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000 563 | word $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $2288, $55AA, $EEBB, $1818, $1818, $1818, $6C6C, $6C6C, $1818, $6C6C, $6C6C, $6C6C, $0000, $0000, $0000, $1818 564 | word $0000, $0000, $1818, $1818, $0000, $1818, $1818, $6C6C, $0000, $6C6C, $0000, $6C6C, $6C6C, $0000, $6C6C, $0000, $0000, $1818, $6C6C, $0000, $0000, $1818, $6C6C, $6C6C, $1818, $0000, $1818, $FFFF, $FFFF, $0F0F, $F0F0, $0000 565 | word $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $1818, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000 566 | -------------------------------------------------------------------------------- /waitvid.80x25.nine.driver.spin: -------------------------------------------------------------------------------- 1 | '' 2 | '' VGA display 80x25 (dual cog) - video driver and pixel generator 3 | '' 4 | '' Author: Marko Lukat 5 | '' Last modified: 2018/12/10 6 | '' Version: 0.15.nine.7 7 | '' 8 | '' long[par][0]: vgrp:[!Z]:vpin:[!Z]:addr = 2:1:8:5:16 -> zero (accepted) screen buffer (4n) 9 | '' long[par][1]: addr:addr = 16:16 -> zero (accepted) palette/font (2n/4n) 10 | '' long[par][2]: addr:addr = 16:16 -> zero (accepted) cursor locations (4n) 11 | '' long[par][3]: frame indicator/sync lock 12 | '' 13 | '' - character entries are words, i.e. ASCII << 8 | attribute 14 | '' - top left corner is at highest screen memory address 15 | '' 16 | '' - cursor location: $00000000: both cursors off 17 | '' $BBBBAAAA: AAAA == BBBB, one cursor 18 | '' $DDDDCCCC: CCCC <> DDDD, two cursors 19 | '' 20 | '' - cursor format: %00000000_yyyyyyyy_xxxxxxxx_00000_mmm (see below for mode flags) 21 | '' 22 | '' acknowledgements 23 | '' - loader code based on work done by Phil Pilgrim (PhiPi) 24 | '' 25 | '' 20140912: initial version (720x400@70Hz timing, %10 sync locked) 26 | '' 20140914: dual cog timing sorted (colours are under our control) 27 | '' 20140915: pixel loader, use internal 128 entry palette (fixed) 28 | '' 20140916: enabled blink mode (0/1) 29 | '' 20140918: blink mode is now an init time parameter 30 | '' 20140919: cursor, WIP 31 | '' 20140920: cursor implementation complete 32 | '' 20140926: added cursor mask constant 33 | '' 20150615: full character range (9th column is always background) 34 | '' 35 | '' 20181124: dropped character blink mode, now uses 256 entry (hub) palette 36 | '' 20181127: full 9x16 support 37 | '' 20181129: clean palette before use 38 | '' 20181206: re-introduced blink attribute 39 | '' 40 | '' 20181210: added palette data (M. Maccaferri) 41 | '' 20181210: allow screen buffer switch (M. Maccaferri) 42 | '' 20181210: modified cursor code to modify pixels only (M. Maccaferri) 43 | '' 44 | CON 45 | CURSOR_ON = %100 46 | CURSOR_OFF = %000 47 | CURSOR_ULINE = %010 48 | CURSOR_BLOCK = %000 49 | CURSOR_FLASH = %001 50 | CURSOR_SOLID = %000 51 | 52 | CURSOR_MASK = %111 53 | 54 | PUB null 55 | '' This is not a top level object. 56 | 57 | PUB init(ID, mailbox) : cog 58 | 59 | long[mailbox][1] := (@palette << 16) | @vga_font 60 | long[mailbox][3] := 0 61 | 62 | cognew(@driver, mailbox) 63 | cognew(@driver, mailbox | $8000) 64 | 65 | repeat 66 | until long[mailbox][3] == $0000FFFF ' OK (secondary/primary) 67 | 68 | long[mailbox][3] := 0 ' release sync lock 69 | 70 | DAT 71 | ' 72 | ' Each of the 256 (word) palette entries holds FG colour in the high and BG colour in the low byte. Bits 1, 8 and 9 are unused and should be 0. 73 | ' Bit 0 defines whether this colour should blink (1) or not (0). IOW, if the blink attribute is not required all 256 entries are available for 74 | ' user defined colour pairs. 75 | ' 76 | ' For a setup which requires a blink attribute (see below) each colour is doubled (2n: colour|%%0, 2n+1: colour|%%1), e.g. 77 | ' 78 | ' colour format: %FFFF_BBB_A 79 | ' 80 | ' FFFF: foreground index 81 | ' BBB: background index 82 | ' A: blink mode (0/1 = off/on) 83 | ' 84 | palette 85 | word %%000_0_000_0, %%000_0_000_1, %%000_0_200_0, %%000_0_200_1, %%000_0_020_0, %%000_0_020_1, %%000_0_220_0, %%000_0_220_1 86 | word %%000_0_002_0, %%000_0_002_1, %%000_0_202_0, %%000_0_202_1, %%000_0_022_0, %%000_0_022_1, %%000_0_222_0, %%000_0_222_1 87 | word %%200_0_000_0, %%200_0_000_1, %%200_0_200_0, %%200_0_200_1, %%200_0_020_0, %%200_0_020_1, %%200_0_220_0, %%200_0_220_1 88 | word %%200_0_002_0, %%200_0_002_1, %%200_0_202_0, %%200_0_202_1, %%200_0_022_0, %%200_0_022_1, %%200_0_222_0, %%200_0_222_1 89 | word %%020_0_000_0, %%020_0_000_1, %%020_0_200_0, %%020_0_200_1, %%020_0_020_0, %%020_0_020_1, %%020_0_220_0, %%020_0_220_1 90 | word %%020_0_002_0, %%020_0_002_1, %%020_0_202_0, %%020_0_202_1, %%020_0_022_0, %%020_0_022_1, %%020_0_222_0, %%020_0_222_1 91 | word %%220_0_000_0, %%220_0_000_1, %%220_0_200_0, %%220_0_200_1, %%220_0_020_0, %%220_0_020_1, %%220_0_220_0, %%220_0_220_1 92 | word %%220_0_002_0, %%220_0_002_1, %%220_0_202_0, %%220_0_202_1, %%220_0_022_0, %%220_0_022_1, %%220_0_222_0, %%220_0_222_1 93 | word %%002_0_000_0, %%002_0_000_1, %%002_0_200_0, %%002_0_200_1, %%002_0_020_0, %%002_0_020_1, %%002_0_220_0, %%002_0_220_1 94 | word %%002_0_002_0, %%002_0_002_1, %%002_0_202_0, %%002_0_202_1, %%002_0_022_0, %%002_0_022_1, %%002_0_222_0, %%002_0_222_1 95 | word %%202_0_000_0, %%202_0_000_1, %%202_0_200_0, %%202_0_200_1, %%202_0_020_0, %%202_0_020_1, %%202_0_220_0, %%202_0_220_1 96 | word %%202_0_002_0, %%202_0_002_1, %%202_0_202_0, %%202_0_202_1, %%202_0_022_0, %%202_0_022_1, %%202_0_222_0, %%202_0_222_1 97 | word %%022_0_000_0, %%022_0_000_1, %%022_0_200_0, %%022_0_200_1, %%022_0_020_0, %%022_0_020_1, %%022_0_220_0, %%022_0_220_1 98 | word %%022_0_002_0, %%022_0_002_1, %%022_0_202_0, %%022_0_202_1, %%022_0_022_0, %%022_0_022_1, %%022_0_222_0, %%022_0_222_1 99 | word %%222_0_000_0, %%222_0_000_1, %%222_0_200_0, %%222_0_200_1, %%222_0_020_0, %%222_0_020_1, %%222_0_220_0, %%222_0_220_1 100 | word %%222_0_002_0, %%222_0_002_1, %%222_0_202_0, %%222_0_202_1, %%222_0_022_0, %%222_0_022_1, %%222_0_222_0, %%222_0_222_1 101 | word %%111_0_000_0, %%111_0_000_1, %%111_0_200_0, %%111_0_200_1, %%111_0_020_0, %%111_0_020_1, %%111_0_220_0, %%111_0_220_1 102 | word %%111_0_002_0, %%111_0_002_1, %%111_0_202_0, %%111_0_202_1, %%111_0_022_0, %%111_0_022_1, %%111_0_222_0, %%111_0_222_1 103 | word %%300_0_000_0, %%300_0_000_1, %%300_0_200_0, %%300_0_200_1, %%300_0_020_0, %%300_0_020_1, %%300_0_220_0, %%300_0_220_1 104 | word %%300_0_002_0, %%300_0_002_1, %%300_0_202_0, %%300_0_202_1, %%300_0_022_0, %%300_0_022_1, %%300_0_222_0, %%300_0_222_1 105 | word %%030_0_000_0, %%030_0_000_1, %%030_0_200_0, %%030_0_200_1, %%030_0_020_0, %%030_0_020_1, %%030_0_220_0, %%030_0_220_1 106 | word %%030_0_002_0, %%030_0_002_1, %%030_0_202_0, %%030_0_202_1, %%030_0_022_0, %%030_0_022_1, %%030_0_222_0, %%030_0_222_1 107 | word %%331_0_000_0, %%331_0_000_1, %%331_0_200_0, %%331_0_200_1, %%331_0_020_0, %%331_0_020_1, %%331_0_220_0, %%331_0_220_1 108 | word %%331_0_002_0, %%331_0_002_1, %%331_0_202_0, %%331_0_202_1, %%331_0_022_0, %%331_0_022_1, %%331_0_222_0, %%331_0_222_1 109 | word %%003_0_000_0, %%003_0_000_1, %%003_0_200_0, %%003_0_200_1, %%003_0_020_0, %%003_0_020_1, %%003_0_220_0, %%003_0_220_1 110 | word %%003_0_002_0, %%003_0_002_1, %%003_0_202_0, %%003_0_202_1, %%003_0_022_0, %%003_0_022_1, %%003_0_222_0, %%003_0_222_1 111 | word %%303_0_000_0, %%303_0_000_1, %%303_0_200_0, %%303_0_200_1, %%303_0_020_0, %%303_0_020_1, %%303_0_220_0, %%303_0_220_1 112 | word %%303_0_002_0, %%303_0_002_1, %%303_0_202_0, %%303_0_202_1, %%303_0_022_0, %%303_0_022_1, %%303_0_222_0, %%303_0_222_1 113 | word %%033_0_000_0, %%033_0_000_1, %%033_0_200_0, %%033_0_200_1, %%033_0_020_0, %%033_0_020_1, %%033_0_220_0, %%033_0_220_1 114 | word %%033_0_002_0, %%033_0_002_1, %%033_0_202_0, %%033_0_202_1, %%033_0_022_0, %%033_0_022_1, %%033_0_222_0, %%033_0_222_1 115 | word %%333_0_000_0, %%333_0_000_1, %%333_0_200_0, %%333_0_200_1, %%333_0_020_0, %%333_0_020_1, %%333_0_220_0, %%333_0_220_1 116 | word %%333_0_002_0, %%333_0_002_1, %%333_0_202_0, %%333_0_202_1, %%333_0_022_0, %%333_0_022_1, %%333_0_222_0, %%333_0_222_1 117 | 118 | DAT org 0 ' video driver 119 | 120 | driver jmpret $, #setup ' -4 once 121 | 122 | ' horizontal timing 720(720) 1(18) 6(108) 3(54) 123 | ' vertical timing 400(400) 13(13) 2(2) 34(34) 124 | 125 | ' +---------------- front porch 126 | ' | +-------------- sync 127 | ' | | +--------- back porch 128 | ' | | | 129 | vsync mov ecnt, #13+2+(34-4) 130 | 131 | cmp ecnt, #32 wz 132 | if_ne cmp ecnt, #30 wz 133 | if_e xor sync, #$0101 ' in/active 134 | 135 | call #blank 136 | djnz ecnt, #vsync+1 137 | 138 | ' While still in sync, figure out the blink state (used to be based on cnt) and cursor. 139 | ' hsync offers 31 hub windows. 140 | 141 | add fcnt, #1 ' next frame 142 | cmpsub fcnt, #36 wz ' N frames per phase (on/off) 143 | if_z rev rcnt, #{32-}0 ' $F80000_00 vs $000000_1F; 70/(2*36), ~1Hz 144 | 145 | cmp locn, #0 wz ' check cursor availability 146 | mov crs0, #0 ' default is disabled 147 | if_ne rdlong crs0, locn ' override 148 | if_ne rol locn, #16 149 | mov crs1, #0 ' default is disabled 150 | if_ne rdlong crs1, locn ' override 151 | 152 | mov vier, crs0 ' | 153 | call #prep ' process cursor 0 154 | mov crs0, vier ' | 155 | 156 | mov vier, crs1 ' | 157 | call #prep ' process cursor 1 158 | mov crs1, vier ' | 159 | 160 | rdlong temp, scrn_ wz ' get screen address 161 | if_nz mov scrn, temp 162 | if_nz wrlong zero, scrn_ ' acknowledge screen buffer setup 163 | if_nz add scrn, $+1 ' scrn now points to last byte 164 | long 160*25 -1 165 | 166 | mov ecnt, #4 167 | if_nc call #blank ' | 168 | if_nc djnz ecnt, #$-1 ' back porch remainder (primary only) 169 | 170 | ' Vertical sync chain done, do visible area. 171 | 172 | mov zwei, scrn ' screen base address 173 | mov rows, #res_y/16 ' row count 174 | 175 | :scan mov scnt, #16/4/2 ' 16 quad scanlines (split between primary and secondary) 176 | mov eins, font ' font base 177 | if_nc add eins, adv4 ' interleaved 178 | 179 | :line mov vscl, many ' four lines we don't use 180 | waitvid zero, #0 ' 635 hub windows 181 | 182 | call #load ' load pixels and colours for the next four lines 183 | 184 | call #chars ' | 185 | call #chars ' | 186 | call #chars ' display scanlines 187 | call #char3 ' | 188 | 189 | add eins, adv8 ' skip 8 scanlines 190 | djnz scnt, #:line ' for all character scanlines 191 | sub zwei, #80*2 ' next row 192 | djnz rows, #:scan ' for all rows 193 | 194 | mov ecnt, #4 195 | if_c call #blank ' secondary finishes early so 196 | if_c djnz ecnt, #$-1 ' let him do some blank lines 197 | 198 | if_nc wrlong cnt, fcnt_ ' announce vertical blank (primary) 199 | 200 | jmp #vsync ' next frame 201 | 202 | 203 | blank mov vscl, line ' 180/720 204 | waitvid sync, #%0000 ' latch blank line 205 | call #hsync 206 | blank_ret ret 207 | 208 | 209 | chars movd :one, #pix-1 ' | 210 | movs :two, #pix+0 ' | 211 | movd :two, #col+0 ' restore initial settings 212 | 213 | mov vscl, hvis ' 1/9, speed up (one pixel per frame clock) 214 | mov ecnt, #80 ' character count 215 | 216 | :loop add :one, dst1 ' advance 217 | add :two, d1s1 ' advance (pipeline) 218 | :two waitvid 0-0, 1-1 ' emit pixels 219 | :one ror 1-1, #10 ' %%0_cCCCC_bBBBB_aAAAA 220 | djnz ecnt, #:loop 221 | 222 | ' Horizontal sync embedded here due to timing constraints, only 18 clocks are allowed between waitvids. 223 | 224 | hsync mov vscl, wrap ' | 225 | waitvid sync, #%0001111110 ' horizontal sync pulse (1/6/3 reverse) 226 | mov cnt, cnt ' record sync point 227 | hsync_ret 228 | chars_ret ret 229 | 230 | 231 | char3 movs :two, #pix-80 ' | 232 | movd :two, #col+0 ' restore initial settings 233 | 234 | mov vscl, hvis ' 1/9, speed up (one pixel per frame clock) 235 | mov ecnt, #80 ' character count 236 | 237 | :loop add :two, d1s1 ' advance (pipeline) 238 | :two waitvid 0-0, 1-1 ' emit pixels 239 | djnz ecnt, #:loop 240 | 241 | call #hsync 242 | char3_ret ret 243 | 244 | 245 | load muxnc flag, $ ' preserve carry flag 246 | 247 | movd :pix0_0, #pix+0 ' | 248 | movd :pix3_0, #pix-80 ' re/store initial settings 249 | movd :colN_0, #col+0 ' | 250 | 251 | movd :pix0_1, #pix+1 ' | 252 | movd :pix3_1, #pix-79 ' | 253 | movd :colN_1, #col+1 ' | 254 | 255 | mov drei, dst2 ' | 256 | add drei, eins ' tail font address (+1024) 257 | 258 | mov addr, zwei ' current screen base 259 | mov ecnt, #40 ' loop counter 260 | 261 | ' Fetch pixel data and colour. 262 | 263 | :loop rdword frqb, addr {hub} ' +0 = read ASCII + colour 264 | 265 | ror frqb, #7 ' +8 ASCII *2 +{0..1} 266 | mov phsb, eins ' -4 current font address 267 | rdlong pix0, phsb {hub} ' +0 = three scanlines + 1 pixel 268 | 269 | ror frqb, #1 ' +8 ASCII *1 270 | add frqb, drei ' -4 font tail address 271 | rdbyte pix3, frqb {hub} ' +0 = remaining 8 pixels 272 | 273 | shr frqb, #24 ' +8 palette index 274 | mov phsb, plte ' -4 current palette location 275 | rdword colN, phsb {hub} ' +0 = read palette entry 276 | 277 | sub addr, #2 ' +8 advance source 278 | test colN, #1 wz ' -4 check mode 279 | shr pix0, #1 wc ' +0 = extract top pixel 280 | muxc pix3, #$100 ' +4 insert top pixel 281 | 282 | if_nz shr pix0, rcnt ' +8 1: modify foreground (0/31) 283 | if_nz shr pix3, rcnt ' -4 1: modify foreground (0/31) 284 | 285 | and colN, cmsk ' +0 = clean sync bits 286 | or colN, idle ' +4 insert idle state 287 | 288 | :pix0_0 mov 0-0, pix0 ' +8 store scanlines 0..2 289 | add $-1, dst2 ' -4 | 290 | :pix3_0 mov 1-1, pix3 ' +0 = store scanline 3 291 | add $-1, dst2 ' +4 | 292 | :colN_0 mov 2-2, colN ' +8 store palette 293 | add $-1, dst2 ' -4 | 294 | 295 | rdword frqb, addr {hub} ' +0 = 296 | 297 | ror frqb, #7 ' +8 298 | mov phsb, eins ' -4 299 | rdlong pix0, phsb {hub} ' +0 = 300 | 301 | ror frqb, #1 ' +8 302 | add frqb, drei ' -4 303 | rdbyte pix3, frqb {hub} ' +0 = 304 | 305 | shr frqb, #24 ' +8 306 | mov phsb, plte ' -4 307 | rdword colN, phsb {hub} ' +0 = 308 | 309 | sub addr, #2 ' +8 310 | test colN, #1 wz ' -4 311 | shr pix0, #1 wc ' +0 = 312 | muxc pix3, #$100 ' +4 313 | 314 | if_nz shr pix0, rcnt ' +8 315 | if_nz shr pix3, rcnt ' -4 316 | 317 | and colN, cmsk ' +0 = 318 | or colN, idle ' +4 319 | 320 | :pix0_1 mov 0-0, pix0 ' +8 321 | add $-1, dst2 ' -4 322 | :pix3_1 mov 1-1, pix3 ' +0 = 323 | add $-1, dst2 ' +4 324 | :colN_1 mov 2-2, colN ' +8 325 | add $-1, dst2 ' -4 326 | 327 | djnz ecnt, #:loop ' +0 = for all characters 328 | 329 | mov vier, crs0 330 | call #cursor 331 | 332 | cmp crs0, crs1 wz 333 | if_ne mov vier, crs1 334 | if_ne call #cursor 335 | 336 | load_ret jmpret flag, #0-0 nr,wc ' restore carry flag 337 | 338 | 339 | cursor test vier, #%100 wz ' cursor enabled? 340 | 341 | mov temp, vier ' local copy 342 | sar temp, #1+16 ' extract y - 25 343 | if_z add temp, rows wz ' rows = {25..1} 344 | if_nz jmp cursor_ret ' wrong row/disabled 345 | 346 | test vier, #%010 wz,wc ' underscore(1)/block(0) 347 | if_nz cmp scnt, #1 wz 348 | if_nz jmp cursor_ret ' wrong scanline pair 349 | 350 | muxc :set1, #1 ' adjust source 351 | muxc :set2, #1 352 | 353 | ror vier, #8 wc ' carry: blink on/off 354 | movd :set1, vier 355 | sub vier, #80 356 | movd :set2, vier 357 | if_c cmp fcnt, #18 wc ' 70/(2*18), ~2Hz 358 | :set1 if_nc xor 0-0, pmsk{2n} ' cmsk: block 359 | ' pmsk: underscore 360 | :set2 if_nc xor 0-0, pmsk{2n} ' cmsk: block 361 | ' pmsk: underscore 362 | cursor_ret ret 363 | 364 | 365 | prep mov temp, vier ' working copy 366 | shr temp, #16 ' | 367 | and temp, #255 ' extract y 368 | sub temp, #25 ' y - 25 369 | shl temp, #1+16 ' 2(y - 25) 370 | 371 | and vier, xmsk ' get rid of y 372 | max vier, xlim ' limit x to park position (auto off) 373 | xor vier, #%100 ' invert on/off 374 | or vier, temp ' reinsert y 375 | 376 | ror vier, #8 ' align x for add 377 | add vier, #pix 378 | rol vier, #8 ' restore cursor descriptor 379 | 380 | prep_ret ret 381 | 382 | ' initialised data and/or presets 383 | 384 | xmsk long $0000FF07 ' covers mode/x 385 | xlim long 80 << 8 ' park position 386 | 387 | rcnt long $0000001F ' bit shift for blink mode 388 | fcnt long 0 ' blink frame count 389 | adv4 long 256*(4+1)*1 ' 4 scanlines in font 390 | adv8 long 256*(4+1)*2 ' 8 scanlines in font 391 | 392 | flag long 0 ' loader flag storage 393 | idle long hv_idle 394 | sync long hv_idle ^ $0200 395 | 396 | wrap long 18 << 12 | 180 ' 18/180 397 | hvis long 1 << 12 | 9 ' 1/9 398 | line long 180 << 12 | 720 ' 180/720 399 | many long 0 << 12 | 3600 ' 256/3600 400 | 401 | scrn_ long $00000000 -12 ' | 402 | font_ long $00000004 -12 ' | 403 | locn_ long $00000008 -12 ' | 404 | fcnt_ long $0000000C -12 ' mailbox addresses (local copy) (##) 405 | 406 | dst1 long 1 << 9 ' dst +/-= 1 407 | dst2 long 2 << 9 ' dst +/-= 2 408 | d1s1 long 1 << 9 | 1 ' dst/src +/-= 1 409 | 410 | cmsk long %%3330_3330 ' color mask 411 | 412 | long 0[$&1] 413 | pmsk {2n} long %%0_13333_13333_13333 ' xor mask for block cursor 414 | pmsk1 {2n+1} long %%0_13333_13333_13333 ' xor mask for underscore cursor (updated for secondary) 415 | 416 | ' Stuff below is re-purposed for temporary storage. 417 | 418 | setup add trap, par wc ' carry set -> secondary 419 | and trap, hram ' confine to hub RAM 420 | 421 | add scrn_, trap ' @long[par][0] 422 | add font_, trap ' @long[par][1] 423 | add locn_, trap ' @long[par][2] 424 | add fcnt_, trap ' @long[par][3] 425 | 426 | addx trap, #%00 ' add secondary offset 427 | wrbyte hram, trap ' up and running 428 | 429 | rdlong temp, trap wz ' | (%%) 430 | if_nz jmp #$-1 ' synchronized start 431 | 432 | ' primary: cnt + 0 433 | ' secondary: cnt + 2 434 | 435 | rdlong scrn, scrn_ ' get screen address (4n) (%%) 436 | wrlong zero, scrn_ ' acknowledge screen buffer setup 437 | 438 | rdlong font, font_ ' get font definition (2n) (%%) 439 | wrlong zero, font_ ' acknowledge font definition setup 440 | 441 | mov plte, font ' get palette location (2n) (%%) 442 | shr plte, #16 ' | 443 | 444 | rdlong locn, locn_ wz ' get cursor location (%%) 445 | and font, $+1 ' | 446 | long $0000FFFC ' cleanup 447 | if_nz wrlong zero, locn_ ' acknowledge cursor location 448 | 449 | ' Perform pending setup. 450 | 451 | add scrn, $+1 ' scrn now points to last word 452 | long 160*25 -2 453 | 454 | ' Upset video h/w and relatives. 455 | 456 | rdlong temp, #0 ' clkfreq 457 | shr temp, #10 ' ~1ms 458 | if_nc waitpne $, #0 ' adjust primary 459 | 460 | ' primary: cnt + 0 + 6 461 | ' secondary: cnt + 2 + 4 462 | 463 | add temp, cnt 464 | 465 | movi ctrb, #%0_11111_000 ' LOGIC always (loader support) 466 | movi ctra, #%0_00001_101 ' PLL, VCO/4 467 | mov frqa, frqx ' 28.322MHz 468 | 469 | mov vscl, #1 ' reload as fast as possible 470 | mov zwei, scrn ' vgrp:[!Z]:vpin:[!Z]:scrn = 2:1:8:5:16 (%%) 471 | shr zwei, #5+16 ' | 472 | or zwei, #%%000_3 ' | 473 | mov vcfg, zwei ' set vgrp and vpin 474 | movi vcfg, #%0_01_0_00_000 ' VGA, 2 colour mode 475 | 476 | waitcnt temp, #0 ' PLL settled, frame counter flushed 477 | 478 | ror vcfg, #1 ' freeze video h/w 479 | mov vscl, line ' transfer user value 480 | rol vcfg, #1 ' unfreeze 481 | waitpne $, #0 ' get some distance 482 | waitvid zero, #0 ' latch user value 483 | 484 | and mask, vcfg ' transfer vpin 485 | mov temp, vcfg ' | 486 | shr temp, #9 ' extract vgrp 487 | shl temp, #3 ' 0..3 >> 0..24 488 | shl mask, temp ' finalise mask 489 | 490 | max dira, mask ' drive outputs 491 | if_c mov pmsk1, #0 ' no cursor mask for secondary 492 | 493 | ' Setup complete, do the heavy lifting upstairs ... 494 | 495 | jmp %%0 ' return 496 | 497 | ' Local data, used only once. 498 | 499 | frqx long $16A85879 ' 28.322MHz 500 | mask long %11111111 501 | 502 | hram long $00007FFF ' hub RAM mask 503 | trap long $FFFF8000 +12 ' primary/secondary trap (##) 504 | 505 | EOD{ata} fit 506 | 507 | ' uninitialised data and/or temporaries 508 | 509 | org setup 510 | 511 | scrn res 1 ' screen buffer < setup +10 (%%) 512 | font res 1 ' font definition < setup +12 (%%) 513 | plte res 1 ' palette location < setup +14 (%%) 514 | locn res 1 ' cursor location < setup +16 (%%) 515 | ecnt res 1 ' element count 516 | scnt res 1 ' scanlines (per char) 517 | 518 | temp res alias ' < setup + 8 (%%) 519 | addr res 1 ' current screen base 520 | rows res 1 ' display row count 521 | crs0 res 1 ' cursor 0 location and mode 522 | crs1 res 1 ' cursor 1 location and mode 523 | 524 | eins res 1 525 | zwei res 1 ' < setup +30 (%%) 526 | drei res 1 527 | vier res 1 528 | 529 | pix0 res 1 530 | pix3 res 1 531 | colN res 1 532 | 533 | res 80 ' | 534 | pix res 80 +1 ' emitter pixel array | 535 | col res 80 +1 ' emitter colour data | + park position 536 | 537 | tail fit 538 | 539 | CON 540 | zero = $1F0 ' par (dst only) 541 | hv_idle = $01010101 * %10 {%hv} ' h/v sync inactive 542 | 543 | res_x = 720 ' | 544 | res_y = 400 ' | 545 | res_m = 4 ' UI support 546 | 547 | alias = 0 548 | 549 | DAT 550 | 551 | vga_font 552 | long $00000000, $0FC00000, $0FC00000, $00000000, $00000000, $00000000, $00000000, $00000000 553 | long $1FE7F9FE, $00000000, $1FE7F9FE, $0F000000, $07800000, $1F800000, $1FC00000, $00000000 554 | long $00600800, $0C020000, $03000000, $0CC00000, $1FC00000, $0C61F000, $00000000, $03000000 555 | long $03000000, $03000000, $00000000, $00000000, $00000000, $00000000, $00000000, $00000000 556 | long $00000000, $03000000, $0CC33000, $00000000, $07C0C030, $00000000, $03800000, $01806000 557 | long $06000000, $01800000, $00000000, $00000000, $00000000, $00000000, $00000000, $00000000 558 | long $07800000, $03000000, $07C00000, $07C00000, $06000000, $0FE00000, $03800000, $0FE00000 559 | long $07C00000, $07C00000, $00000000, $00000000, $00000000, $00000000, $00000000, $07C00000 560 | long $00000000, $01000000, $07E00000, $07800000, $03E00000, $0FE00000, $0FE00000, $07800000 561 | long $0C600000, $07800000, $0F000000, $0CE00000, $01E00000, $18600000, $0C600000, $07C00000 562 | long $07E00000, $07C00000, $07E00000, $07C00000, $1FE00000, $0C600000, $18600000, $18600000 563 | long $18600000, $18600000, $1FE00000, $07800000, $00000000, $07800000, $06C0E010, $00000000 564 | long $03006018, $00000000, $00E00000, $00000000, $07000000, $00000000, $03800000, $00000000 565 | long $00E00000, $03000000, $0C000000, $00E00000, $03800000, $00000000, $00000000, $00000000 566 | long $00000000, $00000000, $00000000, $00000000, $01000000, $00000000, $00000000, $00000000 567 | long $00000000, $00000000, $00000000, $0E000000, $03000000, $01C00000, $0DC00000, $00000000 568 | long $07800000, $06600000, $03018000, $03804000, $06600000, $01803000, $06C0E000, $00000000 569 | long $03804000, $0C600000, $01803000, $0CC00000, $0780C000, $01803000, $00031800, $0381B038 570 | long $00C06030, $00000000, $0F800000, $03804000, $0C600000, $01803000, $03C06000, $01803000 571 | long $0C600000, $00031800, $00031800, $0300C000, $06C0E000, $18600000, $0CC1F800, $1B038000 572 | long $0180C000, $03018000, $0180C000, $0180C000, $0DC00000, $0001D8DC, $06C1E000, $06C0E000 573 | long $01800000, $00000000, $00000000, $00601800, $00601800, $03000000, $00000000, $00000000 574 | long $11011110, $1542A954, $17677176, $0300C030, $0300C030, $0300C030, $0D8360D8, $00000000 575 | long $00000000, $0D8360D8, $0D8360D8, $00000000, $0D8360D8, $0D8360D8, $0300C030, $00000000 576 | long $0300C030, $0300C030, $00000000, $0300C030, $00000000, $0300C030, $0300C030, $0D8360D8 577 | long $0D8360D8, $00000000, $0D8360D8, $00000000, $0D8360D8, $00000000, $0D8360D8, $0300C030 578 | long $0D8360D8, $00000000, $00000000, $0D8360D8, $0300C030, $00000000, $00000000, $0D8360D8 579 | long $0300C030, $0300C030, $00000000, $3FEFFBFF, $00000000, $01E0781E, $3E0F83E1, $3FEFFBFF 580 | long $00000000, $03C00000, $0FE00000, $00000000, $00000000, $00000000, $00000000, $00000000 581 | long $00000000, $00000000, $03800000, $0F000000, $00000000, $00000000, $07000000, $00000000 582 | long $00000000, $00000000, $00000000, $00000000, $0E000000, $0300C030, $00000000, $00000000 583 | long $06C0E000, $00000000, $00000000, $06078000, $06C0D800, $03607000, $00000000, $00000000 584 | 585 | byte $00, $81, $FF, $00, $00, $18, $18, $00, $FF, $00, $FF, $70, $66, $CC, $C6, $18 586 | byte $07, $70, $3C, $66, $DB, $06, $00, $3C, $3C, $18, $00, $00, $00, $00, $00, $00 587 | byte $00, $3C, $66, $36, $63, $00, $36, $0C, $18, $18, $00, $00, $00, $00, $00, $00 588 | byte $66, $1C, $63, $63, $38, $03, $06, $63, $63, $63, $00, $00, $60, $00, $06, $63 589 | byte $3E, $1C, $66, $66, $36, $66, $66, $66, $63, $18, $30, $66, $06, $E7, $67, $63 590 | byte $66, $63, $66, $63, $DB, $63, $C3, $C3, $C3, $C3, $C3, $0C, $01, $30, $63, $00 591 | byte $00, $00, $06, $00, $30, $00, $36, $00, $06, $18, $60, $06, $18, $00, $00, $00 592 | byte $00, $00, $00, $00, $0C, $00, $00, $00, $00, $00, $00, $18, $18, $18, $3B, $00 593 | byte $66, $00, $0C, $36, $00, $18, $1C, $00, $36, $00, $18, $00, $66, $18, $08, $00 594 | byte $00, $00, $36, $36, $00, $18, $33, $18, $00, $3E, $63, $7E, $26, $66, $66, $18 595 | byte $06, $0C, $06, $06, $3B, $63, $36, $36, $0C, $00, $00, $43, $43, $18, $00, $00 596 | byte $22, $55, $EE, $18, $18, $18, $6C, $00, $00, $6C, $6C, $00, $6C, $6C, $18, $00 597 | byte $18, $18, $00, $18, $00, $18, $18, $6C, $6C, $00, $6C, $00, $6C, $00, $6C, $18 598 | byte $6C, $00, $00, $6C, $18, $00, $00, $6C, $18, $18, $00, $FF, $00, $0F, $F0, $FF 599 | byte $00, $33, $63, $00, $7F, $00, $00, $00, $7E, $1C, $36, $0C, $00, $C0, $0C, $3E 600 | byte $00, $00, $0C, $30, $D8, $18, $00, $00, $36, $00, $00, $30, $36, $0C, $00, $00 601 | 602 | long $00000000, $1024094A, $1FE7F9B6, $0FE3F86C, $07C0E010, $1CE1E078, $1FE3F078, $03000000 603 | long $1CE7F9FE, $0CC1E000, $132619FE, $03C260B0, $0CC330CC, $018061F8, $18C631FC, $0786D830 604 | long $0FE0F81E, $0FE3E0F0, $0300C0FC, $0CC330CC, $1BC6D9B6, $0C61B038, $00000000, $0300C0FC 605 | long $0300C0FC, $0300C030, $0600C000, $00C06000, $00600000, $0CC12000, $0380E010, $07C3F8FE 606 | long $00000000, $0301E078, $00000048, $06C3F86C, $07C01886, $06031886, $0DC0E06C, $0000000C 607 | long $01806018, $06018060, $07833000, $0300C000, $00000000, $00000000, $00000000, $06030080 608 | long $1B661986, $0300C03C, $030180C0, $078300C0, $0661B078, $07E01806, $07E01806, $060300C0 609 | long $07C318C6, $0FC318C6, $0000C030, $0000C030, $0180C060, $0003F000, $0600C018, $030180C6 610 | long $0F6318C6, $0C63186C, $07C330CC, $00601886, $0CC330CC, $03C0B08C, $03C0B08C, $00601886 611 | long $0FE318C6, $0300C030, $06018060, $03C1B0CC, $00C0300C, $1B67F9FE, $0F63F8DE, $0C6318C6 612 | long $07C330CC, $0C6318C6, $07C330CC, $038030C6, $0300C132, $0C6318C6, $18661986, $18661986 613 | long $0301E0CC, $07833186, $030180C2, $01806018, $01C03806, $06018060, $00000000, $00000000 614 | long $00000000, $0600F000, $06C0F00C, $0C61F000, $06C1E060, $0C61F000, $01E0304C, $06637000 615 | long $0DC1B00C, $0300E000, $0C038000, $06C3300C, $0300C030, $1FE33800, $0CC1D800, $0C61F000 616 | long $0CC1D800, $06637000, $0DC1D800, $0C61F000, $0181F818, $06619800, $18661800, $18661800 617 | long $0CC61800, $0C631800, $0663F800, $01C0C030, $0000C030, $0E00C030, $00000000, $06C0E010 618 | long $00601886, $06619800, $0C61F000, $0600F000, $0600F000, $0600F000, $0600F000, $00C33078 619 | long $0C61F000, $0C61F000, $0C61F000, $0300E000, $0300E000, $0300E000, $0C61B038, $0C61B038 620 | long $00C330FE, $1B83B000, $0FE19866, $0C61F000, $0C61F000, $0C61F000, $06619800, $06619800 621 | long $0C631800, $0C6318C6, $0C6318C6, $00601986, $00C0780C, $1FE0C078, $0CC2307C, $0FC0C030 622 | long $0600F000, $0300E000, $0C61F000, $06619800, $0CC1D800, $0FE378CE, $0FC000F8, $07C00038 623 | long $01806000, $0FE00000, $0FE00000, $030198C6, $030198C6, $0300C000, $06C36000, $06C0D800 624 | long $11011110, $1542A954, $17677176, $0300C030, $0300C030, $0300F830, $0D8360D8, $00000000 625 | long $0300F800, $0C0378D8, $0D8360D8, $0C03F800, $0C0378D8, $0D8360D8, $0300F830, $00000000 626 | long $0300C031, $0300C031, $00000001, $0300C031, $00000001, $0300C031, $030FC031, $0D8360D9 627 | long $018F60D9, $018FE001, $000F78D9, $000FF801, $018F60D9, $000FF801, $000F78D9, $000FF831 628 | long $0D8360D9, $000FF801, $00000001, $0D8360D9, $030FC031, $030FC001, $00000001, $0D8360D9 629 | long $030FF831, $0300C030, $00000001, $3FEFFBFF, $00000001, $01E0781E, $3E0F83E1, $3FEFFBFE 630 | long $07637000, $03619866, $006018C6, $06C1B0FE, $018030C6, $0363F000, $0CC330CC, $0301D8DC 631 | long $0CC1E030, $0C63186C, $0C6318C6, $0F818030, $1B63F000, $1B63F0C0, $07C0300C, $0C6318C6 632 | long $000000FE, $0FC0C030, $0C018030, $00C06030, $0300C1B0, $0300C030, $0000C030, $07637000 633 | long $00000038, $00000000, $00000000, $06018060, $06C1B06C, $03E0980C, $07C1F07C, $00000000 634 | 635 | byte $00, $BD, $C3, $7F, $7F, $E7, $FF, $3C, $C3, $42, $BD, $33, $3C, $0C, $C6, $E7 636 | byte $1F, $7C, $18, $66, $D8, $63, $00, $18, $18, $18, $7F, $7F, $03, $FF, $3E, $3E 637 | byte $00, $18, $00, $36, $60, $18, $3B, $00, $0C, $30, $FF, $7E, $00, $7F, $00, $18 638 | byte $DB, $18, $0C, $60, $7F, $60, $63, $18, $63, $60, $00, $00, $06, $00, $60, $18 639 | byte $7B, $7F, $66, $03, $66, $16, $16, $7B, $63, $18, $30, $1E, $06, $C3, $73, $63 640 | byte $06, $63, $36, $30, $18, $63, $C3, $DB, $18, $18, $0C, $0C, $1C, $30, $00, $00 641 | byte $00, $3E, $66, $03, $33, $7F, $06, $33, $66, $18, $60, $1E, $18, $DB, $66, $63 642 | byte $66, $33, $66, $06, $0C, $33, $C3, $C3, $3C, $63, $18, $18, $18, $18, $00, $63 643 | byte $03, $33, $7F, $3E, $3E, $3E, $3E, $06, $7F, $7F, $7F, $18, $18, $18, $63, $63 644 | byte $3E, $D8, $33, $63, $63, $63, $33, $33, $63, $63, $63, $03, $06, $18, $F6, $18 645 | byte $3E, $18, $63, $33, $66, $7B, $00, $00, $06, $03, $60, $0C, $0C, $18, $1B, $6C 646 | byte $22, $55, $EE, $18, $1F, $1F, $6F, $7F, $1F, $6F, $6C, $6F, $7F, $7F, $1F, $1F 647 | byte $F8, $FF, $FF, $F8, $FF, $FF, $F8, $EC, $FC, $EC, $FF, $EF, $EC, $FF, $EF, $FF 648 | byte $FF, $FF, $FF, $FC, $F8, $F8, $FC, $FF, $FF, $1F, $F8, $FF, $FF, $0F, $F0, $00 649 | byte $1B, $33, $03, $36, $18, $1B, $66, $18, $66, $7F, $36, $66, $DB, $DB, $06, $63 650 | byte $7F, $18, $30, $0C, $18, $18, $7E, $00, $00, $18, $00, $37, $00, $00, $3E, $00 651 | 652 | long $00000000, $10240932, $1FE7F9CE, $0381F0FE, $0100E07C, $0300C1CE, $0300C0FC, $0000C078 653 | long $1FE73986, $07833084, $1864C97A, $06619866, $0303F030, $01E07018, $1CE7318C, $0306D878 654 | long $0060381E, $0C0380F0, $0301E0FC, $0CC000CC, $1B06C1B0, $0600E06C, $0FE3F8FE, $0301E0FC 655 | long $0300C030, $0783F030, $0000C060, $0000600C, $0003F806, $000120CC, $0FE3F87C, $0100E038 656 | long $00000000, $03000030, $00000000, $06C3F86C, $0C6308C0, $0C603018, $06619866, $00000000 657 | long $03006018, $03018060, $00033078, $0000C030, $0300C000, $00000000, $03000000, $00603018 658 | long $0CC61986, $0300C030, $0C60180C, $0C6300C0, $06018060, $0C6300C0, $0C6318C6, $01806018 659 | long $0C6318C6, $060300C0, $0300C000, $0300C000, $0600C018, $000000FC, $0180C060, $03000030 660 | long $0061D8F6, $0C6318C6, $0CC330CC, $0CC21806, $06C330CC, $0CC2300C, $00C0300C, $0CC318C6 661 | long $0C6318C6, $0300C030, $06619866, $0CC3306C, $0CC2300C, $18661986, $0C6318C6, $0C6318C6 662 | long $00C0300C, $0F6358C6, $0CC330CC, $0C6318C0, $0300C030, $0C6318C6, $07833186, $0CC7F9B6 663 | long $18633078, $0300C030, $1864180C, $01806018, $0C038070, $06018060, $00000000, $00000000 664 | long $00000000, $06619866, $0CC330CC, $0C601806, $06619866, $0C601806, $00C0300C, $06619866 665 | long $0CC330CC, $0300C030, $0C0300C0, $0CC1B03C, $0300C030, $1B66D9B6, $0CC330CC, $0C6318C6 666 | long $0CC330CC, $06619866, $00C0300C, $0C618038, $0D806018, $06619866, $07833186, $1FE6D9B6 667 | long $0CC1E030, $0C6318C6, $0C603018, $0300C030, $0300C030, $0300C030, $00000000, $0FE318C6 668 | long $07833086, $06619866, $0C601806, $06619866, $06619866, $06619866, $06619866, $0601E0CC 669 | long $0C601806, $0C601806, $0C601806, $0300C030, $0300C030, $0300C030, $0C6318FE, $0C6318FE 670 | long $0CC0300C, $0760D8FC, $06619866, $0C6318C6, $0C6318C6, $0C6318C6, $06619866, $06619866 671 | long $0C6318C6, $0C6318C6, $0C6318C6, $0303F186, $0CE0300C, $0300C1FE, $0CC330CC, $0300C030 672 | long $06619866, $0300C030, $0C6318C6, $06619866, $0CC330CC, $0C6318E6, $00000000, $00000000 673 | long $0C631806, $00601806, $0C0300C0, $1B23980C, $0D2398CC, $0781E078, $0003606C, $0000D86C 674 | long $11011110, $1542A954, $17677176, $0300C030, $0300C030, $0300C030, $0D8360D8, $0D8360D8 675 | long $0300C030, $0D8360D8, $0D8360D8, $0D8360D8, $00000000, $00000000, $00000000, $0300C030 676 | long $00000000, $00000000, $0300C030, $0300C030, $00000000, $0300C030, $0300C030, $0D8360D8 677 | long $00000000, $0D8360D8, $00000000, $0D8360D8, $0D8360D8, $00000000, $0D8360D8, $00000000 678 | long $00000000, $0300C030, $0D8360D8, $00000000, $00000000, $0300C030, $0D8360D8, $0D8360D8 679 | long $0300C030, $00000000, $0300C030, $3FEFFBFF, $3FEFFBFF, $01E0781E, $3E0F83E1, $00000000 680 | long $0760D836, $0C6318C6, $00601806, $06C1B06C, $0C603018, $0360D836, $00C1F0CC, $0300C030 681 | long $0301E0CC, $06C318C6, $06C1B06C, $0CC330CC, $0003F1B6, $00C3F19E, $0180300C, $0C6318C6 682 | long $0FE00000, $00000030, $00006030, $00018030, $0300C030, $0360D836, $0300C000, $0001D8DC 683 | long $00000000, $00000030, $00000030, $0781B06C, $00000000, $00000000, $07C1F07C, $00000000 684 | 685 | byte $00, $7E, $7E, $08, $00, $3C, $3C, $00, $FF, $00, $FF, $1E, $18, $07, $67, $18 686 | byte $01, $40, $00, $66, $D8, $63, $7F, $7E, $18, $18, $00, $00, $00, $00, $00, $00 687 | byte $00, $18, $00, $36, $3E, $61, $6E, $00, $30, $0C, $00, $00, $18, $00, $18, $01 688 | byte $3C, $7E, $7F, $3E, $78, $3E, $3E, $0C, $3E, $1E, $00, $0C, $60, $00, $06, $18 689 | byte $3E, $63, $3F, $3C, $1F, $7F, $0F, $5C, $63, $3C, $1E, $67, $7F, $C3, $63, $3E 690 | byte $0F, $3E, $67, $3E, $3C, $3E, $18, $66, $C3, $3C, $FF, $3C, $40, $3C, $00, $00 691 | byte $00, $6E, $3E, $3E, $6E, $3E, $0F, $3E, $67, $3C, $60, $67, $3C, $DB, $66, $3E 692 | byte $3E, $3E, $0F, $3E, $38, $6E, $18, $66, $C3, $7E, $7F, $70, $18, $0E, $00, $00 693 | byte $30, $6E, $3E, $6E, $6E, $6E, $6E, $60, $3E, $3E, $3E, $3C, $3C, $3C, $63, $63 694 | byte $7F, $EE, $73, $3E, $3E, $3E, $6E, $6E, $7E, $3E, $3E, $18, $3F, $18, $CF, $18 695 | byte $6E, $3C, $3E, $6E, $66, $63, $00, $00, $3E, $00, $00, $60, $7C, $18, $00, $00 696 | byte $22, $55, $EE, $18, $18, $18, $6C, $6C, $18, $6C, $6C, $6C, $00, $00, $00, $18 697 | byte $00, $00, $18, $18, $00, $18, $18, $6C, $00, $6C, $00, $6C, $6C, $00, $6C, $00 698 | byte $00, $18, $6C, $00, $00, $18, $6C, $6C, $18, $00, $18, $FF, $FF, $0F, $F0, $00 699 | byte $6E, $33, $03, $36, $7F, $0E, $06, $18, $7E, $1C, $77, $3C, $00, $03, $38, $63 700 | byte $00, $FF, $7E, $7E, $18, $0E, $00, $00, $00, $00, $00, $38, $00, $00, $00, $00 701 | 702 | long $00000000, $00000000, $00000000, $00000000, $00000000, $00000000, $00000000, $00000000 703 | long $1FE7F9FE, $00000000, $1FE7F9FE, $00000000, $00000000, $00000000, $00000006, $00000000 704 | long $00000000, $00000000, $00000000, $00000000, $00000000, $0000007C, $00000000, $00000000 705 | long $00000000, $00000000, $00000000, $00000000, $00000000, $00000000, $00000000, $00000000 706 | long $00000000, $00000000, $00000000, $00000000, $0000C030, $00000000, $00000000, $00000000 707 | long $00000000, $00000000, $00000000, $00000000, $00000018, $00000000, $00000000, $00000000 708 | long $00000000, $00000000, $00000000, $00000000, $00000000, $00000000, $00000000, $00000000 709 | long $00000000, $00000000, $00000000, $00000000, $00000000, $00000000, $00000000, $00000000 710 | long $00000000, $00000000, $00000000, $00000000, $00000000, $00000000, $00000000, $00000000 711 | long $00000000, $00000000, $00000000, $00000000, $00000000, $00000000, $00000000, $00000000 712 | long $00000000, $00038060, $00000000, $00000000, $00000000, $00000000, $00000000, $00000000 713 | long $00000000, $00000000, $00000000, $00000000, $00000000, $00000000, $00000000, $0007F800 714 | long $00000000, $00000000, $00000000, $00000000, $00000000, $00000000, $00000000, $03C19860 715 | long $00000000, $00000000, $078330CC, $00000000, $00000000, $00000000, $00000000, $00000000 716 | long $01E0300C, $0F018060, $00000000, $00000000, $00000000, $00000000, $00000000, $00000000 717 | long $00000000, $03E180C0, $00000000, $00000000, $00000000, $00000000, $00000000, $00000000 718 | long $0001F0C0, $00000000, $00000000, $00000000, $00000000, $00000000, $00000000, $00000078 719 | long $00000000, $00000000, $00000000, $00000000, $00000000, $00000000, $00000000, $00000000 720 | long $00000000, $00000000, $00000000, $00000000, $00000000, $00000000, $00000000, $00000000 721 | long $03C180C0, $00000000, $00000000, $00000000, $00000000, $00000000, $00000000, $00007036 722 | long $00000000, $00000000, $00000000, $00000000, $00000000, $00000000, $00000000, $00000000 723 | long $00000000, $00000000, $00000000, $0007C060, $000300C0, $00000000, $00000000, $00000000 724 | long $11011110, $1542A954, $17677176, $0300C030, $0300C030, $0300C030, $0D8360D8, $0D8360D8 725 | long $0300C030, $0D8360D8, $0D8360D8, $0D8360D8, $00000000, $00000000, $00000000, $0300C030 726 | long $00000000, $00000000, $0300C030, $0300C030, $00000000, $0300C030, $0300C030, $0D8360D8 727 | long $00000000, $0D8360D8, $00000000, $0D8360D8, $0D8360D8, $00000000, $0D8360D8, $00000000 728 | long $00000000, $0300C030, $0D8360D8, $00000000, $00000000, $0300C030, $0D8360D8, $0D8360D8 729 | long $0300C030, $00000000, $0300C030, $3FEFFBFF, $3FEFFBFF, $01E0781E, $3E0F83E1, $00000000 730 | long $00000000, $00000000, $00000000, $00000000, $00000000, $00000000, $00000006, $00000000 731 | long $00000000, $00000000, $00000000, $00000000, $00000000, $00000000, $00000000, $00000000 732 | long $00000000, $00000000, $00000000, $00000000, $0300C030, $00000000, $00000000, $00000000 733 | long $00000000, $00000000, $00000000, $00000000, $00000000, $00000000, $00000000, $00000000 734 | 735 | byte $00, $00, $00, $00, $00, $00, $00, $00, $FF, $00, $FF, $00, $00, $00, $00, $00 736 | byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 737 | byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 738 | byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 739 | byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 740 | byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 741 | byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 742 | byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 743 | byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 744 | byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 745 | byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 746 | byte $22, $55, $EE, $18, $18, $18, $6C, $6C, $18, $6C, $6C, $6C, $00, $00, $00, $18 747 | byte $00, $00, $18, $18, $00, $18, $18, $6C, $00, $6C, $00, $6C, $6C, $00, $6C, $00 748 | byte $00, $18, $6C, $00, $00, $18, $6C, $6C, $18, $00, $18, $FF, $FF, $0F, $F0, $00 749 | byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 750 | byte $00, $00, $00, $00, $18, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 751 | --------------------------------------------------------------------------------