quinta-feira, 30 de outubro de 2014

Recriando uma tábua digitalizadora para o MSX (update: código fonte)

Combinando o aprendido com o estudo das telas touchscreen resistivas e mais o funcionamento das tábuas digitalizadoras para o MSX, consegui recriar a minha própria. Eu recomendo fortemente a leitura dos artigos citados a fim de melhor compreender o que vai ser descrito aqui.

O circuito ficou a cargo de um PIC16F688, rodando com clock interno de 8MHz.  A PORTA A ficou inteira para o digitalizador, enquanto a PORTA  C ficou para a interface com o MSX.


O protótipo foi montado numa placa padrão. Os primeiros testes foram efetuados com um conjunto de potenciômetros, e posteriormente com o 'touchscreen'.



 O conector do sensor resistivo é um componente SMD, e teve que ser adaptado para a montagem na placa padrão.


 As abas do conector foram soldadas às ilhas e os terminais foram isolados com um pedaço de fita de kapton por baixo.

  

 


 Depois do conjunto montado, o primeiro programa usado para testar o Touchpad foi esse:


Eu notei que quando colocado sobre a mesa a detecção do toque não funcionou direito. Creio que será necessário incluir um delay entre o momento que eu ativo o resistor de pullup e a amostragem da tela. 
Com o sensor resistivo sobre o MSX a detecção fica quase normal. Fora isso, a leitura das coordenadas ficou muito boa, e a auto-calibração também funciona muito bem (vide artigo sobre a leitura do sensor resistivo)


 Em seguida fiz um programa para poder testar o funcionamento de uma forma mais visual.


E capturei o seguinte vídeo:



Ainda dá para melhorar um pouco a estabilidade da leitura, mas como prova de conceito já está OK.  O protótipo já tem todos os elementos necessários. Agora é melhorar o firmware.

Mas antes disso, o próximo passo vai ser montar o circuito numa caixa ou base.

segunda-feira, 27 de outubro de 2014

Um estudo sobre o MSX Touchpad

Recentemente alguém publicou no MSX Wiki um material sobre as pranchetas digitalizadoras para o MSX. Estes periféricos nunca foram produzidos em terras tupiniquins, o que é uma pena, pois teria sido interessante ver programas como o Graphos III com suporte a ele.

O artigo, apesar de bem interessante ainda me deixou com algumas dúvidas, e resolvi esclarecê-las a partir de uma lida no código fonte da BIOS do MSX. Minhas observações podem ser vistas neste link.

Diante do esclarecido resolvi dar uma esboçada no circuito, que utiliza um DAC modelo uPD7001, velho conhecido meu, dos modulos controladores da válvula de saída das RD350 (YPVS). 



Como tinha um chip desses em minha gaveta de componentes resolvi fazer uns testes, e usar pela primeira vez em minha vida a instrução PAD(x) do MSX Basic.



Eu aproveitei para capturar algumas formas de onda, de forma a ilustrar o funcionamento da função GTPAD da BIOS.
  • O MSX faz uma primeira leitura ao Touchpad, provavelmente para "acordar" o circuito. O sinal SI, que seleciona o canal do ADC começa no valor 0.
  • Em seguida vem uma segunda leitura para determinar se a tela está pressionada ou não, e também para selecionar o canal 0 do ADC. Caso a tela não esteja sendo pressionada, a rotina termina.

Sinais SI (Laranja) e /CS (Azul) na condição "Tela NÃO Pressionada"

  • Por outro lado, caso a tela esteja pressionada o MSX faz mais 4 leituras.


Sinais SI (Laranja) e /CS (Azul) na condição "Tela PRESSIONADA"
A tela abaixo tem um pouco mais de detalhes, portanto vou recomeçar a explicação..
  • O MSX faz uma primeira leitura ao Touchpad, provavelmente para "acordar" o circuito;
  • Em seguida vem uma segunda leitura para determinar se a tela está pressionada ou não, e também para selecionar o canal 0 do ADC;
  • A terceira leitura é a do valor X0. Enquanto esta leitura acontece, o sinal SI fica em "1" para selecionar o canal 3 para a próxima leitura;
  • Em seguida o valor de Y0 é lido com o sinal SI em zero, para selecionar o canal 0 para a próxima leitura;
  • O MSX lê então o valor de X1 e coloca SI em "1" para a próxima leitura;
  • Por último o valor de Y1 é lido e os sinais retornam ao estado normal. (faltou o "CH0" em vermelho ali na figura). 

