News:

Printed Amstrad Addict magazine announced, check it out here!

Main Menu
avatar_SerErris

ChatGPT Hello World in Assembler

Started by SerErris, 14:15, 20 January 23

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

SerErris

That was just a fun experiment, but you can acutally get somewhere - however you need to know already a lot or chatGPT needs to learn.

So what did I do?
I asked chatgpt to write a Z80 assembler routine for me that can print a string to the screen on Amstrad CPC.

Here is what the discussion with ChatGPT was:

Me: can you write a simple print string routine in Z80 assembler for the amstrad cpc?

ChatGPT: Sure, here's an example of a simple routine written in Z80 assembly that can be used to print a null-terminated string on the screen of an Amstrad CPC:

print:
    ld hl, message    ; load the address of the message into HL
    ld de, &bc00      ; load the address of the screen memory into DE
    ld bc, &c000      ; set the number of characters to print

print_loop:
    ld a, (hl)        ; load the next character to print
    ld (de), a        ; store the character in screen memory
    inc hl            ; increment the message pointer
    inc de            ; increment the screen memory pointer
    djnz print_loop   ; repeat until all characters have been printed
    ret

message:
    db "Hello, World!", 0


This function will not work with the amstrad cpc as this computer uses a bitmap based screen. to print a character to the screen you need to call a function at &bb5a that needs the character in register a as input.
You are correct, my apologies for the mistake. The Amstrad CPC uses a bitmap-based screen, and the method of printing characters to the screen is different than what I described. To print a character on the Amstrad CPC, you would need to call a firmware function at &bb5a and pass the character to be printed in the A register.
Here's an example of how the print routine would look like :



print:
    ld hl, message    ; load the address of the message into HL
    ld de, &bc00      ; load the address of the screen memory into DE

print_loop:
    ld a, (hl)        ; load the next character to print
    or a              ; check if the character is zero
    jr z, done        ; if zero, the message has been printed
    ld (de), a        ; store the character in screen memory
    inc hl            ; increment the message pointer
    inc de            ; increment the screen memory pointer
    jr print_loop    ; repeat until all characters have been printed
done:
    ret

message:
    db "Hello, World!", 0

That is not correct - it needs to load a character of the string into A and then call &bb5a for each character

You are correct, my apologies for the mistake. To print a string on the Amstrad CPC, you would need to load each character of the string into the A register and then call the firmware function at &bb5a to print it. Here's an example of how the print routine would look like:


print:
    ld hl, message    ; load the address of the message into HL
    call &bb5a        ; call the firmware function to print the character
    inc hl            ; increment the message pointer
    ld a, (hl)
    or a              ; check if the character is zero
    jr z, done        ; if zero, the message has been printed
    jp print          ; repeat until all characters have been printed
done:
    ret

message:
    db "Hello, World!", 0
Actually ... that looks already good. Now make it a little bit more readable - add a symbol:

That is the correct result, but please assign the address &bb5a to the symbol TXT_OUTPUT
You are correct, my apologies. Here's the corrected version of the print routine:

print:
    ld hl, message    ; load the address of the message into HL

print_loop:
    ld a, (hl)        ; load the next character to print
    or a              ; check if the character is zero
    jr z, done        ; if zero, the message has been printed
    call TXT_OUTPUT   ; call the firmware function to print the character
    inc hl            ; increment the message pointer
    jr print_loop     ; repeat until all characters have been printed
done:
    ret

message:
    db "Hello, World!", 0
TXT_OUTPUT equ &bb5a
please put the symbol assignments in the front of the code

Sure, here's the corrected version of the print routine with the symbol assignments at the beginning of the code:

TXT_OUTPUT equ &bb5a

print:
    ld hl, message    ; load the address of the message into HL

print_loop:
    ld a, (hl)        ; load the next character to print
    or a              ; check if the character is zero
    jr z, done        ; if zero, the message has been printed
    call TXT_OUTPUT   ; call the firmware function to print the character
    inc hl            ; increment the message pointer
    jr print_loop     ; repeat until all characters have been printed
done:
    ret

message:
    db "Hello, World!", 0


So that was a task of exactly 5 Minutes - so chatGPT supported me to create that function without me needing to type any assembly language.

I am pretty sure I could have done it faster myself, but it is an interesting concept.

However I failed to write a sqrt routine. It used invalid instructions (such as LD BC,HL) and also had no meaningfull calculation. But I trained it now with a working routine, so anyone asking now for a sqrt for z80 should get the correct answer.

What do you think about the potential for ChatGPT?
Proud owner of 2 Schneider CPC 464, 1 Schneider CPC 6128, GT65 and lots of books
Still learning all the details on how things work.

roudoudou

ld bc,hl can be splited into ld b,h : ld c,l and this is supported by "many" modern z80 assemblers
My pronouns are RASM and ACE

SerErris

#2
I do know that, but this should not be in any output without knowledge of the assembler.

Also LD (ADDR),B is not supported at all.



Can someone try in ChatGPT what the answer is on this question for them?write a simple print string routine in Z80 assembler for the amstrad cpc?
Proud owner of 2 Schneider CPC 464, 1 Schneider CPC 6128, GT65 and lots of books
Still learning all the details on how things work.

Prodatron

ChatGPT isn't really good in Z80 assembler. E.g. it is quite convinced, that there is a MUL command in Z80. But you are able to force him to write a multiplication routine without MUL command, which is still buggy, but at least it tries to use normal ADD and a loop.
It seems, that ChatGPT is a little bit more experienced in 6502 programming.
It is already quite ok in Locomotive Basic.
I really wonder, why it has any knowledge about programming these very old CPUs at all. Did they feed it with the complete collection of all Github repositories?

GRAPHICAL Z80 MULTITASKING OPERATING SYSTEM

SerErris

Yep and it cannot do SQRT routine, even if it is very simple.

That is the most simple (not fastest, or best optimized in any way) routine to do a square root.

;-------------------------------
;Square Root
;Inputs:
;HL = number to be square rooted
;Outputs:
;A  = square root

sqrt:
   ld a,$ff
   ld de,1
sqrtloop:
   inc a
   dec e
   dec de
   add hl,de
   jr c,sqrtloop
   ret


The code from chatgpt is simple unusable ...

it was something like

sqrt:
  ld HL,value
  LD A,(HL)
  LD BC,0
  LD D,0

