Turing Bombe

In 1936, the scientist Alan Turing in his publication “On Computable Numbers, With An Application to Entscheidungsproblem”describes the use of a universal computing machine that could put an end to the problem of decidability in mathematics. As a result, he comes to the conclusion that such a machine would be nothing could solve correctly, if the result of her work would be inverted and looped on itself.It turns out that * ideal * antivirus cannot be created, * ideal * tile layer too, a program that suggests ideal phrases for your crash, etc. Paradox, sir!

However, this universal computing machine can be used to implement any algorithm, which was used by British intelligence, hiring Turing and allowing to create a “Bombe” machine for decrypting German messages during the Second World War.

The following is an example of OOP modeling a single-tape calculator in Dart, based on the original document.

The Turing machine consists of a tape divided into sections, each section contains a symbol, symbols can be read or written. Film class example:

class MapInfiniteTape implements InfiniteTape { 
final _map = Map<int, String>(); 

  String read({required int at}) { 
    return _map[at] ?? ""; 
  } 

  void write({required String symbol, required int at}) { 
    _map[at] = symbol; 
  } 
}

There is also a “scanning square”, it can move along the film, read or write information, in modern language – a magnetic head. Magnetic head class example:

class TapeHead { 
  int _index = 0; 
  InfiniteTape _infiniteTape; 
  TapeHead(this._infiniteTape) {} 

  String next() { 
    _index += 1; 
    move(to: _index); 
    final output = read(); 
    return output; 
  } 

  String previous() { 
    _index -= 1; 
    move(to: _index); 
    final output = read(); 
    return output; 
  } 

  void move({required int to}) { 
    this._index = to; 
  } 

  String read() { 
    return _infiniteTape.read(at: this._index); 
  } 

  void write(String symbol) { 
    _infiniteTape.write(symbol: symbol, at: this._index); 
  } 

  int index() { 
    return _index; 
  } 
} 

The machine contains “m-configurations” by which it can decide what to do next. In modern parlance, states and state handlers. State handler example:

class FiniteStateControl { 
  FiniteStateControlDelegate? delegate = null; 