Sinais SI (Laranja) e /CS (Azul) na condição "Tela PRESSIONADA", detalhes.
Abaixo uma foto da tela do osciloscópio (porque estas capturas são muito feinhas).



Alguns detalhes importantes:
  • O processo de leitura dura por volta de 8ms.
  • O ciclo de clock gerado pelo MSX dura 140us.
  • O sinal de clock no código fonte parece estar invertido. O texto fala que está no estado "0" quando seta um bit, e vice versa. Talvez exista um inversor entre o sinal SCK do MSX e o mesmo sinal do uPD7001. De qualquer modo o circuito funcionou deste jeito.
  • O texto do MSX Wiki fala de um inversor entre o sinal /EOC do uPD7001 e o MSX, mas eu suspeito que no circuito real exista um flip flop que é ativado pelo sinal /CS e resetado pelo sinal EOC do ADC.





domingo, 17 de agosto de 2014

Temporização dos paddles

Segue abaixo um pequeno estudo sobre a temporização do acionamento dos paddles do MSX.

A listagem abaixo contém o trecho exato da BIOS que lê os paddles. Os números marcados em azul / laranja correspondem à quantidade de ciclos de máquina que o Z80 leva para executar as instruções, já considerando o WAIT State inserido a cada ciclo M1 (padrão no MSX).

Para ler os paddles, o MSX gera um pulso no pino 8 e em seguida realiza uma contagem de tempo para ver quanto tempo o sinal do paddle permaneceu ativado. A contagem termina quando o sinal do paddle retorna a nível baixo, ou quando se atingem 255 contagens.

    PDLP1:
128B       LD A,PSG.PB
128D       DI
128E       CALL RDPSG      ; get current port B content
1291       AND E           
1292       OR D            
1293       OR C            
1294  12   OUT (PSG.DW),A  ; set trigger high
1296   5   XOR C           
1297  12   OUT (PSG.DW),A  ; set trigger low again
1299   8   LD  A,0EH       
129B  12   OUT (PSG.LW),A  
129D   8   LD  C,0         ; initialize counter
    PDL2:             
129F  12   IN  A,(PSG.DR)  
12A1   5   AND B           ; end of pulse?
12A2 13/8  JR Z,PDL3       ; yes
12A4   5   INC C           ; bump counter
12A5 13/8  JP  NZ,PDL2     ; no overflow yet
12A8       DEC C           ; make it 255
    PDL3:           
12A9       EI              
12AA       LD A,C          ; return counted value
12AB       RET 

Os números em azul são os ciclos gastos entre a subida do pulso de saída (pino 8 do conector de joystick) e a primeira leitura efetuada (para ser mais exato eu teria que reduzir os ciclos de clock referentes à busca e início da execução da instrução OUT que levanta o sinal de trigger, porém eu teria que acrescentar um tempo idêntico referente à primeira instrução IN do ciclo de leitura).

Num total são 57 ciclos, o que equivale a 15,92us num MSX rodando a 3,5795MHz. Se considerarmos apenas a borda de descida, são 17 ciclos a menos, o que resulta em 11,17us.

Os pulsos em laranja equivalem a uma contagem. Esta temporização já é velha conhecida pois consta no 'Livro Vermelho do MSX', mas como exercício não custa conferir.

Cada interação leva 43 ciclos, ou 12,01us (a 3,5795MHz). O tempo máximo para retornar da leitura do paddle é de 3,06ms (255 * 12,01us).

Com estes números em mente, surgem algumas idéias de emulação. Para isso eis um resumo do que se deve fazer:

  • Responder a uma borda de subida no pino 8 do conector de joystick em até 15us (ou uma de descida em até 11us);
  • Subir para nível alto o(s) pino(s) desejado(s) e mantê-los por um tempo entre 12us e 3ms;
  • Ao final do tempo desejado, colocar o pino em nível baixo (ou mantê-lo em nível baixo por um tempo superior a uma iteração).