loop:
  add HL,BC
  inc C
  djnz loop
  LD (result),C

value: dw 0
result: db0

So it is pure nonsense .. no meaning at all in it. And obviously it will never get the right answer ;-)
Proud owner of 2 Schneider CPC 464, 1 Schneider CPC 6128, GT65 and lots of books
Still learning all the details on how things work.

zhulien

"in z80 assembler code a 0 terminated string output function given a single API call that prints a character in register A at #bb5a"

; In this code, the string to be printed is defined at the memory location specified by the
; string label. The LD HL, string instruction loads the address of the string into the HL
; register pair.

; The LOOP label represents the main loop that reads each character from the string and
; outputs it to the console using the CALL #bb5a instruction. The OR A instruction checks if
; the current character is null, and if it is, the code jumps to the DONE label to exit the
; program. If the current character is not null, it is output to the console and the string pointer
; is incremented using the INC HL instruction.

; Note that the address #bb5a is assumed to be the correct address for the console output
; API in this example. If this address is not correct for your particular system or API, you will
; need to replace it with the correct address. Additionally, you can modify the string to be
; printed by changing the contents of the string label.

; Define the null-terminated string to be printed
string:
    DB 'Hello, world!', 0
   
; Load the address of the string into the HL register pair
LD HL, string

; Loop through the string, printing each character until the null terminator is reached
LOOP:
    LD A, (HL) ; Load the next character from the string
    OR A ; Check if the character is null
    JP Z, DONE ; If it is, jump to the end of the program
    CALL #bb5a ; Otherwise, print the character to the console
    INC HL ; Increment the string pointer
    JP LOOP ; Continue to the next character

; End of program
DONE:
    RET

zhulien

"in z80 assembler code a function that reverses a 0 terminated string"

; Here's an example Z80 assembly code for a function that reverses a null-terminated string:
; To use this function, you would first load the address of the null-terminated string into the
; HL register, and then call the reverse_string function. After the function completes, the
; string will be reversed in place, and the original pointer to the beginning of the string will
; still be stored in the HL register.

reverse_string:
  push af       ; Save the accumulator on the stack
  push hl       ; Save the string pointer on the stack

  ; Find the end of the string
  ld hl, 0      ; Set HL to zero
find_end:
  ld a, (hl)    ; Load the byte at HL into the accumulator
  or a          ; OR the accumulator with itself to set the zero flag if A is zero
  jr z, end     ; Jump to the end of the function if A is zero
  inc hl        ; Increment HL
  jr find_end   ; Repeat until the end of the string is found

  ; Reverse the string
end:
  ld de, -1     ; Set DE to -1 (to account for the null terminator)
  ld bc, 0      ; Set BC to zero
  add hl, de    ; Subtract 1 from HL to point to the last character before the null terminator
reverse_loop:
  ld a, (hl)    ; Load the byte at HL into the accumulator
  ld (bc), a    ; Store the byte at BC (the reversed position)
  dec hl        ; Decrement HL to move to the previous character in the string
  inc bc        ; Increment BC to move to the next reversed position
  cp 0          ; Compare the accumulator with zero
  jr nz, reverse_loop  ; Repeat until the beginning of the string is reached

  pop hl        ; Restore the original string pointer
  pop af        ; Restore the original accumulator value
  ret           ; Return from the function

zhulien

"in z80 assembler code a function that compares two 0 terminated strings"

; Here's an example Z80 assembly code for a function that compares two null-terminated strings:

; To use this function, you would load the addresses of the two null-terminated strings into
; the HL and BC registers, respectively, and then call the compare_strings function. If the
; strings are equal, the function will return with a zero value in the accumulator. If the strings
; are not equal, the function will return with a non-zero value in the accumulator.

compare_strings:
  push af       ; Save the accumulator on the stack
  push bc       ; Save the second string pointer on the stack
  push hl       ; Save the first string pointer on the stack

  ; Compare the strings
compare_loop:
  ld a, (hl)    ; Load the byte at HL (the first string) into the accumulator
  cp (bc)       ; Compare the byte at BC (the second string) with the accumulator
  ret z         ; Return zero if the strings are equal (the null terminator has been reached)
  jr nz, strings_not_equal  ; Jump to strings_not_equal if the strings are not equal
  inc hl        ; Increment HL (the first string pointer)
  inc bc        ; Increment BC (the second string pointer)
  jr compare_loop  ; Repeat until the end of one of the strings is reached

  ; Strings are not equal
strings_not_equal:
  pop hl        ; Restore the first string pointer
  pop bc        ; Restore the second string pointer
  pop af        ; Restore the original accumulator value
  ret           ; Return from the function with a non-zero value in the accumulator


zhulien

amazing stuff, we can now code much much faster and don't need to think too much about these little util functions - but we do need to construct the sentences in a way that is not too ambiguous and give it hints like the APIs to use.  change z80 to 6502 and you got 6502 code :D

it's also an excellent learning tool, but I wouldn't only use chatgpt to learn.

d_kef

I think it'll take some time to reach excellency.
The compare string routine will crash if the strings are equal as it returns without poping back the registers it pushed to the stack.
Furthermore it'll only compare the first character of the two strings.
And if I'm not mistaken the code will not compile because I don't think there is a cp (bc) in z80.

Unless I've drunk too much already and I'm saying nonsense....

d_kef



Axelay

That string reversal function also looks to be a bit of a disaster.  Unless I am mistaken, it doesn't operate on the string you ask it to, doesn't reverse the 'string' it does operate on, and doesn't return from the routine except by chance.

To go back to the question in the original post about what potential I see for ChatGPT - for myself, in terms of Z80 coding, there is no potential whatsoever.  I don't write Z80 code because I like to make games.  I make games because I like to write Z80 code, and for me games present the most interesting challenges.  I have no interest in an 'AI' writing Z80 code for me, even if it was capable of writing good code, let alone functional code.

zhulien

Trying to come up with a standard format to describe requirements...

In z80 assembler code the following function.

Function name:
- str_out

Purpose:
- Print a 0 terminated string.

Entry:
- Register HL points to the 0 terminated string.

Exit:
- None

Rules:
- Any labels within the new function, suffix the function name with a counter.

API's available:
- txt_out_char is at 0xbb5a. Call txt_out_char prints a single character. Entry: Register A contains the character to output. Exit: All registers are corrupted.