  void handle({required String symbol}) { 
    if (symbol == OPCODE_PRINT) { 
      final argument = delegate?.nextSymbol(); 
      print(argument);
    } 
    else if (symbol == OPCODE_GENERATE_RANDOM_NUMBER_FROM_ZERO_TO_AND_WRITE_AFTER) { 
      final to = int.tryParse(delegate!.nextSymbol())!; 
      final value = new Random().nextInt(to); 
      delegate!.nextSymbol(); 
      delegate!.write(value.toString()); 
    } 
    else if (symbol == OPCODE_INPUT_TO_NEXT) { 
      final input = stdin.readLineSync()!; 
      delegate?.nextSymbol(); 
      delegate?.write(input); 
    } 
    else if (symbol == OPCODE_COPY_FROM_TO) { 
      final currentIndex = delegate!.index(); 

и т.д. 

After that, you need to create “configurations”, in modern language these are operation codes (opcodes), their handlers. Example opcodes:

const OPCODE_STOP = "stop"; 
const OPCODE_PRINT = "print"; 
const OPCODE_INCREMENT_NEXT = "increment next"; 
const OPCODE_DECREMENT_NEXT = "decrement next"; 
const OPCODE_IF_PREVIOUS_NOT_EQUAL = "if previous not equal"; 
const OPCODE_MOVE_TO_INDEX = "move to index"; 
const OPCODE_COPY_FROM_TO = "copy from index to index"; 
const OPCODE_INPUT_TO_NEXT = "input to next"; 
const OPCODE_GENERATE_RANDOM_NUMBER_FROM_ZERO_TO_AND_WRITE_AFTER = "generate random number from zero to next and write after"; 

Don’t forget to create an opcode and a breakpoint handler, otherwise you won’t be able to prove or not prove (sic!) The resolution problem.

Now, using the “mediator” pattern, we connect all the classes in the Turing Machine class, create an instance of the class, record the programs through the tape recorder, load the cassette and you can use it!

For me personally, the question remained interesting, what was primary – the creation of a universal calculator or the proof of the “Entscheidungsproblem” as a result of which, as a by-product, a calculator appeared.

Cassettes

For the sake of entertainment, I recorded several cassette programs for my version of the car.

Hello World

print 
hello world 
stop

Считаем до 16-ти

increment next
0
if previous not equal
16
copy from index to index
1
8
print
?
move to index
0
else
copy from index to index
1
16
print
?
print
Finished!
stop

The most interesting challenge was writing Quine a program that prints its source code for a single tape machine. For the first 8 hours it seemed to me that this problem could not be solved with such a small number of opcodes, but after only 16 hours it turned out that I was wrong.

Implementation and examples of cassettes, sources below.

Links

https://gitlab.com/demensdeum/turing-machine

References

https://www.astro.puc.cl/~rparra/tools/PAPERS/turing_1936.pdf
https://kpolyakov.spb.ru/prog/turing.htm
https://www.youtube.com/watch?v=dNRDvLACg5Q
https://www.youtube.com/watch?v=jP3ceURvIYc
https://www.youtube.com/watch?v=9QCJj5QzETI
https://www.youtube.com/watch?v=HeQX2HjkcNo&t=0s

Writing stuff in Assembly for Sega Genesis #5

In this post I will describe the process of reading the joystick, changing the position of the sprite, horizontal flip, the Sega Genesis emulator and potentially the console itself.

Reading of clicks, processing of “events” of the shogi joystick is carried out according to the following scheme:

  1. Request a combination of bits of pressed buttons
  2. Reading the bits of pressed buttons
  3. Processing at the level of game logic

To move the skeleton sprite, we need to store the variables of the current position.

RAM

The variables of the game logic are stored in RAM, so far people have not come up with anything better. Let’s declare the addresses of the variables, change the rendering code:

skeletonXpos = $FF0000
skeletonYpos = $FF0002 
frameCounter = $FF0004 
skeletonHorizontalFlip = $FF0006

    move.w #$0100,skeletonXpos 
    move.w #$0100,skeletonYpos 
    move.w #$0001,skeletonHorizontalFlip 

FillSpriteTable: 
    move.l #$70000003,vdp_control_port 
    move.w skeletonYpos,vdp_data_port  
    move.w #$0F00,vdp_data_port 
    move.w skeletonHorizontalFlip,vdp_data_port 
    move.w skeletonXpos,vdp_data_port 

As you can see, the address available for work starts with 0xFF0000, and ends at 0xFFFFFF, in total we have 64 KB of memory available. Skeleton positions are declared at skeletonXpos, skeletonYpos, horizontal flip at skeletonHorizontalFlip.

Joypad

By analogy with VDP, work with joypads occurs through two ports separately – the control port and the data port, for the first it is 0xA10009 and 0xA10003 so-but. When working with a joypad, there is one interesting feature – first you need to request a combination of buttons for polling, and then, after waiting for the update via the bus, read the necessary clicks. For the C / B buttons and the cross, this is 0x40, an example is below:

ReadJoypad: 
  move.b #$40,joypad_one_control_port; C/B/Dpad 
  nop ; bus sync 
  nop ; bus sync 
  move.b joypad_one_data_port,d2 
  rts 

In register d2, the state of the buttons pressed, or not pressed, in general, what was requested through the date port, will remain. After that, go to the Motorola 68000 register viewer of your favorite emulator, see what the d2 register is, depending on the keystrokes. In a smart way, you can find out in the manual, but we do not believe a word. Further processing of pressed buttons in register d2

HandleJoypad:  
    cmp #$FFFFFF7B,d2; handle left 
    beq MoveLeft  
    cmp #$FFFFFF77,d2; handle right  
    beq MoveRight  
    cmp #$FFFFFF7E,d2; handle up  
    beq MoveUp  
    cmp #$FFFFFF7D,d2; handle down  
    beq MoveDown  
    rts

Of course, you need to check individual bits, not whole words, but for now it will do. Now the simplest thing is left – to write handlers for all events of movement in 4 directions. To do this, we change the variables in RAM, and start the repainting procedure.

Example for moving left + changing horizontal flip:

MoveLeft:  
    move.w skeletonXpos,d0 
    sub.w #1,d0 
    move.w d0,skeletonXpos 
    move.w #$0801,skeletonHorizontalFlip 
    jmp FillSpriteTable

After adding all the handlers and assemblies, you will see how the skeleton moves and rotates around the screen, but too fast, faster than Sonic the hedgehog itself.

Not so fast!

To slow down the speed of the game loop, there are several techniques, I chose the simplest and not affecting work with external ports – counting a digit through a register until it becomes zero.

An example of a slowing loop and a game loop:

StartWaitFrame: 
  move.w #512,frameCounter 
WaitFrame: 
  move.w frameCounter,d0 
  sub.w #1,d0 
  move.w d0,frameCounter 
  dbra d0,WaitFrame 
GameLoop: 
  jsr ReadJoypad 
  jsr HandleJoypad 
  jmp GameLoop 

After that, the skeleton runs slower, which is required. As far as I know, the most common variant of “slowing down” is counting the vertical sync flag, you can count how many times the screen has been rendered, thus binding to a specific fps.

Links

https://gitlab.com/demensdeum/segagenesissamples/-/blob/main/8Joypad/vasm/main.asm

References

https://www.chibiakumas.com/68000/platform2.php
https://huguesjohnson.com/programming/genesis/tiles-sprites/

Writing stuff in Assembly for Sega Genesis #4

In this post I will describe how to draw sprites using the Sega Genesis VDP emulator.
The process for rendering sprites is very similar to rendering tiles:

  1. Loading colors into CRAM
  2. Upload parts of 8×8 sprites to VRAM
  3. Filling the Sprite Table in VRAM

For example, let’s take a skeleton sprite with a sword 32×32 pixels


Skeleton Guy [Animated] by Disthorn

CRAM

Let’s use ImaGenesis to convert it to CRAM colors and VRAM patterns for assembler. After that we get two files in asm format, then we rewrite the colors to the word size, and the tiles must be put in the correct order for rendering.
Interesting information: you can switch the VDP auto-increment through register 0xF to word size, this will remove the address increment from the CRAM color fill code.

VRAM

The shogi manula has the correct tile order for large sprites, but we’re smarter, so we’ll take the indexes from the blog ChibiAkumas , let’s start counting from index 0:

0 4 8 12

1 5 9 13

2 6 10 14

3 7 11 15

Why is everyone upside down? And what do you want, because the prefix is ​​Japanese! It could have been from right to left!
Let’s manually change the order in the sprite asm file:

Sprite: 
	dc.l	$11111111	; Tile #0 
	dc.l	$11111111 
	dc.l	$11111111 
	dc.l	$11111111 
	dc.l	$11111111 
	dc.l	$11111111 
	dc.l	$11111111 
	dc.l	$11111111 
	dc.l	$11111111	; Tile #4 
	dc.l	$11111111 
	dc.l	$11111111 
	dc.l	$11111111 
	dc.l	$11111111 
	dc.l	$11111111 
	dc.l	$11111111 
	dc.l	$11111111
	dc.l	$11111111	; Tile #8 
	dc.l	$11111111 
	dc.l	$11111111 
	dc.l	$11111111 
	dc.l	$11111111 
	dc.l	$11111122 
	dc.l	$11111122 
	dc.l	$11111166 
	dc.l	$11111166	; Tile #12 
	dc.l	$11111166 
	dc.l	$11111166 
	и т.д. 

Load the sprite like regular tiles / patterns:

SpriteVRAM: 
  lea Sprite,a0 
  move.l #$40200000,vdp_control_port; write to VRAM command 
  move.w #128,d0 ; (16*8 rows of sprite) counter 
SpriteVRAMLoop: 
  move.l (a0)+,vdp_data_port; 
  dbra d0,SpriteVRAMLoop 

To draw the sprite, it remains to fill the Sprite Table

Sprite Table

The sprite table is filled in VRAM, the address of its location is put in the VDP register 0x05, the address is again tricky, you can see it in the manual, an example for the F000 address:

dc.b $78 ; 0x05:  Sprite table at VRAM 0xF000 (bits 0-6 = bits 9-15) 

Ok, now let’s write our sprite to the table. To do this, you need to fill in the data “structure” consisting of four words. You can find a binary description of the structure in the manual. Personally, I made it easier, the sprite sheet can be edited manually in the Exodus emulator.

The structure parameters are obvious from the name, for example XPos, YPos – coordinates, Tiles – starting tile number for drawing, HSize, VSize – sprite sizes by adding 8×8 parts, HFlip, VFlip – hardware horizontal and vertical sprite rotations.

It is very important to remember that sprites can be off-screen, this is the correct behavior. unloading off-screen sprites from memory is quite a resource-intensive task.
After filling in the data in the emulator, they need to be copied from VRAM at 0xF000, Exodus also supports this feature.
By analogy with drawing tiles, first we turn to the VDP control port to start recording at 0xF000, then write the structure to the data port.
Let me remind you that the description of VRAM addressing can be read in the manual, or in the blog Nameless Algorithm .

In short, VDP addressing works like this:
[..DC BA98 7654 3210 …. …. …. ..FE]
Where hex is the position of the bit in the desired address. The first two bits are the type of the requested command, for example 01 – write to VRAM. Then for the address 0XF000 it turns out:
0111 0000 0000 0000 0000 0000 0000 0011 (70000003)

As a result, we get the code:

SpriteTable: 
  move.l #$70000003,vdp_control_port 
  move.w #$0100,vdp_data_port 
  move.w #$0F00,vdp_data_port 
  move.w #$0001,vdp_data_port 
  move.w #$0100,vdp_data_port 

After that, the skeleton sprite will be displayed at coordinates 256, 256. Cool yes?

Links

https://gitlab.com/demensdeum/segagenesissamples/-/tree/main/7Sprite/vasm
https://opengameart.org/content/skeleton-guy-animated

References

https://namelessalgorithm.com/genesis/blog/vdp/
https://www.chibiakumas.com/68000/platform3.php#LessonP27
https://plutiedev.com/sprites