terça-feira, 1 de julho de 2014

Expansor de Slots em face simples - Exercício no Kicad

Num expansor de Slots de MSX praticamente todos os fios são interconectados desde o conector de entrada ao último Slot (com exceção é claro dos sinais /SLTSL). Por isso eu nunca entendi a razão pela qual todo expansor de slot tem os chips de decodificação no "meio do caminho", ou seja, entre o conector de entrada e o primeiro slot disponível, obrigando as trilhas a fazer um malabarismo para contornar os chips e chegar aos conectores dos Slots expandidos.
Chips no meio do caminho das trilhas.

Não seria mais fácil colocar os chips no final, ou seja, depois do último slot expandido? Afinal de contas os únicos sinais gerados no circuito decodificador são os de seleção de Slots expandidos.
Chips depois do último conector.
Pensando nisso eu fiz um exercício de layout utilizando o diagrama do expansor de slots do website do Luciano (MSXPRO). Como a placa ia ficar com mais do que 10 x 8cm eu acabei utilizando o Kicad. Eis o diagrama redesenhado.
Diagrama desenhado no Kicad (sem a parte dos Slots)

E não é que no final deu pra fazer o expansor inteiro em face simples? Tudo bem que tem alguns jumpers - e metade deles são por causa da pinagem bizarra do LS273 - mas nada absurdo. E a placa nem ficou tão grande assim, ocupando aproximadamente 15 x 9cm. Se for usada como expansor interno, basta desconsiderar os conectores à direita de P5 e assim a placa vai ocupar apenas 7 x 9 cm.

Placa do expansor, desenhada no Kicad

Ainda falta fazer algumas coisas, como colocar os capacitores de desacoplamento, um LED de indicação de alimentação e alguns jumpers para permitir habilitar/desabilitar os slots (ou quem sabe usar o quadrado mágico). Mas já valeu o exercício.



domingo, 29 de junho de 2014

PPI-Mega8 para o Hotbit - Matriz de diodos

Internamente o Hotbit possui um decodificador que converte os 4 bits da porta C da PPI para 10 linhas. A linha acionada corresponde ao valor em BCD.

Para se fazer um emulador de teclado que possa ser ligado ao conector interno de teclado do Hotbit torna-se necessário então converter de volta este valor para BCD.

Embora existam alguns chips para fazer isso, mas somente os consegui encontrar sob encomenda. Daí surge uma outra solução, que é fazer um codificador usando diodos, como o da figura abaixo, porém ele possui uma desvantagem que é a quantidade enorme de diodos necessária (21). 

Codificador Decimal para BCD com diodos
Contudo é possível simplificar esta matriz levando em conta algumas características do projeto: 

  • Apenas uma das saídas fica em nível baixo por vez. 
  • O HOTBIT usa apenas 9 sinais de seleção (em vez de 10 ou 11 como outros modelos de MSX). 
  • A porta C do ATMega8 que está sendo usado para ler a matriz do teclado ainda tem duas linhas disponíveis. 
Sendo assim, podemos simplificar a matriz de diodos do codificador de forma que ela utilize apenas 8 diodos. 
Matriz codificadora Decimal para 5 pinos.

O funcionamento da matriz é bem simples:
  • Quando o sinal Y0 é acionado, somente a linha PC0 é acionada;
  • Quando o sinal Y1 é acionado, as linhas PC0 e PC1 são acionadas; 
  • Quando o sinal Y2 é acionado, somente a linha PC1 é acionada;
  • Quando o sinal Y3 é acionado, as linhas PC1 e PC2 são acionadas;
  • ... e assim por diante. . 
E a tabela de entradas/saídas da matriz fica:


   PC4 PC3 PC2 PC1 PC0  HEXA   --------TECLAS--------