Gives:

; Explanation:
;
; The function starts by loading the first character of the string pointed to by HL into register A.
; It checks if the character is the null terminator by performing a bitwise OR with itself. If the result is zero (i.e. the character is the null terminator), it returns from the function.
; If the character is not the null terminator, it pushes the current value of HL onto the stack to save the current string pointer.
; It then calls the txt_out_char function to output the current character in register A.
; After the character is output, it pops the original value of HL from the stack to restore the string pointer.
; It then increments HL to move the pointer to the next character in the string and jumps back to the beginning of the function to process the next character.

str_out:
    ld a, (hl) ; Load the first character of the string into A
    or a      ; Check if it is the null terminator
    ret z     ; Return if it is

    push hl   ; Save the current string pointer
    call txt_out_char ; Output the current character
    pop hl    ; Restore the current string pointer

    inc hl    ; Move the pointer to the next character
    jr str_out ; Loop back to the beginning of the function


zhulien

In z80 assembler code the following function.

Function name:
- str_reverse

Purpose:
- Reverses a 0 terminated string.

Entry:
- Register HL points to the 0 terminated string.

Exit:
- HL points to the reversed string.

Rules:
- Any labels within the new function, suffix the function name with a counter.

Gives:

; Explanation:
;
; The function starts by saving the registers we'll be using onto the stack. We then initialize a counter to -1 and immediately increment it to 0.
; We then loop through the string, counting the number of characters in it. Once we find the null terminator, we jump to the reverse code.
; The reverse code works by moving HL back to the last character in the string and copying it to the current position. We then decrement HL, increment the counter, and compare it to the total number of characters we counted earlier. If they're not equal, we keep copying characters until we've reversed the entire string.
; Finally, we restore the saved registers and return from the function.

str_reverse:
    push bc         ; Save the registers we'll be using
    push de
    push hl

    ld  de, -1      ; Initialize a counter to -1
    inc de

count_chars:
    ld  a, [hl]     ; Load a character from the string
    or  a           ; Check if the character is null
    jr  z, reverse  ; If it is, jump to the reverse code

    inc de          ; Increment the character counter
    inc hl          ; Move to the next character
    jr  count_chars ; Loop until we find the null terminator

reverse:
    dec hl          ; Move HL back to the last character
    ld  c, [hl]     ; Load the last character into C
    ld  b, 0        ; Initialize B to 0

copy_char:
    ld  [hl], c     ; Copy the character to the current position
    dec hl          ; Move HL back one position
    inc b           ; Increment the counter

    cp  de          ; Compare the counter to the total number of characters
    jr  nz, reverse ; If they're not equal, keep copying

    pop hl          ; Restore the saved registers
    pop de
    pop bc

    ret             ; Return from the function

zhulien

In z80 assembler code the following function.

Function name:
- str_compare

Purpose:
- Compares two 0 terminated strings.

Entry:
- Register HL points to one 0 terminated string.
- Register DE points to one 0 terminated string.

Exit:
- A contains 0 if the two strings are the same. 
- A contains 1 if the string pointed to by HL is higher alphabetically than the string pointed to by DE.
- A contains 2 if the string pointed to by DE is higher alphabetically than the string pointed to by HL.

Rules:
- Any labels within the new function, suffix the function name with a counter.

Gives:

; Explanation:
;
; Note that the function uses a loop to compare each character of the two strings until either the characters are not equal or the end of one of the strings is reached. It then compares the last characters of both strings and sets the value of A accordingly.

;--------------------------------------------------------
; Function: str_compare
;
; Purpose: Compare two 0 terminated strings
;
; Entry: HL points to one 0 terminated string.
;        DE points to one 0 terminated string.
;
; Exit:  A contains 0 if the two strings are the same.
;        A contains 1 if HL is higher alphabetically than DE.
;        A contains 2 if DE is higher alphabetically than HL.
;--------------------------------------------------------

str_compare:
    LD A,(HL)        ; Load the first character of string 1 into A
str_compare_loop:
    CP (DE)          ; Compare the character in A with the character in string 2
    JR NZ, str_compare_end ; If the characters are not equal, exit the loop
    OR A             ; Set A to 0 if end of string 1 is reached
    RET Z            ; If end of string 1 is reached, exit the function
    INC HL           ; Move HL to the next character in string 1
    INC DE           ; Move DE to the next character in string 2
    LD A,(HL)        ; Load the next character of string 1 into A
    JR str_compare_loop ; Repeat the loop

str_compare_end:
    CP (DE)          ; Compare the last characters of both strings
    RET Z            ; If both strings are equal, return 0 in A
    JR C, str_compare_HL_higher ; If string 1 is higher, return 1 in A
    LD A,2           ; If string 2 is higher, return 2 in A
    RET

str_compare_HL_higher:
    LD A,1           ; Set A to 1
    RET

zhulien

In z80 assembler code the following function.

Function name:
- bank_select

Purpose:
- Selects a memory bank given a bank number between 0 and 4.

Entry:
- Register A contains the bank to select.

Exit:
- None.

Rules:
- Any labels within the new function, suffix the function name with a counter.
- The bank numbers should lookup a port table containing 0x7fc0, 0x7fc4, 0x7fc5, 0x7fc6, 0x7fc7 and load the looked up number into BC before using out (c), c to select the bank.

Gives:

; Explanation:
;
; This implementation uses a lookup table stored in memory to determine the port number to use for each bank number. The function first loads the address of the lookup table into the hl register pair, and then uses the bank number in the a register to index into the table. The b and c registers are used as temporary registers to perform the indexing and to store the resulting port number. The function then performs an output operation to the selected port number to select the desired memory bank. Finally, the function returns to the caller.

bank_select:
    ld hl, bank_table
    ld b, a
    ld c, 0
    add hl, bc
    ld b, (hl)
    ld c, b
    ld a, c
    out (c), c
    ret

bank_table:
    db 0x7fc0, 0x7fc4, 0x7fc5, 0x7fc6, 0x7fc7

zhulien

Definitely not always correct - but given it only takes a few seconds for it to generate the code and another minute to verify it - it is faster than actually thinking.

andycadley

It's honestly amazing that a text prediction engine is able to generate code that almost looks correct. I mean sure it has bugs and such, but given that it's not really designed for doing this specifically it's pretty impressive.

Makes you wonder what it could do if it was actually designed specifically for that purpose.

zhulien

I agree on that and it definitely accepts tips or hints towards improving such as me telling it to use 16 bit ports and out (c),c, or using the txt_out_char API etc

It does however have some really problematic issues for z80 - usage of invalid instructions being a big one.  for JavaScript it generates some quite complex code 100% including a working hangman game with a single sentence

which makes me wonder, is it just a common question that they just serve up a working game that was there already.

"in javascript code a hangman game with output in html"

https://jsfiddle.net/zhulien/qu12bozp/1/?fbclid=IwAR2Wtog-S1644SSDZFX8YvJGkOeX4vgs7x_CIfHWPaDY3zBm38msRjT9bt8

zhulien

#18
damn... I actually typed a lot and then the post failed - it seems to not like long messages.

So the short version now... the below was coded 100% using ChatGPT - except that I tweaked the usage example only to make it have 2 cubes instead of 1 and put the zooming logic that it had previously generated within.

My request: "in JavaScript please code a function that takes an array of points to plot as vectors within a canvas with 4 other parameters, zoom, x rotation, y rotation, z rotation"

https://jsfiddle.net/zhulien/wbhg8csn/208/

Same where I removed the centre lines from the plot lines array: https://jsfiddle.net/zhulien/sgora6Lh/2/

The cool thing, you can take any of the supporting functions and ask it to convert them to z80 and although it isn't 100%, the logic is correct enough to manually fix - meaning that quite complex code can be coded with the assistance of ChatGPT.  For any code generation, I'd recommend asking it in JS first so you can see whether it works or not in JSFiddle before having it convert to Z80.

function degToRad(degrees) {
  return degrees * Math.PI / 180;
}

function identityMatrix() {
  return [
    [1, 0, 0, 0],
    [0, 1, 0, 0],
    [0, 0, 1, 0],
    [0, 0, 0, 1]
  ];
}

function multiplyMatrix(a, b) {
  var result = new Array(a.length);

  for (var i = 0; i < a.length; i++) {
    result[i] = new Array(b[0].length);
    for (var j = 0; j < b[0].length; j++) {
      var sum = 0;
      for (var k = 0; k < a[0].length; k++) {
        sum += a[i][k] * b[k][j];
      }
      result[i][j] = sum;
    }
  }

  return result;
}

function multiplyMatrixVector(m, v) {
  var result = [0, 0, 0, 0];
  for (var i = 0; i < 4; i++) {
    result[i] = m[i][0] * v[0] + m[i][1] * v[1] + m[i][2] * v[2] + m[i][3] * v[3];
  }
  return result;
}

function perspectiveMatrix(fov, aspectRatio, near, far) {
  var f = 1.0 / Math.tan(fov / 2);
  var rangeInv = 1 / (near - far);
  return [    [f / aspectRatio, 0, 0, 0],
          [0, f, 0, 0],
          [0, 0, (near + far) * rangeInv, near * far * rangeInv * 2],
          [0, 0, -1, 0]
         ];
}

function rotationMatrixX(theta) {
  var cos = Math.cos(theta);
  var sin = Math.sin(theta);
  return [
    [1, 0, 0, 0],
    [0, cos, -sin, 0],
    [0, sin, cos, 0],
    [0, 0, 0, 1]
  ];
}

function rotationMatrixY(theta) {
  var cos = Math.cos(theta);
  var sin = Math.sin(theta);
  return [    [cos, 0, sin, 0],
    [0, 1, 0, 0],
    [-sin, 0, cos, 0],
    [0, 0, 0, 1]
  ];
}

function rotationMatrixZ(theta) {
  var cos = Math.cos(theta);
  var sin = Math.sin(theta);
  return [    [cos, -sin, 0, 0],
    [sin, cos, 0, 0],
    [0, 0, 1, 0],
    [0, 0, 0, 1]
  ];
}

function scaleMatrix(sx, sy, sz) {
  return [    [sx, 0, 0, 0],
    [0, sy, 0, 0],
    [0, 0, sz, 0],
    [0, 0, 0, 1]
  ];
}

function translationMatrix(x, y, z) {
  return [    [1, 0, 0, x],
    [0, 1, 0, y],
    [0, 0, 1, z],
    [0, 0, 0, 1]
  ];
}

function viewportMatrix(x, y, width, height, depth) {
  return [    [width / 2, 0, 0, x + width / 2],
    [0, height / 2, 0, y + height / 2],
    [0, 0, depth / 2, depth / 2 - 0.5],
    [0, 0, 0, 1]
  ];
}

function plotLines(canvas, lines, zoom, xRot, yRot, zRot, xPercent, yPercent) {
  // Convert degrees to radians
  var xTheta = degToRad(xRot);
  var yTheta = degToRad(yRot);
  var zTheta = degToRad(zRot);

  // Create the transformation matrices
  var xRotation = rotationMatrixX(xTheta);
  var yRotation = rotationMatrixY(yTheta);
  var zRotation = rotationMatrixZ(zTheta);
  var scale = scaleMatrix(zoom / 2, zoom / 2, zoom / 2);

var translation = translationMatrix(-50, -50, -50); // translate to the origin
  var viewport = viewportMatrix(xPercent * 2, yPercent, -canvas.width / 50, -canvas.height / 50, 1); // map to the canvas

  // Combine the transformation matrices
  var transformation = multiplyMatrix(viewport, multiplyMatrix(translation, multiplyMatrix(zRotation, multiplyMatrix(yRotation, multiplyMatrix(xRotation, scale)))));

var context = canvas.getContext('2d');

for (var i = 0; i < lines.length; i++) {
    var p1 = lines[i][0];
    var p2 = lines[i][1];

    // Apply the transformation matrix to each point
    var tp1 = multiplyMatrixVector(transformation, p1);
    var tp2 = multiplyMatrixVector(transformation, p2);

    // Convert the transformed points to canvas coordinates
    var x1 = tp1[0];
    var y1 = tp1[1];
    var x2 = tp2[0];
    var y2 = tp2[1];

// Draw the line on the canvas
    context.moveTo(x1, y1);
    context.lineTo(x2, y2);
  }
  context.stroke();
}

Usage Example:

// Get the canvas element from the HTML document
var canvas = document.getElementById("myCanvas");

// Define the lines of the cube
var center = [0.5, 0.5, 0.5, 1];