Y0  1   1   1   1   0    1E    7  6  5  4  3  2  1  0 
Y1  1   1   1   0   0    1C    Ç  ¨  ´  \  =  -  9  8 
Y2  1   1   1   0   1    1D    B  A  <  /  .  ,  [  ~
Y3  1   1   0   0   1    19    J  I  H  G  F  E  D  C
Y4  1   1   0   1   1    1B    R  Q  P  O  N  M  L  K 
Y5  1   0   0   1   1    13    Z  Y  X  W  V  U  T  S 
Y6  1   0   1   1   1    17    F3 F2 F1 C0 CA GR CT SH
Y7  0   0   1   1   1    07    RE SL BS ST TA ES F5 F4
Y8  0   1   1   1   1    0F    RT DW UP LT DE IN HO SP  

O fato de ter sido utilizada uma linha a mais implica que a matriz que guarda o estado das teclas de cada linha vai precisar ter 32 entradas.

Dessa forma, a definição da variável que armazena a matriz de teclado fica:

static volatite unsigned char keymat [32] = {
  0xff ; // 00
  0xff ; // 01
  0xff ; // 02
  0xff ; // 03

  0xff ; // 04
  0xff ; // 05
  0xff ; // 06
  0xff ; // 07  - Teclas RE SL BS ST TA ES F5 F4

  0xff ; // 08
  0xff ; // 09
  0xff ; // 0a
  0xff ; // 0b

  0xff ; // 0c
  0xff ; // 0d
  0xff ; // 0e
  0xff ; // 0f  - Teclas RT DW UP LT DE IN HO SP 

  0xff ; // 10
  0xff ; // 11
  0xff ; // 12
  0xff ; // 13  - Teclas Z  Y  X  W  V  U  T  S

  0xff ; // 14
  0xff ; // 15
  0xff ; // 16
  0xff ; // 17  - Teclas F3 F2 F1 C0 CA GR CT SH

  0xff ; // 18
  0xff ; // 19  - Teclas J  I  H  G  F  E  D  C
  0xff ; // 1a
  0xff ; // 1b  - Teclas R  Q  P  O  N  M  L  K

  0xff ; // 1c  - Teclas Ç  ¨  ´  \  =  -  9  8
  0xff ; // 1d  - Teclas B  A  <  /  .  ,  [  ~
  0xff ; // 1e  - Teclas 7  6  5  4  3  2  1  0
  0xff ; // 1f

}

Quando (por exemplo) se pressiona a tecla "A" no teclado PS/2 a função principal do programa do emulador de teclado faz o seguinte:

keymat [0x1d] &= ~(1<<6 font="">

E na rotina em assembly, as mudanças são mínimas (em relação à versão que lê direto da PPI)

ISR (PCINT1_vect, ISR_NAKED) {
  asm volatile (                    // 7 até aqui
   "in __tmp_reg__,__SREG__ \n\t"   // 1 Salva registrador de Status  
   "ldi r26,lo8(Keymap) \n\t"       // 1 Ponteiro X = endereço de Keymap
   "ldi r27,hi8(Keymap)\n\t"        // 1 
   "in r19,%0 \n\t"                 // 1 Amostra bits da porta C da PPI (PC0..PC4)
   "andi r19,0x1f \n\t"             // 1 limpa bits 5..7
   "add r26,r19 \n\t"               // 1 soma com ponteiro 
   "ld r19,X \n\t"                  // 1 lê coluna correspondente do mapa de teclas
   "out %1,r19 \n\t"                // 1 escreve na porta B da PPI
                                    // até aqui 15 instruções 
   "out __SREG__,__tmp_reg__ \n\t"  // restaura registrador de Status
   "reti \n\t" 
  
   :: "I" (_SFR_IO_ADDR(PINC)), "I" (_SFR_IO_ADDR(PORTB)) ); 
   
}

Com isso, já dá pra definir o layout da placa do adaptador. Os dois conectores têm como objetivo manter o teclado interno funcionando junto com o emulador de teclado.

Adaptador interno para o teclado do Hotbit

terça-feira, 17 de junho de 2014

PPI-Mega8 - Diagrama e tabelas de constantes

Depois de algum tempo estudando e fazendo alguns experimentos com o roteamento da placa cheguei ao circuito básico do emulador de teclado para ser colocado sobre a PPI.


Esta disposição de pinos permitiu fazer ligações praticamente retas entre o AVR e a PPI de forma que a placa de circuito impressso possa ser de face simples


As linhas PC0..PC3 da PPI foram direto aos pinos PINC0..3 do AVR. Com isso apenas as colunas trocaram um pouco de lugar (PB3 foi para o bit 0, etc)

Assim a matriz de teclado mudou para

// Matrix columns reorganized for optimizing PCB layout

    7     6     5     4     3     2     1     0    BIT
 +-----------------------------------------------+                        
 ¦  4  ¦  3  ¦  2  ¦  1  ¦  0  ¦  5  ¦  6  ¦  7  ¦   Row 0                        
 +-----+-----+-----+-----+-----+-----+-----+-----¦                        
 ¦  \  ¦  =  ¦  -  ¦  9  ¦  8  ¦  ´  ¦  ¨  ¦  Ç  ¦   Row 1                        
 +-----+-----+-----+-----+-----+-----+-----+-----¦                        
 ¦  /  ¦  .  ¦  ,  ¦  [  ¦  ~  ¦  <  ¦  A  ¦  B  ¦   Row 2                        
 +-----+-----+-----+-----+-----+-----+-----+-----¦                        
 ¦  G  ¦  F  ¦  E  ¦  D  ¦  C  ¦  H  ¦  I  ¦  J  ¦   Row 3                        
 +-----+-----+-----+-----+-----+-----+-----+-----¦                        
 ¦  O  ¦  N  ¦  M  ¦  L  ¦  K  ¦  P  ¦  Q  ¦  R  ¦   Row 4                        
 +-----+-----+-----+-----+-----+-----+-----+-----¦                        
 ¦  W  ¦  V  ¦  U  ¦  T  ¦  S  ¦  X  ¦  Y  ¦  Z  ¦   Row 5                        
 +-----+-----+-----+-----+-----+-----+-----+-----¦
 ¦CODE ¦ CAP ¦GRAPH¦CTRL ¦SHIFT¦ F1  ¦ F2  ¦ F3  ¦   Row 6
 +-----+-----+-----+-----+-----+-----+-----+-----¦
 ¦STOP ¦ TAB ¦ ESC ¦ F5  ¦ F4  ¦ BS  ¦ SEL ¦ CR  ¦   Row 7
 +-----+-----+-----+-----+-----+-----+-----+-----¦
 ¦LEFT ¦ DEL ¦ INS ¦HOME ¦SPACE¦ UP  ¦DOWN ¦RIGHT¦   Row 8
 +-----------------------------------------------+
    4     3     2     1     0     5     6     7       Column

E o endereço das teclas ficou:

// ROW 0   Hexa
#define _0             0x03
#define _1             0x04
#define _2             0x05
#define _3             0x06
#define _4             0x07
#define _5             0x02
#define _6             0x01
#define _7             0x00

// ROW 1
#define _8             0x0B
#define _9             0x0C
#define _MINUS         0x0D
#define _EQUAL         0x0E
#define _BACKSLASH     0x0F
#define _ACUTE         0x0A
#define _UBER         0x09
#define _CCEDIL       0x08

// ROW 2
#define _TILDE         0x13
#define _OPENBRACKET   0x14
#define _COMMA         0x15
#define _DOT           0x16
#define _SLASH         0x17
#define _LESSTHAN     0x12
#define _A             0x11
#define _B             0x10

// ROW 3
#define _C             0x1B
#define _D             0x1C
#define _E             0x1D
#define _F             0x1E
#define _G             0x1F
#define _H             0x1A
#define _I             0x19
#define _J             0x18

// ROW 4
#define _K             0x23
#define _L             0x24
#define _M             0x25
#define _N             0x26
#define _O             0x27
#define _P             0x22
#define _Q             0x21
#define _R             0x20

// ROW 5
#define _S             0x2B
#define _T             0x2C
#define _U             0x2D
#define _V             0x2E
#define _W             0x2F
#define _X             0x2A
#define _Y             0x29
#define _Z             0x28

// ROW 6
#define _SHIFT         0x33
#define _CONTROL       0x34
#define _GRAPH         0x35
#define _CAPS         0x36
#define _CODE         0x37
#define _F1           0x32
#define _F2           0x31
#define _F3           0x30

// ROW 7
#define _F4           0x3B
#define _F5           0x3C
#define _ESC           0x3D
#define _TAB           0x3E
#define _STOP         0x3F
#define _BACKSPACE     0x3A 
#define _SELECT       0x39
#define _ENTER         0x38

// ROW 8
#define _SPACE         0x43
#define _HOME         0x44
#define _INSERT       0x45
#define _DELETE       0x46
#define _LEFT         0x47
#define _UP           0x42
#define _DOWN         0x41
#define _RIGHT         0x40

Finalmente, as tabelas de caracteres sem e com shift ficaram assim:

// Keys ordered by PS/2 Scancode
const uint8_t PS2Keymap_Normal[] PROGMEM = { 
_NONE       ,     // 0x00   
_F9         ,     // 0x01  F9
_NONE       ,     // 0x02  
_F5         ,     // 0x03  F5
_F3         ,     // 0x04  F3
_F1         ,     // 0x05  F1
_F2         ,     // 0x06  F2
_STOP       ,     // 0x07  F12           
_NONE       ,     // 0x08                
_F10        ,     // 0x09  F10           
_F8         ,     // 0x0A  F8            
_F6         ,     // 0x0B  F6            
_F4         ,     // 0x0C  F4            
_TAB        ,     // 0x0D  TAB           
_APOSTROPHE ,     // 0x0E  APOSTROPHE    '
_NONE       ,     // 0x0F                
_NONE       ,     // 0x10                
_GRAPH      ,     // 0x11  L ALT         
_SHIFT      ,     // 0x12  L SHFT        
_NONE       ,     // 0x13                
_CONTROL    ,     // 0x14  L CTRL            
_Q          ,     // 0x15  Q             
_1          ,     // 0x16  1             
_NONE       ,     // 0x17                
_NONE       ,     // 0x18                
_NONE       ,     // 0x19                
_Z          ,     // 0x1A  Z             
_S          ,     // 0x1B  S             
_A          ,     // 0x1C  A             
_W          ,     // 0x1D  W             
_2          ,     // 0x1E  2             
_NONE       ,     // 0x1F                
_NONE       ,     // 0x20                
_C          ,     // 0x21  C             
_X          ,     // 0x22  X             
_D          ,     // 0x23  D             
_E          ,     // 0x24  E             
_4          ,     // 0x25  4             
_3          ,     // 0x26  3             
_NONE       ,     // 0x27                
_NONE       ,     // 0x28                
_SPACE      ,     // 0x29  SPACE         
_V          ,     // 0x2A  V             
_F          ,     // 0x2B  F             
_T          ,     // 0x2C  T             
_R          ,     // 0x2D  R             
_5          ,     // 0x2E  5             
_NONE       ,     // 0x2F                
_NONE       ,     // 0x30                
_N          ,     // 0x31  N             
_B          ,     // 0x32  B             
_H          ,     // 0x33  H             
_G          ,     // 0x34  G             
_Y          ,     // 0x35  Y             
_6          ,     // 0x36  6             
_NONE       ,     // 0x37                
_NONE       ,     // 0x38                
_NONE       ,     // 0x39                
_M          ,     // 0x3A  M             
_J          ,     // 0x3B  J             
_U          ,     // 0x3C  U             
_7          ,     // 0x3D  7             
_8          ,     // 0x3E  8             
_NONE       ,     // 0x3F                
_NONE       ,     // 0x40                
_COMMA      ,     // 0x41  COMMA         ,
_K          ,     // 0x42  K             
_I          ,     // 0x43  I             
_O          ,     // 0x44  O             
_0          ,     // 0x45  0             
_9          ,     // 0x46  9             
_NONE       ,     // 0x47                
_NONE       ,     // 0x48                
_DOT        ,     // 0x49  DOT           .
_SEMICOLON  ,     // 0x4A  SEMICOLON     ;
_L          ,     // 0x4B  L             
_CCEDIL     ,     // 0x4C  CCCEDIL       Ç
_P          ,     // 0x4D  P             
_MINUS      ,     // 0x4E  MINUS         -
_NONE       ,     // 0x4F  
_NONE       ,     // 0x50  
_SLASH      ,     // 0x51  SLASH         /
_TILDE      ,     // 0x52  TILDE         ~
_NONE       ,     // 0x53  
_ACUTE      ,     // 0x54  ACUTE         `
_EQUAL      ,     // 0x55  EQUAL         =
_NONE       ,     // 0x56  
_NONE       ,     // 0x57  
_CAPS       ,     // 0x58  CAPS
_SHIFT      ,     // 0x59  R SHFT
_ENTER      ,     // 0x5A  ENTER
_OPENBRACKET,     // 0x5B  OPENBRACKET   [
_NONE       ,     // 0x5C  
_CLOSEBRACKET,    // 0x5D  CLOSEBRACKET  ]
_NONE       ,     // 0x5E  
_NONE       ,     // 0x5F  
_NONE       ,     // 0x60  
_BACKSLASH  ,     // 0x61  BACKSLASH     
_NONE       ,     // 0x62  
_F7         ,     // 0x63  F7 (relocated from 0x83 to keep table under 128 chars
_NONE       ,     // 0x64  
_NONE       ,     // 0x65        
_BACKSPACE  ,     // 0x66  BKSP
_NONE       ,     // 0x67  
_NONE       ,     // 0x68  
_1          ,     // 0x69  KP1
_NONE       ,     // 0x6A  
_4          ,     // 0x6B  KP4
_7          ,     // 0x6C  KP7
_DOT        ,     // 0x6D  KPDOT         .
_NONE       ,     // 0x6E  
_NONE       ,     // 0x6F  
_0          ,     // 0x70  KP0
_COMMA      ,     // 0x71  KPCOMMA       ,
_2          ,     // 0x72  KP2
_5          ,     // 0x73  KP5
_6          ,     // 0x74  KP6
_8          ,     // 0x75  KP8
_ESC        ,     // 0x76  ESC
_NONE       ,    // 0x77  NUM
_SELECT     ,     // 0x78  F11
_PLUS       ,     // 0x79  KPPLUS        +
_3          ,     // 0x7A  KP3
_MINUS      ,     // 0x7B  KPMINUS       -
_ASTERISK   ,     // 0x7C  KPTIMES       *
_9          ,     // 0x7D  KP9
_NONE       ,     // 0x7E  SCROLL
_NONE       ,     // 0x7F  
 };
/*
_NONE       ,     // 0x80  
_NONE       ,     // 0x81  
_NONE       ,     // 0x82  
_F7             // 0x83  F7
 };
*/

const uint8_t PS2Keymap_Shifted[] PROGMEM =
_NONE       ,      // 0x00   
_F9         ,      // 0x01  
_NONE       ,      // 0x02  
_F5         ,      // 0x03  
_F3         ,      // 0x04  
_F1         ,      // 0x05  
_F2         ,      // 0x06  
_SELECT     ,      // 0x07  
_NONE       ,      // 0x08  
_F10        ,      // 0x09  
_F8         ,      // 0x0A  
_F6         ,      // 0x0B  
_F4         ,      // 0x0C  
_TAB        ,      // 0x0D  
_QUOTE      ,      // 0x0E   QUOTE      "
_NONE       ,      // 0x0F              
_NONE       ,      // 0x10              
_GRAPH      ,      // 0x11              
_SHIFT      ,      // 0x12              
_NONE       ,      // 0x13              
_CONTROL    ,      // 0x14              
_Q          ,      // 0x15              
_EXCLAMATION,      // 0x16  EXCLAMATION !
_NONE       ,      // 0x17              
_NONE       ,      // 0x18              
_NONE       ,      // 0x19              
_Z          ,      // 0x1A              
_S          ,      // 0x1B              
_A          ,      // 0x1C              
_W          ,      // 0x1D              
_AT         ,      // 0x1E  AT          @
_NONE       ,      // 0x1F              
_NONE       ,      // 0x20              
_C          ,      // 0x21              
_X          ,      // 0x22              
_D          ,      // 0x23              
_E          ,      // 0x24              
_DOLLAR     ,      // 0x25  DOLLAR      $
_NUMBER     ,      // 0x26  NUMBER      #
_NONE       ,      // 0x27              
_NONE       ,      // 0x28              
_SPACE      ,      // 0x29              
_V          ,      // 0x2A              
_F          ,      // 0x2B              
_T          ,      // 0x2C              
_R          ,      // 0x2D              
_PERCENT    ,      // 0x2E  PERCENT     %
_NONE       ,      // 0x2F              
_NONE       ,      // 0x30              
_N          ,      // 0x31              
_B          ,      // 0x32              
_H          ,      // 0x33              
_G          ,      // 0x34              
_Y          ,      // 0x35              
_UBER       ,      // 0x36  UBER        ¨
_NONE       ,      // 0x37              
_NONE       ,      // 0x38              
_NONE       ,      // 0x39              
_M          ,      // 0x3A              
_J          ,      // 0x3B              
_U          ,      // 0x3C              
_AMPERSAND  ,      // 0x3D  AMPERSAND   &
_ASTERISK   ,      // 0x3E  ASTERISK    *
_NONE       ,      // 0x3F              
_NONE       ,      // 0x40              
_LESSTHAN   ,      // 0x41  LESSTHAN    <
_K          ,      // 0x42              
_I          ,      // 0x43              
_O          ,      // 0x44              
_CLOSEBRACE ,      // 0x45  CLOSEBRACE  )
_OPENBRACE  ,      // 0x46  OPENBRACE   (
_NONE       ,      // 0x47              
_NONE       ,      // 0x48              
_GREATERTHAN,      // 0x49  GREATERTHAN >
_COLON      ,      // 0x4A  COLON       :
_L          ,      // 0x4B              
_CCEDIL    ,      // 0x4C              
_P          ,      // 0x4D              
_UNDERSCORE ,      // 0x4E  UNDERSCORE  _
_NONE       ,      // 0x4F              
_NONE       ,      // 0x50              
_QUESTION   ,      // 0x51  QUESTION    ?
_CIRCUMFLEX ,      // 0x52  CIRCUMFLEX  ^
_NONE       ,      // 0x53              
_GRAVE      ,      // 0x54  GRAVE       `
_PLUS       ,      // 0x55  PLUS        +
_NONE       ,      // 0x56              
_NONE       ,      // 0x57              
_CAPS       ,      // 0x58              
_SHIFT      ,      // 0x59              
_ENTER      ,      // 0x5A              
_OPENKEY    ,      // 0x5B  OPENKEY     {
_NONE       ,      // 0x5C              
_CLOSEKEY   ,      // 0x5D  CLOSEKEY    }
_NONE       ,      // 0x5E              
_NONE       ,      // 0x5F              
_NONE       ,      // 0x60              
_PIPE       ,      // 0x61  PIPE        |
_NONE       ,      // 0x62              
_F7         ,      // 0x63  F7 (relocated from 0x83 to keep table under 128 chars            
_NONE       ,      // 0x64  
_NONE       ,      // 0x65  
_BACKSPACE  ,      // 0x66  
_NONE       ,      // 0x67  
_NONE       ,      // 0x68  
_1          ,      // 0x69  
_NONE       ,      // 0x6A  
_4          ,      // 0x6B  
_7          ,      // 0x6C  
_DOT        ,      // 0x6D  
_NONE       ,      // 0x6E  
_NONE       ,      // 0x6F  
_0          ,      // 0x70  
_COMMA      ,      // 0x71  
_2          ,      // 0x72  
_5          ,      // 0x73  
_6          ,      // 0x74  
_8          ,      // 0x75  
_ESC        ,      // 0x76  
_NONE       ,      // 0x77  
_SELECT     ,      // 0x78  
_PLUS       ,      // 0x79  
_3          ,      // 0x7A  
_MINUS      ,      // 0x7B  
_ASTERISK   ,      // 0x7C  
_9          ,      // 0x7D  
_NONE       ,      // 0x7E  
_NONE       ,      // 0x7F  
 };