var lines = [
  // Define the edges of the cube
  [[0, 0, 0, 1], [0, 0, 1, 1]],
  [[0, 0, 1, 1], [0, 1, 1, 1]],
  [[0, 1, 1, 1], [0, 1, 0, 1]],
  [[0, 1, 0, 1], [0, 0, 0, 1]],
  [[1, 0, 0, 1], [1, 0, 1, 1]],
  [[1, 0, 1, 1], [1, 1, 1, 1]],
  [[1, 1, 1, 1], [1, 1, 0, 1]],
  [[1, 1, 0, 1], [1, 0, 0, 1]],
  [[0, 0, 0, 1], [1, 0, 0, 1]],
  [[0, 0, 1, 1], [1, 0, 1, 1]],
  [[0, 1, 1, 1], [1, 1, 1, 1]],
  [[0, 1, 0, 1], [1, 1, 0, 1]],
 
  // Define lines from the center point to each corner of the cube
  [[0, 0, 0, 1], center],
  [[0, 0, 1, 1], center],
  [[0, 1, 1, 1], center],
  [[0, 1, 0, 1], center],
  [[1, 0, 0, 1], center],
  [[1, 0, 1, 1], center],
  [[1, 1, 1, 1], center],
  [[1, 1, 0, 1], center]
];

// Define the initial parameters for the cube
var zoom = 50;
var xRot = 0;
var yRot = 0;
var zRot = 0;
var x = 10;
var y = 10;
var z = 0;
var zoomDirection = 1;

var zoom2 = 50;
var xRot2 = 0;
var yRot2 = 0;
var zRot2 = 0;
var x2 = 50;
var y2 = 50;
var z2 = 0;
var zoomDirection2 = -1;



// Animate the cube by rotating it on all 3 axis and zooming in and out
var animateCube = function() {

var context = canvas.getContext('2d');
  context.clearRect(0, 0, canvas.width, canvas.height);
  context.beginPath();

// Update the rotation and zoom parameters
  xRot += 3;
  yRot += 2;
  zRot += 1;

if (zoom < 10)
  {
    zoomDirection = 1
  }
  else if (zoom > 100)
  {
    zoomDirection = -1
  }
  zoom += zoomDirection * 0.1;
 
  // Plot the lines of the cube on the canvas
  plotLines(canvas, lines, zoom, xRot, yRot, zRot, x, y, z);

  xRot2 += 1;
  yRot2 += 2;
  zRot2 += 3;

if (zoom2 < 10)
  {
    zoomDirection2 = 1
  }
  else if (zoom2 > 100)
  {
    zoomDirection2 = -1
  }
  zoom2 += zoomDirection2 * 0.1;
 
  // Plot the lines of the cube on the canvas
  plotLines(canvas, lines, zoom2, xRot2, yRot2, zRot2, x2, y2, z2);

  // Schedule the next frame of the animation
  setTimeout(animateCube, 10);
};

// Start the animation
animateCube();

zhulien

#19
First 500 lines of dialog / code generated...  If anyone wants the complete dialog, explain how i can upload it to here - because for some reason I cannot attach anything.

in JavaScript please code a function that takes an array of points to plot as vectors within a canvas with 4 other parameters, zoom, x rotation, y rotation, z rotation
Sure! Here's a function that takes an array of points as input and plots them as vectors within a canvas, using the specified zoom and rotation values:

javascript
Copy code
function plotPoints(canvas, points, zoom, xRot, yRot, zRot) {
  const context = canvas.getContext('2d');

  // Clear the canvas
  context.clearRect(0, 0, canvas.width, canvas.height);

  // Apply zoom and rotation to each point
  const transformedPoints = points.map(([x, y, z]) => {
    // Apply zoom
    x *= zoom;
    y *= zoom;
    z *= zoom;

    // Apply x rotation
    const xRotRad = (xRot / 180) * Math.PI;
    const cosX = Math.cos(xRotRad);
    const sinX = Math.sin(xRotRad);
    const y1 = y * cosX - z * sinX;
    const z1 = y * sinX + z * cosX;
    y = y1;
    z = z1;

    // Apply y rotation
    const yRotRad = (yRot / 180) * Math.PI;
    const cosY = Math.cos(yRotRad);
    const sinY = Math.sin(yRotRad);
    const x1 = x * cosY + z * sinY;
    const z2 = -x * sinY + z * cosY;
    x = x1;
    z = z2;

    // Apply z rotation
    const zRotRad = (zRot / 180) * Math.PI;
    const cosZ = Math.cos(zRotRad);
    const sinZ = Math.sin(zRotRad);
    const x2 = x * cosZ - y * sinZ;
    const y2 = x * sinZ + y * cosZ;
    x = x2;
    y = y2;

    // Return the transformed point
    return [x, y, z];
  });

  // Draw lines between each point
  context.beginPath();
  transformedPoints.forEach(([x, y, z], i) => {
    if (i === 0) {
      context.moveTo(x + canvas.width / 2, y + canvas.height / 2);
    } else {
      context.lineTo(x + canvas.width / 2, y + canvas.height / 2);
    }
  });
  context.stroke();
}
To use this function, you'll need to pass in a canvas element, an array of points, and the zoom and rotation values. For example:

javascript
Copy code
const canvas = document.getElementById('myCanvas');
const points = [
  [0, 0, 0],
  [100, 0, 0],
  [100, 100, 0],
  [0, 100, 0],
  [0, 0, 100],
  [100, 0, 100],
  [100, 100, 100],
  [0, 100, 100],
];
const zoom = 1;
const xRot = 45;
const yRot = 45;
const zRot = 0;

plotPoints(canvas, points, zoom, xRot, yRot, zRot);
This will plot the specified points within the canvas, with a zoom of 1 and rotation values of 45 degrees around the x and y axes. The zRot value is 0, so there's no rotation around the z axis.




julian@alientechnology.com.au
using the plotPoints function you just created, why can't I see the cube?
If you're not seeing the cube when using the plotPoints function, there could be a few different reasons why:

The canvas is not big enough: If the canvas is too small, the points may be getting drawn outside the visible area. You can try increasing the size of the canvas to see if that helps.

The points are not in the correct order: In order to draw a cube using lines, the points need to be ordered in a specific way. It's possible that the points in your array are not in the correct order, which could be causing the lines to be drawn incorrectly. Make sure that the points are ordered correctly.

The zoom value is too high or too low: If the zoom value is too high, the points may be getting drawn too close together or overlapping each other. If the zoom value is too low, the points may be getting drawn too far apart. Try adjusting the zoom value to see if that helps.

The rotation values are incorrect: If the rotation values are not set correctly, the cube may be rotated in a way that makes it difficult to see. Make sure that the rotation values are set correctly and try adjusting them to see if that helps.

Here's an example of how to plot a cube using the plotPoints function:

javascript
Copy code
const canvas = document.getElementById('myCanvas');
const points = [
  [-50, -50, -50],
  [50, -50, -50],
  [50, 50, -50],
  [-50, 50, -50],
  [-50, -50, 50],
  [50, -50, 50],
  [50, 50, 50],
  [-50, 50, 50],
];
const zoom = 2;
const xRot = 45;
const yRot = 45;
const zRot = 0;

plotPoints(canvas, points, zoom, xRot, yRot, zRot);
This should draw a cube within the canvas. If you're still having trouble seeing the cube, try adjusting the canvas size, zoom value, or rotation values until you can see it clearly.




julian@alientechnology.com.au
i can see the shape but it is not a rotated cube, it appears to be a mess
If the shape you're seeing is not a properly rotated cube, there could be a few reasons why:

The order of the points is incorrect: The order in which the points are listed matters for constructing a cube. Make sure that the points in your array are in the correct order to form a cube.

The rotation values are not set correctly: It's possible that the rotation values you're using are not resulting in the desired rotation. Try adjusting the rotation values and see if the shape appears more like a rotated cube.

The zoom value is causing the points to overlap: If the zoom value is too high, the points may be overlapping, which can make it difficult to see the cube properly. Try reducing the zoom value to see if that helps.

The canvas is not oriented correctly: If the canvas is not oriented correctly, the shape may appear distorted. Make sure that the canvas is oriented properly, with the x and y axes in the correct positions.

Here's an example of how to plot a rotated cube using the plotPoints function:

javascript
Copy code
const canvas = document.getElementById('myCanvas');
const points = [
  [-50, -50, -50],
  [50, -50, -50],
  [50, 50, -50],
  [-50, 50, -50],
  [-50, -50, 50],
  [50, -50, 50],
  [50, 50, 50],
  [-50, 50, 50],
];
const zoom = 2;
const xRot = 45;
const yRot = 45;
const zRot = 45;

plotPoints(canvas, points, zoom, xRot, yRot, zRot);
This code should draw a rotated cube within the canvas. If the shape you're seeing is still not a properly rotated cube, try adjusting the points, rotation values, and zoom value until the shape appears correctly.




julian@alientechnology.com.au
please add 2 more parameters to the plotpoints function, that is x and y which are the percentage horizontally and vertically within the canvas the object is to be plotted
Sure! Here's an updated version of the plotPoints function that includes two additional parameters for the x and y position within the canvas:

javascript
Copy code
function plotPoints(canvas, points, zoom, xRot, yRot, zRot, xPercent, yPercent) {
  const context = canvas.getContext('2d');

  // Clear the canvas
  context.clearRect(0, 0, canvas.width, canvas.height);

  // Apply zoom and rotation to each point
  const transformedPoints = points.map(([x, y, z]) => {
    // Apply zoom
    x *= zoom;
    y *= zoom;
    z *= zoom;

    // Apply x rotation
    const xRotRad = (xRot / 180) * Math.PI;
    const cosX = Math.cos(xRotRad);
    const sinX = Math.sin(xRotRad);
    const y1 = y * cosX - z * sinX;
    const z1 = y * sinX + z * cosX;
    y = y1;
    z = z1;

    // Apply y rotation
    const yRotRad = (yRot / 180) * Math.PI;
    const cosY = Math.cos(yRotRad);
    const sinY = Math.sin(yRotRad);
    const x1 = x * cosY + z * sinY;
    const z2 = -x * sinY + z * cosY;
    x = x1;
    z = z2;

    // Apply z rotation
    const zRotRad = (zRot / 180) * Math.PI;
    const cosZ = Math.cos(zRotRad);
    const sinZ = Math.sin(zRotRad);
    const x2 = x * cosZ - y * sinZ;
    const y2 = x * sinZ + y * cosZ;
    x = x2;
    y = y2;

    // Return the transformed point
    return [x, y, z];
  });

  // Calculate the x and y position within the canvas
  const xOffset = canvas.width * (xPercent / 100);
  const yOffset = canvas.height * (yPercent / 100);

  // Draw lines between each point
  context.beginPath();
  transformedPoints.forEach(([x, y, z], i) => {
    if (i === 0) {
      context.moveTo(x + xOffset, y + yOffset);
    } else {
      context.lineTo(x + xOffset, y + yOffset);
    }
  });
  context.stroke();
}
The xPercent and yPercent parameters are percentages representing the horizontal and vertical position within the canvas where the object should be plotted. They are used to calculate the x and y offsets within the canvas, which are added to the x and y coordinates of each point before it is drawn.

Here's an example of how to use the updated plotPoints function:

javascript
Copy code
const canvas = document.getElementById('myCanvas');
const points = [
  [-50, -50, -50],
  [50, -50, -50],
  [50, 50, -50],
  [-50, 50, -50],
  [-50, -50, 50],
  [50, -50, 50],
  [50, 50, 50],
  [-50, 50, 50],
];
const zoom = 2;
const xRot = 45;
const yRot = 45;
const zRot = 45;
const xPercent = 50; // Center the object horizontally
const yPercent = 50; // Center the object vertically

plotPoints(canvas, points, zoom, xRot, yRot,



julian@alientechnology.com.au
please note the points should also be in percentage of the canvas
Sure! Here's an updated version of the plotPoints function that includes two additional parameters for the x and y position within the canvas, as well as updates to the input points to be in percentage of the canvas:

javascript
Copy code
function plotPoints(canvas, points, zoom, xRot, yRot, zRot, xPercent, yPercent) {
  const context = canvas.getContext('2d');

  // Clear the canvas
  context.clearRect(0, 0, canvas.width, canvas.height);

  // Apply zoom and rotation to each point
  const transformedPoints = points.map(([xPercent, yPercent, zPercent]) => {
    // Convert percentage values to pixel values
    const x = canvas.width * (xPercent / 100);
    const y = canvas.height * (yPercent / 100);
    const z = canvas.width * (zPercent / 100);

    // Apply zoom
    const zoomedX = x * zoom;
    const zoomedY = y * zoom;
    const zoomedZ = z * zoom;

    // Apply x rotation
    const xRotRad = (xRot / 180) * Math.PI;
    const cosX = Math.cos(xRotRad);
    const sinX = Math.sin(xRotRad);
    const y1 = zoomedY * cosX - zoomedZ * sinX;
    const z1 = zoomedY * sinX + zoomedZ * cosX;
    const x1 = zoomedX;
    const transformedX = x1;
    const transformedY = y1;
    const transformedZ = z1;

    // Apply y rotation
    const yRotRad = (yRot / 180) * Math.PI;
    const cosY = Math.cos(yRotRad);
    const sinY = Math.sin(yRotRad);
    const x2 = transformedX * cosY + transformedZ * sinY;
    const z2 = -transformedX * sinY + transformedZ * cosY;
    const transformedX2 = x2;
    const transformedY2 = transformedY;
    const transformedZ2 = z2;

    // Apply z rotation
    const zRotRad = (zRot / 180) * Math.PI;
    const cosZ = Math.cos(zRotRad);
    const sinZ = Math.sin(zRotRad);
    const x3 = transformedX2 * cosZ - transformedY2 * sinZ;
    const y3 = transformedX2 * sinZ + transformedY2 * cosZ;
    const transformedX3 = x3;
    const transformedY3 = y3;
    const transformedZ3 = transformedZ2;

    // Return the transformed point
    return [transformedX3, transformedY3, transformedZ3];
  });

  // Calculate the x and y position within the canvas
  const xOffset = canvas.width * (xPercent / 100);
  const yOffset = canvas.height * (yPercent / 100);

  // Draw lines between each point
  context.beginPath();
  transformedPoints.forEach(([x, y, z], i) => {
    if (i === 0) {
      context.moveTo(x + xOffset, y + yOffset);
    } else {
      context.lineTo(x + xOffset, y + yOffset);
    }
  });
  context.stroke();
}
In this updated version of the function, the input points are in percentages of the canvas width and height, rather than in pixel values. The function converts these percentage values to pixel values before applying the zoom and rotation transformations.

Here's an example of how to use the updated plotPoints function:

javascript
Copy code
const canvas = document.getElementById('myCanvas');
const points =



julian@alientechnology.com.au
the usage example did not complete
I apologize for the incomplete usage example. Here's the complete example:

javascript
Copy code
const canvas = document.getElementById('myCanvas');
const points = [
  [25, 25, 25],
  [75, 25, 25],
  [75, 75, 25],
  [25, 75, 25],
  [25, 25, 75],
  [75, 25, 75],
  [75, 75, 75],
  [25, 75, 75],
];
const zoom = 2;
const xRot = 45;
const yRot = 45;
const zRot = 45;
const xPercent = 50; // Center the object horizontally
const yPercent = 50; // Center the object vertically

plotPoints(canvas, points, zoom, xRot, yRot, zRot, xPercent, yPercent);
In this example, the points array contains 8 points, each specified as a percentage of the canvas width and height. The zoom, xRot, yRot, and zRot values are used to apply transformations to the points. The xPercent and yPercent values are used to position the object within the canvas.

This code should draw a cube within the canvas that is positioned in the center and has been transformed by the specified rotation and zoom values.




julian@alientechnology.com.au
x and y should be calculated as:

  // Calculate the x and y position within the canvas
  const xOffset = canvas.width / 100 * (xPercent / 100 * 0.5);
  const yOffset = canvas.height / 100 * (yPercent / 100 * 0.5);
Sure, here's an updated version of the plotPoints function with the adjusted calculation for xOffset and yOffset:

javascript
Copy code
function plotPoints(canvas, points, zoom, xRot, yRot, zRot, xPercent, yPercent) {
  const context = canvas.getContext('2d');

  // Clear the canvas
  context.clearRect(0, 0, canvas.width, canvas.height);

  // Apply zoom and rotation to each point
  const transformedPoints = points.map(([xPercent, yPercent, zPercent]) => {
    // Convert percentage values to pixel values
    const x = canvas.width * (xPercent / 100);
    const y = canvas.height * (yPercent / 100);
    const z = canvas.width * (zPercent / 100);

    // Apply zoom
    const zoomedX = x * zoom;
    const zoomedY = y * zoom;
    const zoomedZ = z * zoom;

    // Apply x rotation
    const xRotRad = (xRot / 180) * Math.PI;
    const cosX = Math.cos(xRotRad);
    const sinX = Math.sin(xRotRad);
    const y1 = zoomedY * cosX - zoomedZ * sinX;
    const z1 = zoomedY * sinX + zoomedZ * cosX;
    const x1 = zoomedX;
    const transformedX = x1;
    const transformedY = y1;
    const transformedZ = z1;

    // Apply y rotation
    const yRotRad = (yRot / 180) * Math.PI;
    const cosY = Math.cos(yRotRad);
    const sinY = Math.sin(yRotRad);
    const x2 = transformedX * cosY + transformedZ * sinY;
    const z2 = -transformedX * sinY + transformedZ * cosY;
    const transformedX2 = x2;
    const transformedY2 = transformedY;
    const transformedZ2 = z2;

    // Apply z rotation
    const zRotRad = (zRot / 180) * Math.PI;
    const cosZ = Math.cos(zRotRad);
    const sinZ = Math.sin(zRotRad);
    const x3 = transformedX2 * cosZ - transformedY2 * sinZ;
    const y3 = transformedX2 * sinZ + transformedY2 * cosZ;
    const transformedX3 = x3;
    const transformedY3 = y3;
    const transformedZ3 = transformedZ2;

    // Return the transformed point
    return [transformedX3, transformedY3, transformedZ3];
  });

  // Calculate the x and y position within the canvas
  const xOffset = canvas.width / 100 * (xPercent / 100 * 0.5);
  const yOffset = canvas.height / 100 * (yPercent / 100 * 0.5);

  // Draw lines between each point
  context.beginPath();
  transformedPoints.forEach(([x, y, z], i) => {
    if (i === 0) {
      context.moveTo(x + xOffset, y + yOffset);
    } else {
      context.lineTo(x + xOffset, y + yOffset);
    }
  });
  context.stroke();
}
With this updated function, the xOffset and yOffset values are calculated using the adjusted formula, which takes into account that the xPercent and yPercent values represent a percentage of half the canvas width and height.

Here's an example of how to use the updated plotPoints function with the adjusted xOffset and `y




julian@alientechnology.com.au
please rewrite in ES5
Sure, here's the plotPoints function rewritten in ES5:

javascript
Copy code
function plotPoints(canvas, points, zoom, xRot, yRot, zRot, xPercent, yPercent) {
  var context = canvas.getContext('2d');

  // Clear the canvas
  context.clearRect(0, 0, canvas.width, canvas.height);

  // Apply zoom and rotation to each point
  var transformedPoints = points.map(function(point) {
    // Convert percentage values to pixel values
    var x = canvas.width * (point
 / 100);
    var y = canvas.height * (point[1] / 100);
    var z = canvas.width * (point[2] / 100);
    // Apply zoom
    var zoomedX = x * zoom;
    var zoomedY = y * zoom;
    var zoomedZ = z * zoom;
    // Apply x rotation
    var xRotRad = (xRot / 180) * Math.PI;
    var cosX = Math.cos(xRotRad);
    var sinX = Math.sin(xRotRad);
    var y1 = zoomedY * cosX - zoomedZ * sinX;
    var z1 = zoomedY * sinX + zoomedZ * cosX;
    var x1 = zoomedX;
    var transformedX = x1;
    var transformedY = y1;
    var transformedZ = z1;
    // Apply y rotation
    var yRotRad = (yRot / 180) * Math.PI;
    var cosY = Math.cos(yRotRad);
    var sinY = Math.sin(yRotRad);
    var x2 = transformedX * cosY + transformedZ * sinY;
    var z2 = -transformedX * sinY + transformedZ * cosY;
    var transformedX2 = x2;
    var transformedY2 = transformedY;
    var transformedZ2 = z2;
    // Apply z rotation
    var zRotRad = (zRot / 180) * Math.PI;
    var cosZ = Math.cos(zRotRad);
    var sinZ = Math.sin(zRotRad);
    var x3 = transformedX2 * cosZ - transformedY2 * sinZ;
    var y3 = transformedX2 * sinZ + transformedY2 * cosZ;
    var transformedX3 = x3;
    var transformedY3 = y3;
    var transformedZ3 = transformedZ2;
    // Return the transformed point
    return [transformedX3, transformedY3, transformedZ3];
  });
  // Calculate the x and y position within the canvas
  var xOffset = canvas.width / 100 * (xPercent / 100 * 0.5);
  var yOffset = canvas.height / 100 * (yPercent / 100 * 0.5);
  // Draw lines between each point
  context.beginPath();
  transformedPoints.forEach(function(point, i) {
    if (i === 0) {
      context.moveTo(point + xOffset, point[1] + yOffset);
    } else {
      context.lineTo(point + xOffset, point[1] + yOffset);
    }
  });
  context.stroke();
}
This version of the function uses var instead of const to declare variables, and it uses function instead of arrow functions for the map and forEach callbacks.
julian@alientechnology.com.au
please also provide the usabe example in ES5
Certainly! Here's the usage example for the plotPoints function in ES5:
javascript
Copy code
var canvas = document.getElementById('myCanvas');
var points = [
  [25, 25, 25],
  [75, 25, 25],
  [75, 75, 25],
  [25, 75, 25],
  [25, 25, 75],
  [75, 25, 75],
  [75, 75, 75],
  [25, 75, 75],
];
var zoom = 2;
var xRot = 45;
var yRot = 45;
var zRot = 45;
var xPercent = 50; // Center the object horizontally
var yPercent = 50; // Center the object vertically
plotPoints(canvas, points, zoom, xRot, yRot, zRot, xPercent, yPercent);
In this example, the var keyword is used to declare variables instead of let or const. The points array contains 8 points, each specified as a percentage of the canvas width and height. The zoom, xRot, yRot, and zRot values are used to apply transformations to the points. The xPercent and yPercent values are used to position the object within the canvas.
This code should draw a cube within the canvas that is positioned in the center and has been transformed by the specified rotation and zoom values.

zhulien

#20
What is cool for most part, is you can ask it as a human to tweak the result and it then does it - although I did find sometimes I have to rephrase things to stop it going around in circles.

One of the first cuts where I could see the cube was this, but then I told it to draw it in perspective from a centre point within the cube and it fixed the distortion.

https://jsfiddle.net/zhulien/zp62wth3/26/

SerErris

I have provided ChatGPT with the TXT_VDU jump entries and the description. 

Thank you for providing that section from the firmware compendium describing the TXT VDU firmware functions and their entry points. Based on the information you've provided, here's an example that uses the human-readable labels for some of these functions in an Amstrad CPC assembly code:

Thank you for providing the detailed information from the firmware compendium about the `TXT_OUTPUT` function on the Amstrad CPC. Based on that information, the assembly code snippet that prints a string using the `TXT_OUTPUT` function should be accurate.

To reiterate, here is an example code snippet that utilizes the `TXT_OUTPUT` function to print a string:


assembly
        org &8000    ; Starting address

start  ld hl, message      ; Load address of the message

print_loop:
        ld a, (hl)          ; Load the next character to print
        or a                ; Check if the character is zero (end of string)
        jr z, done          ; If zero, the message has been printed
        call TXT_OUTPUT      ; Call firmware function to output character
        inc hl              ; Move to the next character in the string
        jr print_loop        ; Repeat until all characters have been printed

done    ret                  ; Return from the program

message defb "Hello, Amstrad!", 0

; Firmware labels based on the provided compendium
TXT_OUTPUT  equ &BB5A        ; Output a character or control code to the Text VDU


This assembly code snippet utilizes the `TXT_OUTPUT` function, obeying control codes and printing characters to the screen. It reads each character from the string and calls the `TXT_OUTPUT` function for each character until it encounters a null character (0) marking the end of the string.

Always cross-reference with the firmware compendium or official documentation to ensure accurate usage of firmware functions and their respective entry conditions.
Proud owner of 2 Schneider CPC 464, 1 Schneider CPC 6128, GT65 and lots of books
Still learning all the details on how things work.

Powered by SMFPacks Menu Editor Mod